mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-19 08:30:22 -07:00
Compare commits
572 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
eb3b38d40b | ||
|
d9c8a37811 | ||
|
158c0f2911 | ||
|
44cb8f769e | ||
|
705b97c4ac | ||
|
c19f383685 | ||
|
bb5cd5b2dd | ||
|
bb9aa79b21 | ||
|
dfa4836216 | ||
|
18377d221d | ||
|
2aa21625bd | ||
|
2946009006 | ||
|
b0cc49218d | ||
|
a5c9b3e490 | ||
|
dfb841c303 | ||
|
5ceb0ec15d | ||
|
f707d86912 | ||
|
75f2cb5f65 | ||
|
a51e639d81 | ||
|
59b2340680 | ||
|
da3583fd5e | ||
|
04124988e8 | ||
|
390a357859 | ||
|
9a87498bb1 | ||
|
f58bb72d3a | ||
|
60cd5b7a48 | ||
|
25cf06f6cf | ||
|
e44aae0c20 | ||
|
fcb6f936ea | ||
|
9958d29764 | ||
|
239cdd67fd | ||
|
f01e3043b8 | ||
|
04c98abd1f | ||
|
53bfb92d65 | ||
|
2b3cac018e | ||
|
f909b0f114 | ||
|
fa1e343b04 | ||
|
22b12e3013 | ||
|
7a971735af | ||
|
6abb5b0c7e | ||
|
0dfcba9825 | ||
|
e5df8cdc62 | ||
|
f8bbe5124c | ||
|
948277895e | ||
|
708a7c24ef | ||
|
1ce614dfc0 | ||
|
930eeac900 | ||
|
ec93f8a1cd | ||
|
997fefbc11 | ||
|
c7eb141098 | ||
|
1f50cdfa8b | ||
|
1c530cbc66 | ||
|
9cd5b25745 | ||
|
78ff20ddf0 | ||
|
2626f89ea6 | ||
|
adbae0f74d | ||
|
46ac115bd1 | ||
|
3465efcdc1 | ||
|
cdf5736f1a | ||
|
2d6ca96e07 | ||
|
d9cad5e1b6 | ||
|
0c736217a7 | ||
|
77ecf09506 | ||
|
f1ac1847ff | ||
|
ce821294e2 | ||
|
6f174a9e08 | ||
|
5b3e489108 | ||
|
2670b8f772 | ||
|
858c0e26d1 | ||
|
b5ef049ea1 | ||
|
615e0dae46 | ||
|
ce4766772d | ||
|
8d6618104e | ||
|
50e1bec85f | ||
|
b8a204c21d | ||
|
2ee5118d7a | ||
|
5d005f11fa | ||
|
54c89104de | ||
|
b2ad21a65c | ||
|
72cb5d24b6 | ||
|
9868b18378 | ||
|
208f4c48db | ||
|
4506871310 | ||
|
6483f4ec22 | ||
|
ae1fe860ff | ||
|
49974d5e34 | ||
|
94bc132084 | ||
|
e9c3fcbb64 | ||
|
40147d3a3f | ||
|
23ecce0e7a | ||
|
465e3d979d | ||
|
b10a43dabc | ||
|
5bd7ff884d | ||
|
2118440488 | ||
|
c505eb55ff | ||
|
f5c5cfa960 | ||
|
0302bfdc22 | ||
|
4f868a1f3c | ||
|
94c55fe909 | ||
|
742bce016c | ||
|
4cf62c114e | ||
|
41f5f67f6c | ||
|
02d7badd15 | ||
|
0e80ecc534 | ||
|
be6268a7ec | ||
|
a9549dbca0 | ||
|
b06fbdb743 | ||
|
241a4935a2 | ||
|
3577a6be31 | ||
|
2e540e4ec4 | ||
|
a4f7d7c594 | ||
|
55e953b383 | ||
|
d29723cb76 | ||
|
400dd16072 | ||
|
a3b96961a2 | ||
|
a3d32f3b70 | ||
|
4d14bcb02f | ||
|
9b4060f09b | ||
|
867bc86089 | ||
|
f48ee7a3d1 | ||
|
51afc2c291 | ||
|
02f7da2bf2 | ||
|
7631d4c73f | ||
|
ddae3036ca | ||
|
225e13c3cc | ||
|
3fa6320a39 | ||
|
1ae7e2164c | ||
|
877fb5b93a | ||
|
0e521788bc | ||
|
ffd6cf65e4 | ||
|
8b7b169043 | ||
|
533bc5115e | ||
|
99ab3e19d9 | ||
|
f4e19d3f1e | ||
|
06469b3391 | ||
|
303a10d27c | ||
|
4d85e7996d | ||
|
2da4f427ea | ||
|
a17cea8b8c | ||
|
6538970087 | ||
|
0399e64274 | ||
|
382f0f23f1 | ||
|
b83c9f5c6f | ||
|
d775686380 | ||
|
0dc531c4a7 | ||
|
0a7e2cb152 | ||
|
4f991610d0 | ||
|
ea852965ff | ||
|
260d8e1f71 | ||
|
642f394eb3 | ||
|
b15c2bfff6 | ||
|
da86db43d4 | ||
|
70ae99f521 | ||
|
a8eda7f978 | ||
|
8a8f394da7 | ||
|
51838fb5f5 | ||
|
85f874d10f | ||
|
9b3925009a | ||
|
3c128679ee | ||
|
e96b8ce4cc | ||
|
433b7881a3 | ||
|
ed05f14300 | ||
|
c62fb08da6 | ||
|
8ba20fcae1 | ||
|
ff97d18c4c | ||
|
5e8bb71785 | ||
|
b496e2c718 | ||
|
a41b8d5e97 | ||
|
8654029f86 | ||
|
a4e6c5d678 | ||
|
3a47c73f34 | ||
|
1f0fd79b91 | ||
|
d1a59ec39e | ||
|
4c987b20e2 | ||
|
2309270752 | ||
|
79b526a041 | ||
|
075bbecabd | ||
|
8aaffda969 | ||
|
10a335631e | ||
|
da2d7c3971 | ||
|
05eb0aa43d | ||
|
fc7223edc0 | ||
|
86c279d7d0 | ||
|
46b00a4a86 | ||
|
4a79eea6dc | ||
|
7374a023ef | ||
|
a46576afc3 | ||
|
10035a85cc | ||
|
c93140a5f1 | ||
|
5380cbcdda | ||
|
9ea76428b6 | ||
|
0cd04bd666 | ||
|
1c2b9a9ce3 | ||
|
cec084c178 | ||
|
c2ef8fcc00 | ||
|
3fc3521a97 | ||
|
9a67354fa2 | ||
|
f7ba86d1f3 | ||
|
f3db1b172c | ||
|
2a6d070774 | ||
|
aec69131cd | ||
|
4b968e5bc1 | ||
|
a852461c7d | ||
|
e4abf26069 | ||
|
006bd9eef5 | ||
|
5ee35f914f | ||
|
aa1bd647b1 | ||
|
fdb7ca6c8f | ||
|
6ab5a0befb | ||
|
6384f4acf4 | ||
|
4600043a49 | ||
|
279b06044c | ||
|
ccbdba7ee2 | ||
|
c7f0519faf | ||
|
7ea4fbf0ba | ||
|
f6ca4bac51 | ||
|
155eba57d8 | ||
|
7b10530a0d | ||
|
a25a214523 | ||
|
c8d80a2920 | ||
|
03385fc07f | ||
|
cca0f48b74 | ||
|
60edb376f2 | ||
|
6f74d8d7e9 | ||
|
ec4bea7901 | ||
|
9171db1984 | ||
|
5f60fc7d00 | ||
|
c4f46473df | ||
|
011d7ccb91 | ||
|
efc51eb7d1 | ||
|
c2835b6b0f | ||
|
d5d7f69d1e | ||
|
5cef2f44fe | ||
|
22154fa272 | ||
|
2ddd16ef28 | ||
|
d7382aa8a1 | ||
|
90306bdae6 | ||
|
b1ab0f7539 | ||
|
bf5e4bf116 | ||
|
4c471218c9 | ||
|
e59680481d | ||
|
f4315db50f | ||
|
3cc8e8c6be | ||
|
b37944605f | ||
|
f4995c1837 | ||
|
6ffde36466 | ||
|
c754d7963f | ||
|
8c97cb7858 | ||
|
e86d3a14e4 | ||
|
4b25fbe5fd | ||
|
81e93acba4 | ||
|
b21edb1a97 | ||
|
e4af4b5e2e | ||
|
d30cc19d25 | ||
|
f15b49e0fd | ||
|
c544c5115c | ||
|
b80b64cd6c | ||
|
4082e876d5 | ||
|
8ce1665fdb | ||
|
7753e8ea68 | ||
|
cb4230e1c2 | ||
|
7055d0c138 | ||
|
4435f5c546 | ||
|
7a84317f33 | ||
|
2433760786 | ||
|
b51ab182ae | ||
|
d7e7a29261 | ||
|
f1ef724a87 | ||
|
474bfcbccd | ||
|
905ca39bc9 | ||
|
3a21dd84b3 | ||
|
e6be4af21f | ||
|
2c78867a98 | ||
|
2acbb59bf2 | ||
|
11943f761e | ||
|
f148b96bea | ||
|
445337d03d | ||
|
34f2a4713e | ||
|
d5920bc5da | ||
|
aee9b8ac19 | ||
|
32a3d84d74 | ||
|
002cd91fbf | ||
|
01f4074421 | ||
|
6787fe8933 | ||
|
f0850905f0 | ||
|
d1ea18b492 | ||
|
73ae9790f9 | ||
|
3458d7ac93 | ||
|
e59464629f | ||
|
2e81648980 | ||
|
8f50460bfe | ||
|
f4b148df1e | ||
|
0e24f9c0d5 | ||
|
2cfa5d2408 | ||
|
fa246cb6ed | ||
|
6d25ef09cd | ||
|
0137a5f6cd | ||
|
3c1a2e9fca | ||
|
fb8eaba83f | ||
|
3352317ca8 | ||
|
e59623d1d5 | ||
|
d01f9943e1 | ||
|
59c615c321 | ||
|
e2a9271150 | ||
|
897ee276dc | ||
|
94a30889a7 | ||
|
9228116c9a | ||
|
410da2e46f | ||
|
7a6fde8414 | ||
|
3eb6cb1875 | ||
|
2f967037aa | ||
|
1309b59f2c | ||
|
fb36815b01 | ||
|
df3fba1572 | ||
|
40adb3dfb4 | ||
|
5d2b008294 | ||
|
208f94fe12 | ||
|
1789405163 | ||
|
68bb3e7f0a | ||
|
f83fe9986b | ||
|
f2d43e5f21 | ||
|
e1179b665b | ||
|
f261fb6fe0 | ||
|
3a43e7bb9a | ||
|
1f97643e83 | ||
|
56f6f61596 | ||
|
8e10ddb592 | ||
|
feb8ad48f0 | ||
|
f7fcbe32c9 | ||
|
a724332eb8 | ||
|
54441e0c4e | ||
|
acbcf0cf11 | ||
|
30b49c75bf | ||
|
ff9e059de6 | ||
|
f1e32cd122 | ||
|
868b2b544a | ||
|
8a6778f0a0 | ||
|
64591c85aa | ||
|
873bff390e | ||
|
84c9baecc6 | ||
|
3b99e906df | ||
|
5e7292434a | ||
|
70cfc7cc9c | ||
|
1da0b2c02e | ||
|
708d166360 | ||
|
44004abc01 | ||
|
31431a9271 | ||
|
70d94fec13 | ||
|
373108102c | ||
|
97a24ec6f3 | ||
|
64fefa3749 | ||
|
e380b6ed66 | ||
|
88adae73ba | ||
|
c6f672257b | ||
|
5b43c106bd | ||
|
d11d069715 | ||
|
ddf180fa30 | ||
|
a4b7d1c2d7 | ||
|
12b5034c99 | ||
|
9c38287410 | ||
|
ac5668192e | ||
|
7d51dee103 | ||
|
ea16b64ec1 | ||
|
e6a9cfab91 | ||
|
ef03f69116 | ||
|
32c0fa2f2f | ||
|
7d1c78f4a3 | ||
|
d462cc7fa1 | ||
|
09ec1cca51 | ||
|
6fc9c8e479 | ||
|
6131919715 | ||
|
aaa5573c73 | ||
|
344e32d71b | ||
|
d41135d07c | ||
|
b884f1f7c8 | ||
|
3d1dd6b5c7 | ||
|
1d3904c3e7 | ||
|
529ad4eaf4 | ||
|
d3042e5358 | ||
|
d2773d7a4e | ||
|
5fd90548dc | ||
|
25d5ce4833 | ||
|
e7a72de9b5 | ||
|
cb7ed4f62b | ||
|
2a478c30ca | ||
|
04ac46c543 | ||
|
2f55806d6f | ||
|
3b207d29bd | ||
|
6bd6c5512e | ||
|
bb5b09def0 | ||
|
efe29a2461 | ||
|
e951011503 | ||
|
16aeb24bc1 | ||
|
74d0f34cf3 | ||
|
4abf9155ee | ||
|
0a28e13787 | ||
|
3cd6e3960f | ||
|
8b1d6e3009 | ||
|
445acec2a2 | ||
|
45c3787e75 | ||
|
9199a9746d | ||
|
bce58d9d65 | ||
|
f3fc8d599a | ||
|
107723bdf4 | ||
|
065e89648b | ||
|
354d4594de | ||
|
089fdd1ea0 | ||
|
1815f9a2e5 | ||
|
d8f79d7678 | ||
|
80b2fd135b | ||
|
fda9790cde | ||
|
9b3f71390c | ||
|
0e5d03a557 | ||
|
57a39984dd | ||
|
944e36ea2e | ||
|
4a1b960cbe | ||
|
5d8261aee2 | ||
|
d075d1cab9 | ||
|
bb099e5733 | ||
|
dadb2e0949 | ||
|
a8c2d5a616 | ||
|
465cf66df1 | ||
|
1a0a22ad03 | ||
|
0a1ae48a9f | ||
|
ae403e6a05 | ||
|
ae4e38d9d5 | ||
|
ecae3c5e4b | ||
|
f1bd62806e | ||
|
fda5626594 | ||
|
c90dbfab6f | ||
|
d335c8f101 | ||
|
c8a0443adc | ||
|
ce48bc5408 | ||
|
da6e966313 | ||
|
a661203bb6 | ||
|
9e8d9791c7 | ||
|
2d82a92324 | ||
|
407453166c | ||
|
8dd2cd41fb | ||
|
0a0e56d99c | ||
|
086fd7ece8 | ||
|
4da9b7cc5b | ||
|
f56153a9c1 | ||
|
a36fa5c229 | ||
|
fdfcfc824e | ||
|
d01756c1f4 | ||
|
078e13f463 | ||
|
47d645d84a | ||
|
b65f8a8723 | ||
|
401a3bae61 | ||
|
76a899627e | ||
|
b7a3c45269 | ||
|
2bad73354a | ||
|
0dc7367a70 | ||
|
52b9ae592b | ||
|
25add26881 | ||
|
f16f170433 | ||
|
a6b263713a | ||
|
4f0f512cab | ||
|
a3a7499317 | ||
|
b117fae3b4 | ||
|
2671656a75 | ||
|
2778aff08f | ||
|
9e4f90aedf | ||
|
15dc024a39 | ||
|
3b85690aa6 | ||
|
cef09fbfe6 | ||
|
a8b568c6c4 | ||
|
b5fb6110ab | ||
|
da9252a23e | ||
|
8475a8ef99 | ||
|
f9c37ca43b | ||
|
9dc9366fc6 | ||
|
85aba23cbe | ||
|
2d1ebadb9b | ||
|
e66eab7b6a | ||
|
c9822b08f9 | ||
|
983bc067da | ||
|
b320bc2dc6 | ||
|
ad64726f5d | ||
|
5fa2594659 | ||
|
75727e7c17 | ||
|
67e1e46f9b | ||
|
308b1f3afb | ||
|
c143907857 | ||
|
830350a1f7 | ||
|
95542e4488 | ||
|
5642ed331d | ||
|
c7086f936a | ||
|
b9f110ef87 | ||
|
1bf4937b02 | ||
|
6a90b50545 | ||
|
602d6b7356 | ||
|
780e3dd542 | ||
|
f1a7a7497e | ||
|
f390f48a07 | ||
|
1b06d222cf | ||
|
391ff29110 | ||
|
a5c14370c1 | ||
|
b0bae15499 | ||
|
e8317ae34d | ||
|
a25d228840 | ||
|
60f069d540 | ||
|
2e6e0e9278 | ||
|
f69e72eca1 | ||
|
b0cd9972e8 | ||
|
f3c49c1295 | ||
|
4971725b69 | ||
|
1697171fc0 | ||
|
81721b8aa8 | ||
|
365caa49ff | ||
|
94381e5999 | ||
|
42fd366046 | ||
|
cbd2451570 | ||
|
dde3e082c9 | ||
|
9f3c9ac01a | ||
|
7f177fa1cf | ||
|
d64ac47202 | ||
|
4e93b2def5 | ||
|
aff34089c4 | ||
|
1989b0049f | ||
|
9f933da1c5 | ||
|
af301312d5 | ||
|
8c14c2a5f4 | ||
|
cb211d83f6 | ||
|
fde569db65 | ||
|
5b37d53992 | ||
|
deb077c346 | ||
|
3f40d6d936 | ||
|
a364e80425 | ||
|
2b01a5bcf6 | ||
|
8d4c18d723 | ||
|
c600e1aaff | ||
|
cca0adf6a3 | ||
|
534adad6b1 | ||
|
775111b603 | ||
|
85632e7c33 | ||
|
43ca66779b | ||
|
e75e2cdac7 | ||
|
2eaa4d80a0 | ||
|
dddb64dc35 | ||
|
1a3d17da91 | ||
|
2a24a61126 | ||
|
2e2e2e2cad | ||
|
68a5842f06 | ||
|
5f7ad767db | ||
|
a4a4fffffb | ||
|
1830296df3 | ||
|
e536b02248 | ||
|
1cc1a46c2e | ||
|
31422ae25d | ||
|
57921d7dbd | ||
|
8e8073c421 | ||
|
52ee7a8748 | ||
|
71dc9f6128 | ||
|
fd67ee9ecd | ||
|
bec18dc6f9 | ||
|
5b714f05f8 | ||
|
7c43eed2c1 | ||
|
23e7d8f6a7 | ||
|
df06cb4d71 | ||
|
0fec38fe72 | ||
|
80a4852266 | ||
|
4c4471c66d | ||
|
6378c8ed65 | ||
|
49e5f9c428 | ||
|
5f1df55fcb | ||
|
e2c78c00e5 | ||
|
bd7092a9fe | ||
|
c7d9719910 | ||
|
7ae7920572 | ||
|
4b6163aef3 |
76
.github/ISSUE_TEMPLATE/bug.yml
vendored
76
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -1,75 +1,15 @@
|
||||
name: Bug Report
|
||||
description: Something is not working right
|
||||
labels: ["bug"]
|
||||
name: Do not open issues, go to discussions please!
|
||||
description: Do not open an issue
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Already reported ? *
|
||||
description: Before opening a new bug report, please take a moment to search through the current open and closed issues to check if it already exists.
|
||||
label: Please close this issue.
|
||||
description: Users cannot open issues. I want my issue to be closed.
|
||||
options:
|
||||
- label: I have searched the existing open and closed issues.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Regression?
|
||||
description: |
|
||||
Regression means that something used to work but no longer does.
|
||||
**BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue
|
||||
multiple: true
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
validations:
|
||||
required: true
|
||||
- label: Yes, I want this issue to be closed.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: ver
|
||||
id: body
|
||||
attributes:
|
||||
label: System Info and Version
|
||||
description: |
|
||||
Paste the output of `hyprctl systeminfo -c` here. If you can't
|
||||
launch Hyprland, paste the output of `Hyprland --systeminfo`.
|
||||
If `Hyprland --systeminfo` errors out (added in 0.44.0), find
|
||||
and paste the Hyprland version manually.
|
||||
value: "<details>
|
||||
<summary>System/Version info</summary>
|
||||
|
||||
|
||||
```sh
|
||||
|
||||
<Paste the output of the command here>
|
||||
|
||||
```
|
||||
|
||||
|
||||
</details>"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Description
|
||||
description: "What went wrong?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: "How can someone else reproduce the issue?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Crash reports, logs, images, videos
|
||||
description: |
|
||||
Anything that can help. Please always ATTACH and not paste them.
|
||||
Logs can be found in $XDG_RUNTIME_DIR/hypr
|
||||
Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
|
||||
|
||||
label: Issue body
|
||||
|
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
@ -1,19 +0,0 @@
|
||||
name: Feature Request
|
||||
description: I'd like to request additional functionality
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before opening a new issue, take a moment to search through the current open ones.
|
||||
|
||||
---
|
||||
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Description
|
||||
description: "Describe your idea"
|
||||
validations:
|
||||
required: true
|
||||
|
2
.github/actions/setup_base/action.yml
vendored
2
.github/actions/setup_base/action.yml
vendored
@ -20,6 +20,7 @@ runs:
|
||||
clang \
|
||||
cmake \
|
||||
git \
|
||||
glaze \
|
||||
glm \
|
||||
glslang \
|
||||
go \
|
||||
@ -35,6 +36,7 @@ runs:
|
||||
libinput \
|
||||
libjxl \
|
||||
libliftoff \
|
||||
libspng \
|
||||
libwebp \
|
||||
libxcursor \
|
||||
libxcvt \
|
||||
|
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Build Hyprland
|
||||
run: |
|
||||
make all
|
||||
CFLAGS=-Werror CXXFLAGS=-Werror make all
|
||||
|
||||
- name: Compress and package artifacts
|
||||
run: |
|
||||
@ -107,6 +107,7 @@ jobs:
|
||||
run: make release
|
||||
|
||||
clang-format:
|
||||
permissions: read-all
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
|
48
.github/workflows/clang-format.yml
vendored
Normal file
48
.github/workflows/clang-format.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: clang-format
|
||||
on: pull_request_target
|
||||
jobs:
|
||||
clang-format:
|
||||
permissions: write-all
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Checkout repository actions
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github/actions
|
||||
|
||||
- name: Setup base
|
||||
uses: ./.github/actions/setup_base
|
||||
|
||||
- name: Configure
|
||||
run: meson setup build -Ddefault_library=static
|
||||
|
||||
- name: clang-format check
|
||||
run: ninja -C build clang-format-check
|
||||
|
||||
- name: clang-format apply
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
run: ninja -C build clang-format
|
||||
|
||||
- name: Create patch
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch
|
||||
echo '<details>' >> clang-format.patch
|
||||
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
|
||||
echo >> clang-format.patch
|
||||
echo '```diff' >> clang-format.patch
|
||||
git diff >> clang-format.patch
|
||||
echo '```' >> clang-format.patch
|
||||
echo >> clang-format.patch
|
||||
echo '</details>' >> clang-format.patch
|
||||
|
||||
- name: Comment patch
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
uses: mshick/add-pr-comment@v2
|
||||
with:
|
||||
message-path: |
|
||||
clang-format.patch
|
101
.github/workflows/close-issues.yml
vendored
Normal file
101
.github/workflows/close-issues.yml
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
name: Close Unauthorized Issues
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
close-unauthorized-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
# XXX: This *could* be done in Bash by abusing GitHub's own tool to interact with its API
|
||||
# but that's too much of a hack, and we'll be adding a layer of abstraction. github-script
|
||||
# is a workflow that eases interaction with GitHub API in the workflow run context.
|
||||
- name: "Close 'unauthorized' issues"
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const ALLOWED_USERS = ['vaxerski', 'fufexan', 'NotAShelf'];
|
||||
const CLOSING_COMMENT = 'Users are no longer allowed to open issues themselves, please open a discussion instead.\n\nPlease see the [wiki](https://wiki.hyprland.org/Contributing-and-Debugging/Issue-Guidelines/) on why this is the case.\n\nWe are volunteers, and we need your cooperation to make the best software we can. Thank you for understanding! ❤️\n\n[Open a discussion here](https://github.com/hyprwm/Hyprland/discussions)';
|
||||
|
||||
async function closeUnauthorizedIssue(issueNumber, userName) {
|
||||
if (ALLOWED_USERS.includes(userName)) {
|
||||
console.log(`Issue #${issueNumber} - Created by authorized user ${userName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Issue #${issueNumber} - Unauthorized, closing`);
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned'
|
||||
});
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: CLOSING_COMMENT
|
||||
});
|
||||
}
|
||||
|
||||
if (context.eventName === 'issues' && context.payload.action === 'opened') {
|
||||
// Direct access to the issue that triggered the workflow
|
||||
const issue = context.payload.issue;
|
||||
|
||||
// Skip if this is a PR
|
||||
if (issue.pull_request) {
|
||||
console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the single issue that triggered the workflow
|
||||
await closeUnauthorizedIssue(issue.number, issue.user.login);
|
||||
} else {
|
||||
// For manual runs, we need to handle pagination
|
||||
async function* fetchAllOpenIssues() {
|
||||
let page = 1;
|
||||
let hasNextPage = true;
|
||||
|
||||
while (hasNextPage) {
|
||||
const response = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
page: page
|
||||
});
|
||||
|
||||
if (response.data.length === 0) {
|
||||
hasNextPage = false;
|
||||
} else {
|
||||
for (const issue of response.data) {
|
||||
yield issue;
|
||||
}
|
||||
page++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process issues one by one
|
||||
for await (const issue of fetchAllOpenIssues()) {
|
||||
try {
|
||||
// Skip pull requests
|
||||
if (issue.pull_request) {
|
||||
console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await closeUnauthorizedIssue(issue.number, issue.user.login);
|
||||
} catch (error) {
|
||||
console.error(`Error processing issue #${issue.number}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
1
.github/workflows/nix-build.yml
vendored
1
.github/workflows/nix-build.yml
vendored
@ -19,7 +19,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
|
28
.github/workflows/stale.yml
vendored
28
.github/workflows/stale.yml
vendored
@ -1,28 +0,0 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "7 */4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'hyprwm/Hyprland'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.STALEBOT_PAT }}
|
||||
stale-issue-label: "stale"
|
||||
stale-pr-label: "stale"
|
||||
operations-per-run: 40
|
||||
days-before-close: -1
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -28,6 +28,8 @@ protocols/*.c*
|
||||
protocols/*.h*
|
||||
.ccls-cache
|
||||
*.so
|
||||
src/render/shaders/*.inc
|
||||
src/render/shaders/Shaders.hpp
|
||||
|
||||
hyprctl/hyprctl
|
||||
|
||||
|
@ -25,6 +25,9 @@ message(STATUS "Gathering git info")
|
||||
# Get git info hash and branch
|
||||
execute_process(COMMAND ./scripts/generateVersion.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
# Make shader files includable
|
||||
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
@ -77,6 +80,7 @@ add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
|
||||
include_directories(. "src/" "protocols/")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CXX_STANDARD_REQUIRED ON)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
@ -85,6 +89,7 @@ add_compile_options(
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-narrowing
|
||||
-Wno-pointer-arith
|
||||
-Wno-clobbered
|
||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||
|
||||
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
|
||||
@ -94,20 +99,24 @@ message(STATUS "Checking deps...")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(LEGACY_RENDERER)
|
||||
set(GLES_VERSION "GLES2")
|
||||
else()
|
||||
set(GLES_VERSION "GLES3")
|
||||
endif()
|
||||
set(GLES_VERSION "GLES3")
|
||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5)
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
|
||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
||||
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
|
||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.2.3)
|
||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
|
||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.7.0)
|
||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.3)
|
||||
|
||||
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
||||
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
|
||||
list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR)
|
||||
list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH)
|
||||
|
||||
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
|
||||
add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR})
|
||||
add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR})
|
||||
add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH})
|
||||
add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
|
||||
add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
|
||||
add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
|
||||
@ -120,7 +129,7 @@ pkg_check_modules(
|
||||
xkbcommon
|
||||
uuid
|
||||
wayland-server>=1.22.90
|
||||
wayland-protocols
|
||||
wayland-protocols>=1.43
|
||||
cairo
|
||||
pango
|
||||
pangocairo
|
||||
@ -197,9 +206,10 @@ if(NOT HAS_TIMERFD AND epoll_FOUND)
|
||||
target_link_libraries(Hyprland PkgConfig::epoll)
|
||||
endif()
|
||||
|
||||
if(LEGACY_RENDERER)
|
||||
message(STATUS "Using the legacy GLES2 renderer!")
|
||||
add_compile_definitions(LEGACY_RENDERER)
|
||||
check_include_file("sys/inotify.h" HAS_INOTIFY)
|
||||
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
|
||||
if(NOT HAS_INOTIFY AND inotify_FOUND)
|
||||
target_link_libraries(Hyprland PkgConfig::inotify)
|
||||
endif()
|
||||
|
||||
if(NO_XWAYLAND)
|
||||
@ -248,7 +258,15 @@ target_precompile_headers(Hyprland PRIVATE
|
||||
|
||||
message(STATUS "Setting link libraries")
|
||||
|
||||
target_link_libraries(Hyprland rt PkgConfig::aquamarine_dep PkgConfig::hyprlang_dep PkgConfig::hyprutils_dep PkgConfig::hyprcursor_dep PkgConfig::hyprgraphics_dep PkgConfig::deps)
|
||||
target_link_libraries(
|
||||
Hyprland
|
||||
rt
|
||||
PkgConfig::aquamarine_dep
|
||||
PkgConfig::hyprlang_dep
|
||||
PkgConfig::hyprutils_dep
|
||||
PkgConfig::hyprcursor_dep
|
||||
PkgConfig::hyprgraphics_dep
|
||||
PkgConfig::deps)
|
||||
if(udis_dep_FOUND)
|
||||
target_link_libraries(Hyprland PkgConfig::udis_dep)
|
||||
else()
|
||||
@ -290,7 +308,7 @@ endfunction()
|
||||
|
||||
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
|
||||
|
||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
|
||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
||||
if(hyprland_protocols_dep_FOUND)
|
||||
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
|
||||
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
|
||||
@ -316,8 +334,13 @@ protocolnew("protocols" "kde-server-decoration" true)
|
||||
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
|
||||
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
|
||||
protocolnew("protocols" "xx-color-management-v4" true)
|
||||
protocolnew("protocols" "frog-color-management-v1" true)
|
||||
protocolnew("protocols" "wayland-drm" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1" true)
|
||||
|
||||
protocolnew("staging/tearing-control" "tearing-control-v1" false)
|
||||
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
|
||||
@ -350,12 +373,22 @@ protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
|
||||
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
|
||||
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
|
||||
protocolnew("staging/security-context" "security-context-v1" false)
|
||||
protocolnew("staging/content-type" "content-type-v1" false)
|
||||
protocolnew("staging/color-management" "color-management-v1" false)
|
||||
protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false)
|
||||
protocolnew("staging/xdg-system-bell" "xdg-system-bell-v1" false)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
# tools
|
||||
add_subdirectory(hyprctl)
|
||||
add_subdirectory(hyprpm)
|
||||
|
||||
if(NO_HYPRPM)
|
||||
message(STATUS "hyprpm is disabled")
|
||||
else()
|
||||
add_subdirectory(hyprpm)
|
||||
message(STATUS "hyprpm is enabled (NO_HYPRPM not defined)")
|
||||
endif()
|
||||
|
||||
# binary and symlink
|
||||
install(TARGETS Hyprland)
|
||||
@ -366,7 +399,6 @@ install(
|
||||
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
|
||||
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
|
||||
)")
|
||||
|
||||
# session file
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
||||
@ -410,4 +442,7 @@ install(
|
||||
DIRECTORY ${HEADERS_SRC}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h*")
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "*.inc"
|
||||
)
|
||||
|
10
Makefile
10
Makefile
@ -3,14 +3,6 @@ PREFIX = /usr/local
|
||||
stub:
|
||||
@echo "Do not run $(MAKE) directly without any arguments. Please refer to the wiki on how to compile Hyprland."
|
||||
|
||||
legacyrenderer:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
legacyrendererdebug:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
|
||||
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
release:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
@ -52,7 +44,7 @@ installheaders:
|
||||
|
||||
cmake --build ./build --config Release --target generate-protocol-headers
|
||||
|
||||
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
|
||||
find src -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.inc' \) -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
|
||||
cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols
|
||||
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
|
||||
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
|
||||
|
32
SECURITY.md
Normal file
32
SECURITY.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Hyprland Development Security Policy
|
||||
|
||||
If you have a bug that affects the security of your system, you may
|
||||
want to privately disclose it instead of making it immediately public.
|
||||
|
||||
## Supported versions
|
||||
|
||||
_Only_ the most recent release on Github is supported. There are no LTS releases.
|
||||
|
||||
## What is not a security issue
|
||||
|
||||
Some examples of issues that should not be reported as security issues:
|
||||
|
||||
- An app can execute a command when ran outside of a sandbox
|
||||
- An app can write / read hyprland sockets when ran outside of a sandbox
|
||||
- Crashes
|
||||
- Things that are protected via permissions when the permission system is disabled
|
||||
|
||||
## What is a security issue
|
||||
|
||||
Some examples of issues that should be reported as security issues:
|
||||
|
||||
- Sandboxed application executing arbitrary code via Hyprland
|
||||
- Application being able to modify Hyprland's code on the fly
|
||||
- Application being able to keylog / track user's activity beyond what the wayland protocols allow
|
||||
|
||||
## How to report security issues
|
||||
|
||||
Please report your security issues via either of these channels:
|
||||
- Mail: `vaxry [at] vaxry [dot] net`
|
||||
- Matrix: `@vaxry:matrix.vaxry.net`
|
||||
- Discord: `@vaxry`
|
@ -52,6 +52,23 @@ env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
###################
|
||||
### PERMISSIONS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Permissions/
|
||||
# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
|
||||
# for security reasons
|
||||
|
||||
# ecosystem {
|
||||
# enforce_permissions = 1
|
||||
# }
|
||||
|
||||
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
|
||||
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
|
||||
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
@ -81,6 +98,7 @@ general {
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
@ -138,10 +156,10 @@ animations {
|
||||
# uncomment all if you wish to use that.
|
||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||
# workspace = f[1], gapsout:0, gapsin:0
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
@ -259,8 +277,8 @@ bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
@ -275,14 +293,11 @@ bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
# Example windowrule
|
||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
|
@ -22,5 +22,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
]
|
||||
}
|
120
flake.lock
generated
120
flake.lock
generated
@ -16,11 +16,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734364797,
|
||||
"narHash": "sha256-2h1c+P0v3l0Z/ypUSsAPhU/yiSRgFwjVFODWp0S3d/w=",
|
||||
"lastModified": 1745357003,
|
||||
"narHash": "sha256-jYwzQkv1r7HN/4qrAuKp+NR4YYNp2xDrOX5O9YVqkWo=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "8e77618b403a82fde2105a8e3cd7cabe7ef00952",
|
||||
"rev": "a19cf76ee1a15c1c12083fa372747ce46387289f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -79,11 +79,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734364709,
|
||||
"narHash": "sha256-+2bZJL2u5hva7rSp65OfKJBK+k03T6GB/NCvpoS1OOo=",
|
||||
"lastModified": 1745948457,
|
||||
"narHash": "sha256-lzTV10FJTCGNtMdgW5YAhCAqezeAzKOd/97HbQK8GTU=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"rev": "f388aacd22be4a6e4d634fbaf6f75eb0713d239a",
|
||||
"rev": "ac903e80b33ba6a88df83d02232483d99f327573",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -105,11 +105,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733684019,
|
||||
"narHash": "sha256-2kYREgmSmbLsmDpLEq96hxVAU3qz8aCvVhF65yCFZHY=",
|
||||
"lastModified": 1745015490,
|
||||
"narHash": "sha256-apEJ9zoSzmslhJ2vOKFcXTMZLUFYzh1ghfB6Rbw3Low=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "fb2c0268645a77403af3b8a4ce8fa7ba5917f15d",
|
||||
"rev": "60754910946b4e2dc1377b967b7156cb989c5873",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -128,11 +128,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728345020,
|
||||
"narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
|
||||
"lastModified": 1743714874,
|
||||
"narHash": "sha256-yt8F7NhMFCFHUHy/lNjH/pjZyIDFNk52Q4tivQ31WFo=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "a7c183800e74f337753de186522b9017a07a8cee",
|
||||
"rev": "3a5c2bda1c1a4e55cc1330c782547695a93f05b2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -141,9 +141,44 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qt-support": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"hyprland-qtutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland-qtutils",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-qtutils",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737634706,
|
||||
"narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "8810df502cdee755993cb803eba7b23f189db795",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qtutils": {
|
||||
"inputs": {
|
||||
"hyprland-qt-support": "hyprland-qt-support",
|
||||
"hyprlang": [
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprland-qtutils",
|
||||
"hyprlang",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
@ -154,11 +189,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733940128,
|
||||
"narHash": "sha256-hmfXWj2GA9cj1QUkPFYtAAeohhs615zL4E3APy3FnvQ=",
|
||||
"lastModified": 1745951494,
|
||||
"narHash": "sha256-2dModE32doiyQMmd6EDAQeZnz+5LOs6KXyE0qX76WIg=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"rev": "3833097e50473a152dd614d4b468886840b4ea78",
|
||||
"rev": "4be1d324faf8d6e82c2be9f8510d299984dfdd2e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -180,11 +215,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734364628,
|
||||
"narHash": "sha256-ii8fzJfI953n/EmIxVvq64ZAwhvwuuPHWfGd61/mJG8=",
|
||||
"lastModified": 1746655412,
|
||||
"narHash": "sha256-kVQ0bHVtX6baYxRWWIh4u3LNJZb9Zcm2xBeDPOGz5BY=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "16e59c1eb13d9fb6de066f54e7555eb5e8a4aba5",
|
||||
"rev": "557241780c179cf7ef224df392f8e67dab6cef83",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -203,11 +238,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733502241,
|
||||
"narHash": "sha256-KAUNC4Dgq8WQjYov5auBw/usaHixhacvb7cRDd0AG/k=",
|
||||
"lastModified": 1746635225,
|
||||
"narHash": "sha256-W9G9bb0zRYDBRseHbVez0J8qVpD5QbizX67H/vsudhM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "104117aed6dd68561be38b50f218190aa47f2cd8",
|
||||
"rev": "674ea57373f08b7609ce93baff131117a0dfe70d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -226,11 +261,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726874836,
|
||||
"narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
|
||||
"lastModified": 1739870480,
|
||||
"narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
|
||||
"rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -241,11 +276,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1734119587,
|
||||
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
|
||||
"lastModified": 1747327360,
|
||||
"narHash": "sha256-LSmTbiq/nqZR9B2t4MRnWG7cb0KVNU70dB7RT4+wYK4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
|
||||
"rev": "e06158e58f3adee28b139e9c2bcfcc41f8625b46",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -255,37 +290,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1730741070,
|
||||
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734279981,
|
||||
"narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
|
||||
"lastModified": 1747372754,
|
||||
"narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
|
||||
"rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -347,11 +365,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734124279,
|
||||
"narHash": "sha256-YNpFfiQjYt2o6LGcMN9NkjVvprC8ELrIpLHlbZbclRM=",
|
||||
"lastModified": 1745871725,
|
||||
"narHash": "sha256-M24SNc2flblWGXFkGQfqSlEOzAGZnMc9QG3GH4K/KbE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "0c6861f819f6d31f6195c9864709b2556f00b5cf",
|
||||
"rev": "76bbf1a6b1378e4ab5230bad00ad04bc287c969e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -39,7 +39,7 @@
|
||||
url = "github:hyprwm/hyprland-qtutils";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
};
|
||||
|
||||
hyprlang = {
|
||||
@ -132,7 +132,6 @@
|
||||
# hyprland-packages
|
||||
hyprland
|
||||
hyprland-debug
|
||||
hyprland-legacy-renderer
|
||||
hyprland-unwrapped
|
||||
# hyprland-extras
|
||||
xdg-desktop-portal-hyprland
|
||||
@ -157,5 +156,11 @@
|
||||
|
||||
nixosModules.default = import ./nix/module.nix inputs;
|
||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||
|
||||
# Hydra build jobs
|
||||
# Recent versions of Hydra can aggregate jobsets from 'hydraJobs' intead of a release.nix
|
||||
# or similar. Remember to filter large or incompatible attributes here. More eval jobs can
|
||||
# be added by merging, e.g., self.packages // self.devShells.
|
||||
hydraJobs = self.packages;
|
||||
};
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ commands:
|
||||
getoption <option> → Gets the config option status (values)
|
||||
globalshortcuts → Lists all global shortcuts
|
||||
hyprpaper ... → Issue a hyprpaper request
|
||||
hyprsunset ... → Issue a hyprsunset request
|
||||
instances → Lists all running instances of Hyprland with
|
||||
their info
|
||||
keyword <name> <value> → Issue a keyword to call a config keyword
|
||||
@ -81,6 +82,16 @@ requests:
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view HYPRSUNSET_HELP = R"#(usage: hyprctl [flags] hyprsunset <request>
|
||||
|
||||
requests:
|
||||
temperature <temp> → Enable blue-light filter
|
||||
identity → Disable blue-light filter
|
||||
gamma <gamma> → Enable gamma filter
|
||||
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view NOTIFY_HELP = R"#(usage: hyprctl [flags] notify <icon> <time_ms> <color> <message...>
|
||||
|
||||
icon:
|
||||
|
@ -23,7 +23,7 @@ _hyprctl () {
|
||||
local words cword
|
||||
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
|
||||
|
||||
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize)
|
||||
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize sendkeystate)
|
||||
declare -A literal_transitions
|
||||
literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)"
|
||||
literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)"
|
||||
|
@ -29,7 +29,7 @@ function _hyprctl
|
||||
set COMP_CWORD (count $COMP_WORDS)
|
||||
end
|
||||
|
||||
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize"
|
||||
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate"
|
||||
|
||||
set descriptions
|
||||
set descriptions[1] "Resize the active window"
|
||||
|
@ -106,6 +106,7 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
|
||||
| (execr) "Execute a raw shell command"
|
||||
| (pass) "Pass the key to a specified window"
|
||||
| (sendshortcut) "On shortcut X sends shortcut Y to a specified window"
|
||||
| (sendkeystate) "Send a key with specific state (down/repeat/up) to a specified window (window must keep focus for events to continue)"
|
||||
| (killactive) "Close the active window"
|
||||
| (closewindow) "Close a specified window"
|
||||
| (workspace) "Change the workspace"
|
||||
|
@ -17,7 +17,7 @@ _hyprctl_cmd_0 () {
|
||||
}
|
||||
|
||||
_hyprctl () {
|
||||
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize")
|
||||
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate")
|
||||
|
||||
local -A descriptions
|
||||
descriptions[1]="Resize the active window"
|
||||
|
@ -12,22 +12,17 @@
|
||||
#include <sys/un.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <format>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <cstdarg>
|
||||
#include <sys/socket.h>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <cstring>
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#include "Strings.hpp"
|
||||
@ -154,8 +149,16 @@ int rollingRead(const int socket) {
|
||||
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
if (SERVERSOCKET < 0) {
|
||||
log("Couldn't open a socket (1)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
if (setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)) < 0) {
|
||||
log("Couldn't set socket timeout (2)");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
|
||||
|
||||
@ -164,14 +167,9 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
log("Couldn't open a socket (1)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (instanceSignature.empty()) {
|
||||
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||
return 2;
|
||||
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)");
|
||||
return 3;
|
||||
}
|
||||
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
@ -184,39 +182,40 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
log("Couldn't connect to " + socketPath + ". (3)");
|
||||
return 3;
|
||||
log("Couldn't connect to " + socketPath + ". (4)");
|
||||
return 4;
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't write (4)");
|
||||
return 4;
|
||||
log("Couldn't write (5)");
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (needRoll)
|
||||
return rollingRead(SERVERSOCKET);
|
||||
|
||||
std::string reply = "";
|
||||
char buffer[8192] = {0};
|
||||
std::string reply = "";
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
log("Hyprland IPC didn't respond in time\n");
|
||||
log("Couldn't read (5)");
|
||||
return 5;
|
||||
log("Couldn't read (6)");
|
||||
return 6;
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == 8192) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
while (sizeWritten == BUFFER_SIZE) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't read (5)");
|
||||
return 5;
|
||||
log("Couldn't read (6)");
|
||||
return 6;
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
@ -228,7 +227,7 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requestHyprpaper(std::string arg) {
|
||||
int requestIPC(std::string filename, std::string arg) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
@ -246,7 +245,7 @@ int requestHyprpaper(std::string arg) {
|
||||
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.hyprpaper.sock";
|
||||
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename;
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
@ -264,10 +263,10 @@ int requestHyprpaper(std::string arg) {
|
||||
log("Couldn't write (4)");
|
||||
return 4;
|
||||
}
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't read (5)");
|
||||
@ -281,11 +280,21 @@ int requestHyprpaper(std::string arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requestHyprpaper(std::string arg) {
|
||||
return requestIPC(".hyprpaper.sock", arg);
|
||||
}
|
||||
|
||||
int requestHyprsunset(std::string arg) {
|
||||
return requestIPC(".hyprsunset.sock", arg);
|
||||
}
|
||||
|
||||
void batchRequest(std::string arg, bool json) {
|
||||
std::string commands = arg.substr(arg.find_first_of(' ') + 1);
|
||||
|
||||
if (json)
|
||||
if (json) {
|
||||
RE2::GlobalReplace(&commands, ";\\s*", ";j/");
|
||||
commands.insert(0, "j/");
|
||||
}
|
||||
|
||||
std::string rq = "[[BATCH]]" + commands;
|
||||
request(rq);
|
||||
@ -383,6 +392,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (cmd == "hyprpaper") {
|
||||
std::println("{}", HYPRPAPER_HELP);
|
||||
} else if (cmd == "hyprsunset") {
|
||||
std::println("{}", HYPRSUNSET_HELP);
|
||||
} else if (cmd == "notify") {
|
||||
std::println("{}", NOTIFY_HELP);
|
||||
} else if (cmd == "output") {
|
||||
@ -464,6 +475,8 @@ int main(int argc, char** argv) {
|
||||
batchRequest(fullRequest, json);
|
||||
else if (fullRequest.contains("/hyprpaper"))
|
||||
exitStatus = requestHyprpaper(fullRequest);
|
||||
else if (fullRequest.contains("/hyprsunset"))
|
||||
exitStatus = requestHyprsunset(fullRequest);
|
||||
else if (fullRequest.contains("/switchxkblayout"))
|
||||
exitStatus = request(fullRequest, 2);
|
||||
else if (fullRequest.contains("/seterror"))
|
||||
|
@ -9,11 +9,25 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.2.4)
|
||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
||||
|
||||
find_package(glaze QUIET)
|
||||
if (NOT glaze_FOUND)
|
||||
set(GLAZE_VERSION v5.1.1)
|
||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
glaze
|
||||
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
||||
GIT_TAG ${GLAZE_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(glaze)
|
||||
endif()
|
||||
|
||||
add_executable(hyprpm ${SRCFILES})
|
||||
|
||||
target_link_libraries(hyprpm PUBLIC PkgConfig::hyprpm_deps)
|
||||
target_link_libraries(hyprpm PUBLIC PkgConfig::hyprpm_deps glaze::glaze)
|
||||
|
||||
# binary
|
||||
install(TARGETS hyprpm)
|
||||
|
@ -14,7 +14,7 @@ hyprpm [<FLAGS>]... <ARGUMENT>
|
||||
| (list) "List all installed plugins"
|
||||
| (enable <PLUGINS>) "Load a plugin"
|
||||
| (disable <PLUGINS>) "Unload a plugin"
|
||||
| (reload) "Reload all plugins"
|
||||
| (reload) "Reload plugins to match the enabled/disabled state. Use -f to force reload."
|
||||
;
|
||||
|
||||
<PLUGINS> ::= {{{ hyprpm list | awk '/Plugin/{print $4}' }}};
|
||||
|
@ -1,45 +1,91 @@
|
||||
#include "DataState.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <toml++/toml.hpp>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "PluginManager.hpp"
|
||||
#include "../helpers/Die.hpp"
|
||||
#include "../helpers/Sys.hpp"
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
|
||||
std::string DataState::getDataStatePath() {
|
||||
const auto HOME = getenv("HOME");
|
||||
if (!HOME) {
|
||||
std::println(stderr, "DataState: no $HOME");
|
||||
throw std::runtime_error("no $HOME");
|
||||
return "";
|
||||
static std::string getTempRoot() {
|
||||
static auto ENV = getenv("XDG_RUNTIME_DIR");
|
||||
if (!ENV) {
|
||||
std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME");
|
||||
const auto STR = ENV + std::string{"/hyprpm/"};
|
||||
|
||||
if (XDG_DATA_HOME)
|
||||
return std::string{XDG_DATA_HOME} + "/hyprpm";
|
||||
return std::string{HOME} + "/.local/share/hyprpm";
|
||||
return STR;
|
||||
}
|
||||
|
||||
// write the state to a file
|
||||
static bool writeState(const std::string& str, const std::string& to) {
|
||||
// create temp file in a safe temp root
|
||||
std::ofstream of(getTempRoot() + ".temp-state", std::ios::trunc);
|
||||
if (!of.good())
|
||||
return false;
|
||||
|
||||
of << str;
|
||||
of.close();
|
||||
|
||||
return NSys::root::install(getTempRoot() + ".temp-state", to, "644");
|
||||
}
|
||||
|
||||
std::filesystem::path DataState::getDataStatePath() {
|
||||
return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername);
|
||||
}
|
||||
|
||||
std::string DataState::getHeadersPath() {
|
||||
return getDataStatePath() + "/headersRoot";
|
||||
return getDataStatePath() / "headersRoot";
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> DataState::getPluginStates() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
std::vector<std::filesystem::path> states;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(getDataStatePath())) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
const auto stateFile = entry.path() / "state.toml";
|
||||
if (!std::filesystem::exists(stateFile))
|
||||
continue;
|
||||
|
||||
states.emplace_back(stateFile);
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
void DataState::ensureStateStoreExists() {
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
if (!std::filesystem::exists(PATH))
|
||||
std::filesystem::create_directories(PATH);
|
||||
|
||||
if (!std::filesystem::exists(getHeadersPath()))
|
||||
std::filesystem::create_directories(getHeadersPath());
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(getHeadersPath(), ec) || ec) {
|
||||
std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now..."));
|
||||
if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) {
|
||||
if (!NSys::root::createDirectory("/var/cache/hyprpm", "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!std::filesystem::exists(getDataStatePath(), ec) || ec) {
|
||||
if (!NSys::root::createDirectory(getDataStatePath().string(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!NSys::root::createDirectory(getHeadersPath(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
}
|
||||
|
||||
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath() + "/" + repo.name;
|
||||
const auto PATH = getDataStatePath() / repo.name;
|
||||
|
||||
std::filesystem::create_directories(PATH);
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("addNewPluginRepo: failed to create cache dir");
|
||||
}
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"repository", toml::table{
|
||||
@ -50,39 +96,36 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||
}}
|
||||
};
|
||||
for (auto const& p : repo.plugins) {
|
||||
// copy .so to the good place
|
||||
if (std::filesystem::exists(p.filename))
|
||||
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");
|
||||
const auto filename = p.name + ".so";
|
||||
|
||||
// copy .so to the good place and chmod 755
|
||||
if (std::filesystem::exists(p.filename)) {
|
||||
if (!NSys::root::install(p.filename, (PATH / filename).string(), "0755"))
|
||||
Debug::die("addNewPluginRepo: failed to install so file");
|
||||
}
|
||||
|
||||
DATA.emplace(p.name, toml::table{
|
||||
{"filename", p.name + ".so"},
|
||||
{"filename", filename},
|
||||
{"enabled", p.enabled},
|
||||
{"failed", p.failed}
|
||||
});
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
|
||||
ofs << DATA;
|
||||
ofs.close();
|
||||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
}
|
||||
|
||||
bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (URL == urlOrName || NAME == urlOrName)
|
||||
return true;
|
||||
@ -94,31 +137,29 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (URL == urlOrName || NAME == urlOrName) {
|
||||
|
||||
// unload the plugins!!
|
||||
for (const auto& file : std::filesystem::directory_iterator(entry.path())) {
|
||||
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
|
||||
if (!file.path().string().ends_with(".so"))
|
||||
continue;
|
||||
|
||||
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
|
||||
}
|
||||
|
||||
std::filesystem::remove_all(entry.path());
|
||||
const auto PATH = stateFile.parent_path().string();
|
||||
|
||||
if (!PATH.starts_with("/var/cache/hyprpm") || PATH.contains('\''))
|
||||
return; // WTF?
|
||||
|
||||
// scary!
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("removePluginRepo: failed to remove dir");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -127,9 +168,13 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
void DataState::updateGlobalState(const SGlobalState& state) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::filesystem::create_directories(PATH);
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("updateGlobalState: failed to create dir");
|
||||
}
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"state", toml::table{
|
||||
@ -139,20 +184,23 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
|
||||
ofs << DATA;
|
||||
ofs.close();
|
||||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
}
|
||||
|
||||
SGlobalState DataState::getGlobalState() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
const auto stateFile = getDataStatePath() / "state.toml";
|
||||
|
||||
if (!std::filesystem::exists(PATH + "/state.toml"))
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(stateFile, ec) || ec)
|
||||
return SGlobalState{};
|
||||
|
||||
auto DATA = toml::parse_file(PATH + "/state.toml");
|
||||
auto DATA = toml::parse_file(stateFile.c_str());
|
||||
|
||||
SGlobalState state;
|
||||
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
|
||||
@ -164,18 +212,9 @@ SGlobalState DataState::getGlobalState() {
|
||||
std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::vector<SPluginRepository> repos;
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
@ -208,17 +247,8 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
for (const auto& [key, val] : STATE) {
|
||||
if (key == "repository")
|
||||
continue;
|
||||
@ -231,11 +261,14 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
if (FAILED)
|
||||
return false;
|
||||
|
||||
(*STATE[key].as_table()).insert_or_assign("enabled", enabled);
|
||||
auto modifiedState = STATE;
|
||||
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
|
||||
|
||||
std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc);
|
||||
state << STATE;
|
||||
state.close();
|
||||
std::stringstream ss;
|
||||
ss << modifiedState;
|
||||
|
||||
if (!writeState(ss.str(), stateFile.string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -243,3 +276,18 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DataState::purgeAllCache() {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(getDataStatePath()) && !ec) {
|
||||
std::println("{}", infoString("Nothing to do"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PATH = getDataStatePath().string();
|
||||
if (PATH.contains('\''))
|
||||
return;
|
||||
// scary!
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("Failed to run a superuser cmd");
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Plugin.hpp"
|
||||
@ -9,14 +10,16 @@ struct SGlobalState {
|
||||
};
|
||||
|
||||
namespace DataState {
|
||||
std::string getDataStatePath();
|
||||
std::string getHeadersPath();
|
||||
void ensureStateStoreExists();
|
||||
void addNewPluginRepo(const SPluginRepository& repo);
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
std::filesystem::path getDataStatePath();
|
||||
std::string getHeadersPath();
|
||||
std::vector<std::filesystem::path> getPluginStates();
|
||||
void ensureStateStoreExists();
|
||||
void addNewPluginRepo(const SPluginRepository& repo);
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
void purgeAllCache();
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
};
|
86
hyprpm/src/core/HyprlandSocket.cpp
Normal file
86
hyprpm/src/core/HyprlandSocket.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "HyprlandSocket.hpp"
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
#include <print>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
static int getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
static std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
std::string NHyprlandSocket::send(const std::string& cmd) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
std::println("{}", failureString("Couldn't open a socket (1)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!HIS) {
|
||||
std::println("{}", failureString("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
std::println("{}", failureString("Couldn't connect to " + socketPath + ". (4)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't write (5)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string reply = "";
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't read (6)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == BUFFER_SIZE) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't read (7)"));
|
||||
return "";
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
|
||||
close(SERVERSOCKET);
|
||||
|
||||
return reply;
|
||||
}
|
7
hyprpm/src/core/HyprlandSocket.hpp
Normal file
7
hyprpm/src/core/HyprlandSocket.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NHyprlandSocket {
|
||||
std::string send(const std::string& cmd);
|
||||
};
|
@ -1,6 +1,12 @@
|
||||
#include "Manifest.hpp"
|
||||
#include <toml++/toml.hpp>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
// Alphanumerics and -_ allowed for plugin names. No magic names.
|
||||
// [A-Za-z0-9\-_]*
|
||||
static bool validManifestName(const std::string_view& n) {
|
||||
return std::ranges::all_of(n, [](const char& c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == '=' || (c >= '0' && c <= '9'); });
|
||||
}
|
||||
|
||||
CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
auto manifest = toml::parse_file(path);
|
||||
@ -11,11 +17,17 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_vPlugins.push_back(plugin);
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
for (auto& plugin : m_plugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.version = manifest[plugin.name]["version"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
|
||||
@ -37,21 +49,21 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == MANIFEST_HYPRPM) {
|
||||
m_sRepository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
m_repository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
if (authors) {
|
||||
for (auto&& a : *authors) {
|
||||
m_sRepository.authors.push_back(a.as_string()->value_or("?"));
|
||||
m_repository.authors.push_back(a.as_string()->value_or("?"));
|
||||
}
|
||||
} else {
|
||||
auto author = manifest["repository"]["author"].value_or("");
|
||||
if (!std::string{author}.empty())
|
||||
m_sRepository.authors.push_back(author);
|
||||
m_repository.authors.push_back(author);
|
||||
}
|
||||
|
||||
auto pins = manifest["repository"]["commit_pins"].as_array();
|
||||
@ -59,7 +71,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
for (auto&& pin : *pins) {
|
||||
auto pinArr = pin.as_array();
|
||||
if (pinArr && pinArr->get(1))
|
||||
m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
m_repository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,11 +80,17 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_vPlugins.push_back(plugin);
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
for (auto& plugin : m_plugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["output"].value_or("?");
|
||||
plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0);
|
||||
@ -94,12 +112,12 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ???
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
}
|
||||
}
|
@ -27,8 +27,8 @@ class CManifest {
|
||||
std::string name;
|
||||
std::vector<std::string> authors;
|
||||
std::vector<std::pair<std::string, std::string>> commitPins;
|
||||
} m_sRepository;
|
||||
} m_repository;
|
||||
|
||||
std::vector<SManifestPlugin> m_vPlugins;
|
||||
bool m_bGood = true;
|
||||
std::vector<SManifestPlugin> m_plugins;
|
||||
bool m_good = true;
|
||||
};
|
@ -4,13 +4,14 @@
|
||||
#include "../progress/CProgressBar.hpp"
|
||||
#include "Manifest.hpp"
|
||||
#include "DataState.hpp"
|
||||
#include "HyprlandSocket.hpp"
|
||||
#include "../helpers/Sys.hpp"
|
||||
#include "../helpers/Die.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
@ -21,6 +22,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <toml++/toml.hpp>
|
||||
#include <glaze/glaze.hpp>
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
@ -50,6 +52,13 @@ static std::string getTempRoot() {
|
||||
return STR;
|
||||
}
|
||||
|
||||
CPluginManager::CPluginManager() {
|
||||
if (NSys::isSuperuser())
|
||||
Debug::die("Don't run hyprpm as a superuser.");
|
||||
|
||||
m_szUsername = getpwuid(NSys::getUID())->pw_name;
|
||||
}
|
||||
|
||||
SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||
static bool onceRunning = false;
|
||||
static bool onceInstalled = false;
|
||||
@ -67,7 +76,7 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||
else
|
||||
onceInstalled = true;
|
||||
|
||||
const auto HLVERCALL = running ? execAndGet("hyprctl version") : execAndGet("Hyprland --version");
|
||||
const auto HLVERCALL = running ? NHyprlandSocket::send("/version") : execAndGet("Hyprland --version");
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
|
||||
|
||||
@ -83,13 +92,13 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
|
||||
|
||||
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
|
||||
hldate = hldate.substr(0, hldate.find("\n"));
|
||||
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(" "));
|
||||
hlcommits = hlcommits.substr(0, hlcommits.find(' '));
|
||||
}
|
||||
|
||||
int commits = 0;
|
||||
@ -130,7 +139,8 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
const auto HLVER = getHyprlandVersion();
|
||||
|
||||
if (!hasDeps()) {
|
||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
|
||||
std::println(stderr, "\n{}",
|
||||
failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config, git, g++, gcc"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -226,14 +236,14 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pManifest->m_bGood) {
|
||||
if (!pManifest->m_good) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.m_iSteps = 2;
|
||||
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
|
||||
for (auto const& pl : pManifest->m_vPlugins) {
|
||||
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_plugins.size()) + " plugins:"));
|
||||
for (auto const& pl : pManifest->m_plugins) {
|
||||
std::string message = "→ " + pl.name + " by ";
|
||||
for (auto const& a : pl.authors) {
|
||||
message += a + ", ";
|
||||
@ -246,12 +256,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
progress.printMessageAbove(message);
|
||||
}
|
||||
|
||||
if (!pManifest->m_sRepository.commitPins.empty()) {
|
||||
if (!pManifest->m_repository.commitPins.empty()) {
|
||||
// check commit pins
|
||||
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
|
||||
|
||||
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
||||
for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
|
||||
if (hl != HLVER.hash)
|
||||
continue;
|
||||
|
||||
@ -274,6 +284,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
|
||||
if (HEADERSSTATUS != HEADERS_OK) {
|
||||
std::println("\n{}", headerError(HEADERSSTATUS));
|
||||
std::println("\n{}", infoString("if the problem persists, try running hyprpm purge-cache."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -282,7 +293,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
progress.m_szCurrentMessage = "Building plugin(s)";
|
||||
progress.print();
|
||||
|
||||
for (auto& p : pManifest->m_vPlugins) {
|
||||
for (auto& p : pManifest->m_plugins) {
|
||||
std::string out;
|
||||
|
||||
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
|
||||
@ -325,11 +336,11 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
||||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
|
||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name;
|
||||
repo.url = url;
|
||||
repo.rev = rev;
|
||||
repo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_vPlugins) {
|
||||
for (auto const& p : pManifest->m_plugins) {
|
||||
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
|
||||
}
|
||||
DataState::addNewPluginRepo(repo);
|
||||
@ -378,7 +389,7 @@ eHeadersErrors CPluginManager::headersValid() {
|
||||
|
||||
// find headers commit
|
||||
const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
|
||||
auto headers = execAndGet(cmd.c_str());
|
||||
auto headers = execAndGet(cmd);
|
||||
|
||||
if (!headers.contains("-I/"))
|
||||
return HEADERS_MISSING;
|
||||
@ -436,7 +447,7 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
const auto HLVER = getHyprlandVersion(false);
|
||||
|
||||
if (!hasDeps()) {
|
||||
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
|
||||
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config, git, g++, gcc"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -464,7 +475,9 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning https://github.com/hyprwm/Hyprland, this might take a moment."));
|
||||
const auto& HL_URL = m_szCustomHlUrl.empty() ? "https://github.com/hyprwm/Hyprland" : m_szCustomHlUrl;
|
||||
|
||||
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning {}, this might take a moment.", HL_URL));
|
||||
|
||||
const bool bShallow = (HLVER.branch == "main") && !m_bNoShallow;
|
||||
|
||||
@ -475,12 +488,12 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
if (m_bVerbose && bShallow)
|
||||
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
||||
|
||||
std::string ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-{}{}", getTempRoot(), USERNAME,
|
||||
(bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
||||
std::string ret =
|
||||
execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
||||
|
||||
if (!std::filesystem::exists(WORKINGDIR)) {
|
||||
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
||||
ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/hyprland hyprland-{}", getTempRoot(), USERNAME));
|
||||
ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME));
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
||||
@ -547,13 +560,21 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
progress.m_szCurrentMessage = "Installing sources";
|
||||
progress.print();
|
||||
|
||||
const std::string& cmd =
|
||||
std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
|
||||
std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile", DataState::getHeadersPath(), WORKINGDIR);
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(verboseString("installation will run: {}", cmd));
|
||||
progress.printMessageAbove(verboseString("prepare install will run: {}", cmd));
|
||||
|
||||
ret = execAndGet(cmd);
|
||||
|
||||
cmd = std::format("make -C '{}' installheaders && chmod -R 644 '{}' && find '{}' -type d -exec chmod a+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(),
|
||||
DataState::getHeadersPath());
|
||||
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(verboseString("install will run as sudo: {}", cmd));
|
||||
|
||||
// WORKINGDIR and headersPath should not contain anything unsafe. Usernames can't contain cmd exec parts.
|
||||
ret = NSys::root::runAsSuperuserUnsafe(cmd);
|
||||
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("installer returned: {}", ret));
|
||||
|
||||
@ -567,9 +588,14 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
progress.m_szCurrentMessage = "Done!";
|
||||
progress.print();
|
||||
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
||||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
|
||||
std::print("\n");
|
||||
} else {
|
||||
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
|
||||
progress.printMessageAbove(infoString("if the problem persists, try running hyprpm purge-cache."));
|
||||
progress.m_iSteps = 5;
|
||||
progress.m_szCurrentMessage = "Failed";
|
||||
progress.print();
|
||||
@ -676,17 +702,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pManifest->m_bGood) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
||||
if (!pManifest->m_good) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a bad manifest"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
|
||||
if (repo.rev.empty() && !pManifest->m_repository.commitPins.empty()) {
|
||||
// check commit pins unless a revision is specified
|
||||
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
|
||||
|
||||
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
||||
for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
|
||||
if (hl != HLVER.hash)
|
||||
continue;
|
||||
|
||||
@ -696,7 +722,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& p : pManifest->m_vPlugins) {
|
||||
for (auto& p : pManifest->m_plugins) {
|
||||
std::string out;
|
||||
|
||||
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
|
||||
@ -738,7 +764,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
newrepo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_vPlugins) {
|
||||
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; });
|
||||
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
||||
}
|
||||
@ -781,7 +807,7 @@ bool CPluginManager::disablePlugin(const std::string& name) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
|
||||
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload) {
|
||||
if (headersValid() != HEADERS_OK) {
|
||||
std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
|
||||
return LOADSTATE_HEADERS_OUTDATED;
|
||||
@ -790,35 +816,28 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
|
||||
const auto HOME = getenv("HOME");
|
||||
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
if (!HOME || !HIS) {
|
||||
std::println(stderr, "PluginManager: no $HOME or HIS");
|
||||
std::println(stderr, "PluginManager: no $HOME or $HYPRLAND_INSTANCE_SIGNATURE");
|
||||
return LOADSTATE_FAIL;
|
||||
}
|
||||
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
|
||||
const auto HYPRPMPATH = DataState::getDataStatePath();
|
||||
|
||||
auto pluginLines = execAndGet("hyprctl plugins list | grep Plugin");
|
||||
const auto json = glz::read_json<glz::json_t::array_t>(NHyprlandSocket::send("j/plugins list"));
|
||||
if (!json) {
|
||||
std::println(stderr, "PluginManager: couldn't parse plugin list output");
|
||||
return LOADSTATE_FAIL;
|
||||
}
|
||||
|
||||
std::vector<std::string> loadedPlugins;
|
||||
for (const auto& plugin : json.value()) {
|
||||
if (!plugin.is_object() || !plugin.contains("name")) {
|
||||
std::println(stderr, "PluginManager: couldn't parse plugin object");
|
||||
return LOADSTATE_FAIL;
|
||||
}
|
||||
loadedPlugins.emplace_back(plugin["name"].get<std::string>());
|
||||
}
|
||||
|
||||
std::println("{}", successString("Ensuring plugin load state"));
|
||||
|
||||
// iterate line by line
|
||||
while (!pluginLines.empty()) {
|
||||
auto plLine = pluginLines.substr(0, pluginLines.find('\n'));
|
||||
|
||||
if (pluginLines.find('\n') != std::string::npos)
|
||||
pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
|
||||
else
|
||||
pluginLines = "";
|
||||
|
||||
if (plLine.back() != ':')
|
||||
continue;
|
||||
|
||||
plLine = plLine.substr(7);
|
||||
plLine = plLine.substr(0, plLine.find(" by "));
|
||||
|
||||
loadedPlugins.push_back(plLine);
|
||||
}
|
||||
|
||||
// get state
|
||||
const auto REPOS = DataState::getAllRepositories();
|
||||
|
||||
@ -849,11 +868,11 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
|
||||
// (and Hyprland needs to restart)
|
||||
bool hyprlandVersionMismatch = false;
|
||||
|
||||
// unload disabled plugins
|
||||
// unload disabled plugins (or all if forceReload is true)
|
||||
for (auto const& p : loadedPlugins) {
|
||||
if (!enabled(p)) {
|
||||
if (forceReload || !enabled(p)) {
|
||||
// unload
|
||||
if (!loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false)) {
|
||||
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p) / (p + ".so"), false)) {
|
||||
std::println("{}", infoString("{} will be unloaded after restarting Hyprland", p));
|
||||
hyprlandVersionMismatch = true;
|
||||
} else
|
||||
@ -867,10 +886,10 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
|
||||
if (!p.enabled)
|
||||
continue;
|
||||
|
||||
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
|
||||
if (!forceReload && std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
|
||||
continue;
|
||||
|
||||
if (!loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true)) {
|
||||
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) {
|
||||
std::println("{}", infoString("{} will be loaded after restarting Hyprland", p.name));
|
||||
hyprlandVersionMismatch = true;
|
||||
} else
|
||||
@ -888,14 +907,15 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
||||
auto HLVER = getHyprlandVersion(true);
|
||||
|
||||
if (state.headersHashCompiled != HLVER.hash) {
|
||||
std::println("{}", infoString("Running Hyprland version differs from plugin state, please restart Hyprland."));
|
||||
if (load)
|
||||
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (load)
|
||||
execAndGet("hyprctl plugin load " + path);
|
||||
NHyprlandSocket::send("/plugin load " + path);
|
||||
else
|
||||
execAndGet("hyprctl plugin unload " + path);
|
||||
NHyprlandSocket::send("/plugin unload " + path);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -920,7 +940,7 @@ void CPluginManager::listAllPlugins() {
|
||||
}
|
||||
|
||||
void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message) {
|
||||
execAndGet("hyprctl notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
|
||||
NHyprlandSocket::send("/notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
|
||||
}
|
||||
|
||||
std::string CPluginManager::headerError(const eHeadersErrors err) {
|
||||
@ -953,7 +973,7 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
|
||||
}
|
||||
|
||||
bool CPluginManager::hasDeps() {
|
||||
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config"};
|
||||
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config", "g++", "gcc", "git"};
|
||||
for (auto const& d : deps) {
|
||||
if (!execAndGet("command -v " + d).contains("/"))
|
||||
return false;
|
||||
|
@ -40,6 +40,8 @@ struct SHyprlandVersion {
|
||||
|
||||
class CPluginManager {
|
||||
public:
|
||||
CPluginManager();
|
||||
|
||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||
bool removePluginRepo(const std::string& urlOrName);
|
||||
|
||||
@ -51,7 +53,7 @@ class CPluginManager {
|
||||
|
||||
bool enablePlugin(const std::string& name);
|
||||
bool disablePlugin(const std::string& name);
|
||||
ePluginLoadStateReturn ensurePluginsLoadState();
|
||||
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
|
||||
|
||||
bool loadUnloadPlugin(const std::string& path, bool load);
|
||||
SHyprlandVersion getHyprlandVersion(bool running = true);
|
||||
@ -62,6 +64,7 @@ class CPluginManager {
|
||||
|
||||
bool m_bVerbose = false;
|
||||
bool m_bNoShallow = false;
|
||||
std::string m_szCustomHlUrl, m_szUsername;
|
||||
|
||||
// will delete recursively if exists!!
|
||||
bool createSafeDirectory(const std::string& path);
|
||||
|
15
hyprpm/src/helpers/Die.hpp
Normal file
15
hyprpm/src/helpers/Die.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace Debug {
|
||||
template <typename... Args>
|
||||
void die(std::format_string<Args...> fmt, Args&&... args) {
|
||||
const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::cout << "\n[ERR] " << logMsg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
};
|
168
hyprpm/src/helpers/Sys.cpp
Normal file
168
hyprpm/src/helpers/Sys.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#include "Sys.hpp"
|
||||
#include "Die.hpp"
|
||||
#include "StringUtils.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
inline constexpr std::array<std::string_view, 3> SUPERUSER_BINARIES = {
|
||||
"sudo",
|
||||
"doas",
|
||||
"run0",
|
||||
};
|
||||
|
||||
static std::string validSubinsAsStr() {
|
||||
std::ostringstream oss;
|
||||
auto it = SUPERUSER_BINARIES.begin();
|
||||
if (it != SUPERUSER_BINARIES.end()) {
|
||||
oss << *it++;
|
||||
for (; it != SUPERUSER_BINARIES.end(); ++it)
|
||||
oss << ", " << *it;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static bool executableExistsInPath(const std::string& exe) {
|
||||
const char* PATHENV = std::getenv("PATH");
|
||||
if (!PATHENV)
|
||||
return false;
|
||||
|
||||
CVarList paths(PATHENV, 0, ':', true);
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& PATH : paths) {
|
||||
std::filesystem::path candidate = std::filesystem::path(PATH) / exe;
|
||||
if (!std::filesystem::exists(candidate, ec) || ec)
|
||||
continue;
|
||||
if (!std::filesystem::is_regular_file(candidate, ec) || ec)
|
||||
continue;
|
||||
auto perms = std::filesystem::status(candidate, ec).permissions();
|
||||
if (ec)
|
||||
continue;
|
||||
if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string subin() {
|
||||
static std::string bin;
|
||||
static bool once = true;
|
||||
if (!once)
|
||||
return bin;
|
||||
|
||||
for (const auto& BIN : SUPERUSER_BINARIES) {
|
||||
if (!executableExistsInPath(std::string{BIN}))
|
||||
continue;
|
||||
|
||||
bin = BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
once = false;
|
||||
|
||||
if (bin.empty())
|
||||
Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr()));
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
static bool verifyStringValid(const std::string& s) {
|
||||
return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; });
|
||||
}
|
||||
|
||||
int NSys::getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
int NSys::getEUID() {
|
||||
const auto UID = geteuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
bool NSys::isSuperuser() {
|
||||
return getuid() != geteuid() || geteuid() == 0;
|
||||
}
|
||||
|
||||
void NSys::root::cacheSudo() {
|
||||
// "caches" the sudo so that the prompt later doesn't pop up in a weird spot
|
||||
// sudo will not ask us again for a moment
|
||||
CProcess proc(subin(), {"echo", "hyprland"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
void NSys::root::dropSudo() {
|
||||
if (subin() != "sudo") {
|
||||
std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin()));
|
||||
return;
|
||||
}
|
||||
|
||||
CProcess proc(subin(), {"-k"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
bool NSys::root::createDirectory(const std::string& path, const std::string& mode) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::removeRecursive(const std::string& path) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
std::error_code ec;
|
||||
const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec);
|
||||
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm"))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) {
|
||||
if (!verifyStringValid(what) || !verifyStringValid(where))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"install", "-m" + mode, "-o", "0", "-g", "0", what, where});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) {
|
||||
CProcess proc(subin(), {"/bin/sh", "-c", cmd});
|
||||
|
||||
if (!proc.runSync())
|
||||
return "";
|
||||
|
||||
return proc.stdOut();
|
||||
}
|
23
hyprpm/src/helpers/Sys.hpp
Normal file
23
hyprpm/src/helpers/Sys.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NSys {
|
||||
bool isSuperuser();
|
||||
int getUID();
|
||||
int getEUID();
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace root {
|
||||
void cacheSudo();
|
||||
void dropSudo();
|
||||
|
||||
//
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool createDirectory(const std::string& path, const std::string& mode);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool removeRecursive(const std::string& path);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool install(const std::string& what, const std::string& where, const std::string& mode);
|
||||
|
||||
// Do not use this unless absolutely necessary!
|
||||
std::string runAsSuperuserUnsafe(const std::string& cmd);
|
||||
};
|
||||
};
|
@ -2,33 +2,36 @@
|
||||
#include "helpers/StringUtils.hpp"
|
||||
#include "core/PluginManager.hpp"
|
||||
#include "core/DataState.hpp"
|
||||
#include "helpers/Sys.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||
┃
|
||||
┣ 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.
|
||||
┣ remove [url/name] → Remove an installed plugin repository
|
||||
┣ enable [name] → Enable a plugin
|
||||
┣ disable [name] → Disable a plugin
|
||||
┣ update → Check and update all plugins if needed
|
||||
┣ remove [url/name] → Remove an installed plugin repository.
|
||||
┣ enable [name] → Enable a plugin.
|
||||
┣ disable [name] → Disable a plugin.
|
||||
┣ update → Check and update all plugins if needed.
|
||||
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
||||
┣ list → List all installed plugins
|
||||
┣ list → List all installed plugins.
|
||||
┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers.
|
||||
┃
|
||||
┣ Flags:
|
||||
┃
|
||||
┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events)
|
||||
┣ --notify-fail | -nn → Send a hyprland notification for fail events only
|
||||
┣ --help | -h → Show this menu
|
||||
┣ --verbose | -v → Enable too much logging
|
||||
┣ --force | -f → Force an operation ignoring checks (e.g. update -f)
|
||||
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources
|
||||
┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events).
|
||||
┣ --notify-fail | -nn → Send a hyprland notification for fail events only.
|
||||
┣ --help | -h → Show this menu.
|
||||
┣ --verbose | -v → Enable too much logging.
|
||||
┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
|
||||
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources.
|
||||
┣ --hl-url | → Pass a custom hyprland source url.
|
||||
┗
|
||||
)#";
|
||||
|
||||
@ -45,6 +48,7 @@ int main(int argc, char** argv, char** envp) {
|
||||
|
||||
std::vector<std::string> command;
|
||||
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
|
||||
std::string customHlUrl;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (ARGS[i].starts_with("-")) {
|
||||
@ -59,6 +63,13 @@ int main(int argc, char** argv, char** envp) {
|
||||
verbose = true;
|
||||
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
|
||||
noShallow = true;
|
||||
} else if (ARGS[i] == "--hl-url") {
|
||||
if (i + 1 >= argc) {
|
||||
std::println(stderr, "Missing argument for --hl-url");
|
||||
return 1;
|
||||
}
|
||||
customHlUrl = ARGS[i + 1];
|
||||
i++;
|
||||
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
|
||||
force = true;
|
||||
std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
|
||||
@ -66,19 +77,19 @@ int main(int argc, char** argv, char** envp) {
|
||||
std::println(stderr, "Unrecognized option {}", ARGS[i]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
command.push_back(ARGS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.empty()) {
|
||||
std::println(stderr, "{}", HELP);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_pPluginManager = std::make_unique<CPluginManager>();
|
||||
g_pPluginManager->m_bVerbose = verbose;
|
||||
g_pPluginManager->m_bNoShallow = noShallow;
|
||||
g_pPluginManager = std::make_unique<CPluginManager>();
|
||||
g_pPluginManager->m_bVerbose = verbose;
|
||||
g_pPluginManager->m_bNoShallow = noShallow;
|
||||
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
|
||||
|
||||
if (command[0] == "add") {
|
||||
if (command.size() < 2) {
|
||||
@ -87,21 +98,30 @@ int main(int argc, char** argv, char** envp) {
|
||||
}
|
||||
|
||||
std::string rev = "";
|
||||
if (command.size() >= 3) {
|
||||
if (command.size() >= 3)
|
||||
rev = command[2];
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
|
||||
} else if (command[0] == "remove") {
|
||||
if (ARGS.size() < 2) {
|
||||
if (command.size() < 2) {
|
||||
std::println(stderr, "{}", failureString("Not enough args for remove."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
||||
} else if (command[0] == "update") {
|
||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||
bool headers = g_pPluginManager->updateHeaders(force);
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||
bool headers = g_pPluginManager->updateHeaders(force);
|
||||
|
||||
if (headers) {
|
||||
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
@ -122,7 +142,7 @@ int main(int argc, char** argv, char** envp) {
|
||||
} else if (notify)
|
||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
|
||||
} else if (command[0] == "enable") {
|
||||
if (ARGS.size() < 2) {
|
||||
if (command.size() < 2) {
|
||||
std::println(stderr, "{}", failureString("Not enough args for enable."));
|
||||
return 1;
|
||||
}
|
||||
@ -132,7 +152,10 @@ int main(int argc, char** argv, char** envp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
if (ret == LOADSTATE_HYPRLAND_UPDATED)
|
||||
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland.");
|
||||
@ -150,24 +173,36 @@ int main(int argc, char** argv, char** envp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
if (ret != LOADSTATE_OK)
|
||||
return 1;
|
||||
} else if (command[0] == "reload") {
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
|
||||
|
||||
if (ret != LOADSTATE_OK && notify) {
|
||||
switch (ret) {
|
||||
case LOADSTATE_FAIL:
|
||||
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
||||
case LOADSTATE_HEADERS_OUTDATED:
|
||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
|
||||
break;
|
||||
default: break;
|
||||
if (ret != LOADSTATE_OK) {
|
||||
if (notify) {
|
||||
switch (ret) {
|
||||
case LOADSTATE_FAIL:
|
||||
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
||||
case LOADSTATE_HEADERS_OUTDATED:
|
||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else if (notify && !notifyFail) {
|
||||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
||||
}
|
||||
} else if (command[0] == "purge-cache") {
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
DataState::purgeAllCache();
|
||||
} else if (command[0] == "list") {
|
||||
g_pPluginManager->listAllPlugins();
|
||||
} else {
|
||||
|
@ -8,6 +8,7 @@ executable(
|
||||
dependency('hyprutils', version: '>= 0.1.1'),
|
||||
dependency('threads'),
|
||||
dependency('tomlplusplus'),
|
||||
dependency('glaze', method: 'cmake'),
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
|
@ -1,82 +1,78 @@
|
||||
#include "CProgressBar.hpp"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
|
||||
#include <print>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "../helpers/Colors.hpp"
|
||||
|
||||
void CProgressBar::printMessageAbove(const std::string& msg) {
|
||||
struct winsize w;
|
||||
static winsize getTerminalSize() {
|
||||
winsize w{};
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return w;
|
||||
}
|
||||
|
||||
std::string spaces;
|
||||
spaces.reserve(w.ws_col);
|
||||
for (size_t i = 0; i < w.ws_col; ++i) {
|
||||
spaces += ' ';
|
||||
}
|
||||
static void clearCurrentLine() {
|
||||
std::print("\r\33[2K"); // ansi escape sequence to clear entire line
|
||||
}
|
||||
|
||||
std::println("\r{}\r{}", spaces, msg);
|
||||
print();
|
||||
void CProgressBar::printMessageAbove(const std::string& msg) {
|
||||
clearCurrentLine();
|
||||
std::print("\r{}\n", msg);
|
||||
|
||||
print(); // reprint bar underneath
|
||||
}
|
||||
|
||||
void CProgressBar::print() {
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
const auto w = getTerminalSize();
|
||||
|
||||
if (m_bFirstPrint)
|
||||
if (m_bFirstPrint) {
|
||||
std::print("\n");
|
||||
m_bFirstPrint = false;
|
||||
|
||||
std::string spaces;
|
||||
spaces.reserve(w.ws_col);
|
||||
for (size_t i = 0; i < w.ws_col; ++i) {
|
||||
spaces += ' ';
|
||||
m_bFirstPrint = false;
|
||||
}
|
||||
|
||||
std::print("\r{}\r", spaces);
|
||||
clearCurrentLine();
|
||||
|
||||
std::string message = "";
|
||||
|
||||
float percentDone = 0;
|
||||
if (m_fPercentage >= 0)
|
||||
float percentDone = 0.0f;
|
||||
if (m_fPercentage >= 0.0f)
|
||||
percentDone = m_fPercentage;
|
||||
else
|
||||
percentDone = (float)m_iSteps / (float)m_iMaxSteps;
|
||||
|
||||
const auto BARWIDTH = std::clamp(w.ws_col - static_cast<unsigned long>(m_szCurrentMessage.length()) - 2, 0UL, 50UL);
|
||||
|
||||
// draw bar
|
||||
message += std::string{" "} + Colors::GREEN;
|
||||
size_t i = 0;
|
||||
for (; i < std::floor(percentDone * BARWIDTH); ++i) {
|
||||
message += "━";
|
||||
else {
|
||||
// check for divide-by-zero
|
||||
percentDone = m_iMaxSteps > 0 ? static_cast<float>(m_iSteps) / m_iMaxSteps : 0.0f;
|
||||
}
|
||||
// clamp to ensure no overflows (sanity check)
|
||||
percentDone = std::clamp(percentDone, 0.0f, 1.0f);
|
||||
|
||||
const size_t BARWIDTH = std::clamp<size_t>(w.ws_col - m_szCurrentMessage.length() - 2, 0, 50);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << ' ' << Colors::GREEN;
|
||||
|
||||
size_t filled = static_cast<size_t>(std::floor(percentDone * BARWIDTH));
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < filled; ++i)
|
||||
oss << "━";
|
||||
|
||||
if (i < BARWIDTH) {
|
||||
i++;
|
||||
|
||||
message += std::string{"╍"} + Colors::RESET;
|
||||
|
||||
for (; i < BARWIDTH; ++i) {
|
||||
message += "━";
|
||||
}
|
||||
oss << "╍" << Colors::RESET;
|
||||
++i;
|
||||
for (; i < BARWIDTH; ++i)
|
||||
oss << "━";
|
||||
} else
|
||||
message += Colors::RESET;
|
||||
oss << Colors::RESET;
|
||||
|
||||
// draw progress
|
||||
if (m_fPercentage >= 0)
|
||||
message += " " + std::format("{}%", static_cast<int>(percentDone * 100.0)) + " ";
|
||||
if (m_fPercentage >= 0.0f)
|
||||
oss << " " << std::format("{}%", static_cast<int>(percentDone * 100.0)) << ' ';
|
||||
else
|
||||
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
|
||||
|
||||
// draw message
|
||||
std::print("{} {}", message, m_szCurrentMessage);
|
||||
oss << " " << std::format("{} / {}", m_iSteps, m_iMaxSteps) << ' ';
|
||||
|
||||
std::print("{} {}", oss.str(), m_szCurrentMessage);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
24
meson.build
24
meson.build
@ -31,12 +31,16 @@ if cpp_compiler.check_header('execinfo.h')
|
||||
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
|
||||
endif
|
||||
|
||||
aquamarine = dependency('aquamarine', version: '>=0.4.5')
|
||||
aquamarine = dependency('aquamarine', version: '>=0.8.0')
|
||||
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
|
||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.3')
|
||||
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
||||
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
|
||||
hyprutils = dependency('hyprutils', version: '>= 0.7.0')
|
||||
aquamarine_version_list = aquamarine.version().split('.')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_MINOR=@0@'.format(aquamarine_version_list.get(1))], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_PATCH=@0@'.format(aquamarine_version_list.get(2))], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp')
|
||||
@ -58,6 +62,7 @@ endif
|
||||
|
||||
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
|
||||
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
|
||||
inotify_dep = dependency('libinotify', required: false) # inotify on BSDs
|
||||
|
||||
re2 = dependency('re2', required: true)
|
||||
|
||||
@ -72,19 +77,17 @@ if (systemd_option.enabled())
|
||||
subdir('systemd')
|
||||
endif
|
||||
|
||||
if get_option('legacy_renderer').enabled()
|
||||
add_project_arguments('-DLEGACY_RENDERER', language: 'cpp')
|
||||
endif
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
|
||||
endif
|
||||
|
||||
# Generate hyprland version and populate version.h
|
||||
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
|
||||
# Make shader files includable
|
||||
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
|
||||
|
||||
# Install headers
|
||||
globber = run_command('find', 'src', '-name', '*.h*', 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)
|
||||
@ -101,11 +104,14 @@ endif
|
||||
subdir('protocols')
|
||||
subdir('src')
|
||||
subdir('hyprctl')
|
||||
subdir('hyprpm/src')
|
||||
subdir('assets')
|
||||
subdir('example')
|
||||
subdir('docs')
|
||||
|
||||
if get_option('hyprpm').enabled()
|
||||
subdir('hyprpm/src')
|
||||
endif
|
||||
|
||||
# Generate hyprland.pc
|
||||
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
|
||||
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
|
||||
option('uwsm', type: 'feature', value: 'enabled', description: 'Enable uwsm integration (only if systemd is enabled)')
|
||||
option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')
|
||||
option('hyprpm', type: 'feature', value: 'enabled', description: 'Enable hyprpm')
|
||||
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')
|
||||
|
@ -5,12 +5,15 @@
|
||||
pkg-config,
|
||||
pkgconf,
|
||||
makeWrapper,
|
||||
cmake,
|
||||
meson,
|
||||
ninja,
|
||||
aquamarine,
|
||||
binutils,
|
||||
cairo,
|
||||
epoll-shim,
|
||||
git,
|
||||
glaze,
|
||||
hyprcursor,
|
||||
hyprgraphics,
|
||||
hyprland-protocols,
|
||||
@ -24,7 +27,7 @@
|
||||
libinput,
|
||||
libxkbcommon,
|
||||
libuuid,
|
||||
mesa,
|
||||
libgbm,
|
||||
pango,
|
||||
pciutils,
|
||||
re2,
|
||||
@ -38,7 +41,6 @@
|
||||
xwayland,
|
||||
debug ? false,
|
||||
enableXWayland ? true,
|
||||
legacyRenderer ? false,
|
||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
||||
wrapRuntimeDeps ? true,
|
||||
version ? "git",
|
||||
@ -49,13 +51,14 @@
|
||||
enableNvidiaPatches ? false,
|
||||
nvidiaPatches ? false,
|
||||
hidpiXWayland ? false,
|
||||
legacyRenderer ? false,
|
||||
}: let
|
||||
inherit (builtins) baseNameOf foldl';
|
||||
inherit (builtins) baseNameOf foldl' readFile;
|
||||
inherit (lib.asserts) assertMsg;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.lists) flatten concatLists optional optionals;
|
||||
inherit (lib.sources) cleanSourceWith cleanSource;
|
||||
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable;
|
||||
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable trim;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
@ -67,6 +70,7 @@ in
|
||||
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
||||
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
|
||||
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
||||
customStdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hyprland${optionalString debug "-debug"}";
|
||||
inherit version;
|
||||
@ -91,7 +95,7 @@ in
|
||||
DATE = date;
|
||||
DIRTY = optionalString (commit == "") "dirty";
|
||||
HASH = commit;
|
||||
TAG = "v${builtins.readFile "${finalAttrs.src}/VERSION"}";
|
||||
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
||||
|
||||
depsBuildBuild = [
|
||||
pkg-config
|
||||
@ -102,6 +106,7 @@ in
|
||||
makeWrapper
|
||||
meson
|
||||
ninja
|
||||
cmake # needed for glaze
|
||||
pkg-config
|
||||
];
|
||||
|
||||
@ -116,6 +121,7 @@ in
|
||||
aquamarine
|
||||
cairo
|
||||
git
|
||||
glaze
|
||||
hyprcursor
|
||||
hyprgraphics
|
||||
hyprland-protocols
|
||||
@ -126,7 +132,7 @@ in
|
||||
libinput
|
||||
libuuid
|
||||
libxkbcommon
|
||||
mesa
|
||||
libgbm
|
||||
pango
|
||||
pciutils
|
||||
re2
|
||||
@ -137,6 +143,7 @@ in
|
||||
wayland-scanner
|
||||
xorg.libXcursor
|
||||
]
|
||||
(optionals customStdenv.hostPlatform.isBSD [epoll-shim])
|
||||
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
|
||||
(optionals enableXWayland [
|
||||
xorg.libxcb
|
||||
@ -149,16 +156,19 @@ in
|
||||
(optional withSystemd systemd)
|
||||
];
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
mesonBuildType =
|
||||
if debug
|
||||
then "debugoptimized"
|
||||
then "debug"
|
||||
else "release";
|
||||
|
||||
mesonFlags = flatten [
|
||||
(mapAttrsToList mesonEnable {
|
||||
"xwayland" = enableXWayland;
|
||||
"legacy_renderer" = legacyRenderer;
|
||||
"systemd" = withSystemd;
|
||||
"uwsm" = false;
|
||||
"hyprpm" = false;
|
||||
})
|
||||
(mapAttrsToList mesonBool {
|
||||
"b_pch" = false;
|
||||
|
201
nix/lib.nix
Normal file
201
nix/lib.nix
Normal file
@ -0,0 +1,201 @@
|
||||
lib: let
|
||||
inherit (lib)
|
||||
attrNames
|
||||
filterAttrs
|
||||
foldl
|
||||
generators
|
||||
partition
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
concatMapStrings
|
||||
hasPrefix
|
||||
;
|
||||
|
||||
/**
|
||||
Convert a structured Nix attribute set into Hyprland's configuration format.
|
||||
|
||||
This function takes a nested attribute set and converts it into Hyprland-compatible
|
||||
configuration syntax, supporting top, bottom, and regular command sections.
|
||||
|
||||
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
|
||||
`key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format.
|
||||
|
||||
Configuration:
|
||||
|
||||
* `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `["$"]`).
|
||||
* `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`).
|
||||
|
||||
Attention:
|
||||
|
||||
- The function ensures top commands appear **first** and bottom commands **last**.
|
||||
- The generated configuration is a **single string**, suitable for writing to a config file.
|
||||
- Lists are converted into multiple entries, ensuring compatibility with Hyprland.
|
||||
|
||||
# Inputs
|
||||
|
||||
Structured function argument:
|
||||
|
||||
: topCommandsPrefixes (optional, default: `["$"]`)
|
||||
: A list of prefixes that define **top** commands. Any key starting with one of these
|
||||
prefixes will be placed at the beginning of the configuration.
|
||||
: bottomCommandsPrefixes (optional, default: `[]`)
|
||||
: A list of prefixes that define **bottom** commands. Any key starting with one of these
|
||||
prefixes will be placed at the end of the configuration.
|
||||
|
||||
Value:
|
||||
|
||||
: The attribute set to be converted to Hyprland configuration format.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toHyprlang :: AttrSet -> AttrSet -> String
|
||||
```
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
|
||||
```nix
|
||||
let
|
||||
config = {
|
||||
"$mod" = "SUPER";
|
||||
monitor = {
|
||||
"HDMI-A-1" = "1920x1080@60,0x0,1";
|
||||
};
|
||||
exec = [
|
||||
"waybar"
|
||||
"dunst"
|
||||
];
|
||||
};
|
||||
in lib.toHyprlang {} config
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```nix
|
||||
"$mod = SUPER"
|
||||
"monitor:HDMI-A-1 = 1920x1080@60,0x0,1"
|
||||
"exec = waybar"
|
||||
"exec = dunst"
|
||||
```
|
||||
|
||||
:::
|
||||
*/
|
||||
toHyprlang = {
|
||||
topCommandsPrefixes ? ["$"],
|
||||
bottomCommandsPrefixes ? [],
|
||||
}: attrs: let
|
||||
toHyprlang' = attrs: let
|
||||
# Specially configured `toKeyValue` generator with support for duplicate keys
|
||||
# and a legible key-value separator.
|
||||
mkCommands = generators.toKeyValue {
|
||||
mkKeyValue = generators.mkKeyValueDefault {} " = ";
|
||||
listsAsDuplicateKeys = true;
|
||||
indent = ""; # No indent, since we don't have nesting
|
||||
};
|
||||
|
||||
# Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`.
|
||||
# Uses `flattenAttrs` with a colon separator.
|
||||
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
|
||||
|
||||
# General filtering function to check if a key starts with any prefix in a given list.
|
||||
filterCommands = list: n:
|
||||
foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
||||
|
||||
# Partition keys into top commands and the rest
|
||||
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
||||
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
|
||||
remainingCommands = removeAttrs commands result.right;
|
||||
|
||||
# Partition remaining commands into bottom commands and regular commands
|
||||
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
|
||||
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
|
||||
regularCommands = removeAttrs remainingCommands result2.right;
|
||||
in
|
||||
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
|
||||
concatMapStrings mkCommands [
|
||||
topCommands
|
||||
regularCommands
|
||||
bottomCommands
|
||||
];
|
||||
in
|
||||
toHyprlang' attrs;
|
||||
|
||||
/**
|
||||
Flatten a nested attribute set into a flat attribute set, using a custom key separator function.
|
||||
|
||||
This function recursively traverses a nested attribute set and produces a flat attribute set
|
||||
where keys are joined using a user-defined function (`pred`). It allows transforming deeply
|
||||
nested structures into a single-level attribute set while preserving key-value relationships.
|
||||
|
||||
Configuration:
|
||||
|
||||
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
|
||||
|
||||
# Inputs
|
||||
|
||||
Structured function argument:
|
||||
|
||||
: pred (required)
|
||||
: A function that determines how parent and child keys should be combined into a single key.
|
||||
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
|
||||
|
||||
Value:
|
||||
|
||||
: The nested attribute set to be flattened.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet
|
||||
```
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
|
||||
```nix
|
||||
let
|
||||
nested = {
|
||||
a = "3";
|
||||
b = { c = "4"; d = "5"; };
|
||||
};
|
||||
|
||||
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
|
||||
in lib.flattenAttrs separator nested
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```nix
|
||||
{
|
||||
"a" = "3";
|
||||
"b.c" = "4";
|
||||
"b.d" = "5";
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
*/
|
||||
flattenAttrs = pred: attrs: let
|
||||
flattenAttrs' = prefix: attrs:
|
||||
builtins.foldl' (
|
||||
acc: key: let
|
||||
value = attrs.${key};
|
||||
newKey =
|
||||
if prefix == ""
|
||||
then key
|
||||
else pred prefix key;
|
||||
in
|
||||
acc
|
||||
// (
|
||||
if builtins.isAttrs value
|
||||
then flattenAttrs' newKey value
|
||||
else {"${newKey}" = value;}
|
||||
)
|
||||
) {} (builtins.attrNames attrs);
|
||||
in
|
||||
flattenAttrs' "" attrs;
|
||||
in
|
||||
{
|
||||
inherit flattenAttrs toHyprlang;
|
||||
}
|
148
nix/module.nix
148
nix/module.nix
@ -5,17 +5,149 @@ inputs: {
|
||||
...
|
||||
}: let
|
||||
inherit (pkgs.stdenv.hostPlatform) system;
|
||||
selflib = import ./lib.nix lib;
|
||||
cfg = config.programs.hyprland;
|
||||
|
||||
package = inputs.self.packages.${system}.hyprland;
|
||||
portalPackage = inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
|
||||
hyprland = cfg.finalPackage;
|
||||
};
|
||||
in {
|
||||
config = {
|
||||
options = {
|
||||
programs.hyprland = {
|
||||
package = lib.mkDefault package;
|
||||
portalPackage = lib.mkDefault portalPackage;
|
||||
plugins = lib.mkOption {
|
||||
type = with lib.types; listOf (either package path);
|
||||
default = [];
|
||||
description = ''
|
||||
List of Hyprland plugins to use. Can either be packages or
|
||||
absolute plugin paths.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = with lib.types; let
|
||||
valueType =
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
path
|
||||
(attrsOf valueType)
|
||||
(listOf valueType)
|
||||
])
|
||||
// {
|
||||
description = "Hyprland configuration value";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
default = {};
|
||||
description = ''
|
||||
Hyprland configuration written in Nix. Entries with the same key
|
||||
should be written as lists. Variables' and colors' names should be
|
||||
quoted. See <https://wiki.hyprland.org> for more examples.
|
||||
|
||||
Special categories (e.g `devices`) should be written as
|
||||
`"devices[device-name]"`.
|
||||
|
||||
::: {.note}
|
||||
Use the [](#programs.hyprland.plugins) option to
|
||||
declare plugins.
|
||||
:::
|
||||
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
decoration = {
|
||||
shadow_offset = "0 5";
|
||||
"col.shadow" = "rgba(00000099)";
|
||||
};
|
||||
|
||||
"$mod" = "SUPER";
|
||||
|
||||
bindm = [
|
||||
# mouse movements
|
||||
"$mod, mouse:272, movewindow"
|
||||
"$mod, mouse:273, resizewindow"
|
||||
"$mod ALT, mouse:272, resizewindow"
|
||||
];
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
# window resize
|
||||
bind = $mod, S, submap, resize
|
||||
|
||||
submap = resize
|
||||
binde = , right, resizeactive, 10 0
|
||||
binde = , left, resizeactive, -10 0
|
||||
binde = , up, resizeactive, 0 -10
|
||||
binde = , down, resizeactive, 0 10
|
||||
bind = , escape, submap, reset
|
||||
submap = reset
|
||||
'';
|
||||
description = ''
|
||||
Extra configuration lines to add to `/etc/xdg/hypr/hyprland.conf`.
|
||||
'';
|
||||
};
|
||||
|
||||
topPrefixes = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
default = ["$" "bezier"];
|
||||
example = ["$" "bezier" "source"];
|
||||
description = ''
|
||||
List of prefix of attributes to put at the top of the config.
|
||||
'';
|
||||
};
|
||||
|
||||
bottomPrefixes = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
default = [];
|
||||
example = ["source"];
|
||||
description = ''
|
||||
List of prefix of attributes to put at the bottom of the config.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
programs.hyprland = {
|
||||
package = lib.mkDefault inputs.self.packages.${system}.hyprland;
|
||||
portalPackage = lib.mkDefault inputs.self.packages.${system}.xdg-desktop-portal-hyprland;
|
||||
};
|
||||
}
|
||||
(lib.mkIf cfg.enable {
|
||||
environment.etc."xdg/hypr/hyprland.conf" = let
|
||||
shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != [];
|
||||
|
||||
pluginsToHyprlang = plugins:
|
||||
selflib.toHyprlang {
|
||||
topCommandsPrefixes = cfg.topPrefixes;
|
||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||
}
|
||||
{
|
||||
"exec-once" = let
|
||||
mkEntry = entry:
|
||||
if lib.types.package.check entry
|
||||
then "${entry}/lib/lib${entry.pname}.so"
|
||||
else entry;
|
||||
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
||||
in
|
||||
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
||||
};
|
||||
in
|
||||
lib.mkIf shouldGenerate {
|
||||
text =
|
||||
lib.optionalString (cfg.plugins != [])
|
||||
(pluginsToHyprlang cfg.plugins)
|
||||
+ lib.optionalString (cfg.settings != {})
|
||||
(selflib.toHyprlang {
|
||||
topCommandsPrefixes = cfg.topPrefixes;
|
||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||
}
|
||||
cfg.settings)
|
||||
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ in {
|
||||
inputs.hyprutils.overlays.default
|
||||
inputs.hyprwayland-scanner.overlays.default
|
||||
self.overlays.udis86
|
||||
self.overlays.wayland-protocols
|
||||
|
||||
# Hyprland packages themselves
|
||||
(final: _prev: let
|
||||
@ -49,9 +50,15 @@ in {
|
||||
hyprutils = final.hyprutils.override {debug = true;};
|
||||
debug = true;
|
||||
};
|
||||
hyprland-legacy-renderer = final.hyprland.override {legacyRenderer = true;};
|
||||
|
||||
# deprecated packages
|
||||
hyprland-legacy-renderer =
|
||||
builtins.trace ''
|
||||
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
||||
Legacy renderer is no longer supported.
|
||||
''
|
||||
final.hyprland;
|
||||
|
||||
hyprland-nvidia =
|
||||
builtins.trace ''
|
||||
hyprland-nvidia was removed. Please use the hyprland package.
|
||||
@ -89,4 +96,16 @@ in {
|
||||
patches = [];
|
||||
});
|
||||
};
|
||||
|
||||
# TODO: remove when https://github.com/NixOS/nixpkgs/pull/397497 lands in master
|
||||
wayland-protocols = final: prev: {
|
||||
wayland-protocols = prev.wayland-protocols.overrideAttrs (self: super: {
|
||||
version = "1.43";
|
||||
|
||||
src = final.fetchurl {
|
||||
url = "https://gitlab.freedesktop.org/wayland/${self.pname}/-/releases/${self.version}/downloads/${self.pname}-${self.version}.tar.xz";
|
||||
hash = "sha256-ujw0Jd0nxXtSkek9upe+EkeWAeALyrJNJkcZSMtkNlM=";
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
366
protocols/frog-color-management-v1.xml
Normal file
366
protocols/frog-color-management-v1.xml
Normal file
@ -0,0 +1,366 @@
|
||||
<?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,13 +1,13 @@
|
||||
wayland_protos = dependency(
|
||||
'wayland-protocols',
|
||||
version: '>=1.32',
|
||||
version: '>=1.43',
|
||||
fallback: 'wayland-protocols',
|
||||
default_options: ['tests=false'],
|
||||
)
|
||||
|
||||
hyprland_protos = dependency(
|
||||
'hyprland-protocols',
|
||||
version: '>=0.4',
|
||||
version: '>=0.6.4',
|
||||
fallback: 'hyprland-protocols',
|
||||
)
|
||||
|
||||
@ -33,10 +33,15 @@ protocols = [
|
||||
'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',
|
||||
@ -66,6 +71,10 @@ protocols = [
|
||||
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',
|
||||
]
|
||||
|
||||
wl_protocols = []
|
||||
@ -81,7 +90,7 @@ foreach protocol : protocols
|
||||
endforeach
|
||||
|
||||
# wayland.xml generation
|
||||
wayland_scanner = dependency('wayland-scanner')
|
||||
wayland_scanner = dependency('wayland-scanner', native: true)
|
||||
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
|
||||
|
||||
wayland_xml = wayland_scanner_datadir / 'wayland.xml'
|
||||
|
1457
protocols/xx-color-management-v4.xml
Normal file
1457
protocols/xx-color-management-v4.xml
Normal file
File diff suppressed because it is too large
Load Diff
24
scripts/generateShaderIncludes.sh
Executable file
24
scripts/generateShaderIncludes.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
SHADERS_SRC="./src/render/shaders/glsl"
|
||||
|
||||
echo "-- Generating shader includes"
|
||||
|
||||
if [ ! -d ./src/render/shaders ]; then
|
||||
mkdir ./src/render/shaders
|
||||
fi
|
||||
|
||||
echo '#pragma once' > ./src/render/shaders/Shaders.hpp
|
||||
echo '#include <map>' >> ./src/render/shaders/Shaders.hpp
|
||||
echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/render/shaders/Shaders.hpp
|
||||
|
||||
for filename in `ls ${SHADERS_SRC}`; do
|
||||
echo "-- ${filename}"
|
||||
|
||||
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
||||
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
||||
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
||||
echo "}," >> ./src/render/shaders/Shaders.hpp
|
||||
done
|
||||
|
||||
echo '};' >> ./src/render/shaders/Shaders.hpp
|
@ -1,4 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
# if the git directory doesn't exist, don't gather data to avoid overwriting, unless
|
||||
# the version file is missing altogether (otherwise compiling will fail)
|
||||
if [ ! -d ./.git ]; then
|
||||
if [ -f ./src/version.h ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
cp -fr ./src/version.h.in ./src/version.h
|
||||
|
||||
HASH=${HASH-$(git rev-parse HEAD)}
|
||||
|
1667
src/Compositor.cpp
1667
src/Compositor.cpp
File diff suppressed because it is too large
Load Diff
@ -1,38 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "defines.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "events/Events.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "managers/ThreadManager.hpp"
|
||||
#include "managers/XWaylandManager.hpp"
|
||||
#include "managers/input/InputManager.hpp"
|
||||
#include "managers/LayoutManager.hpp"
|
||||
#include "managers/KeybindManager.hpp"
|
||||
#include "managers/AnimationManager.hpp"
|
||||
#include "managers/EventManager.hpp"
|
||||
#include "managers/ProtocolManager.hpp"
|
||||
#include "managers/SessionLockManager.hpp"
|
||||
#include "managers/HookSystemManager.hpp"
|
||||
#include "debug/HyprDebugOverlay.hpp"
|
||||
#include "debug/HyprNotificationOverlay.hpp"
|
||||
#include "helpers/Monitor.hpp"
|
||||
#include "desktop/Workspace.hpp"
|
||||
#include "desktop/Window.hpp"
|
||||
#include "render/Renderer.hpp"
|
||||
#include "render/OpenGL.hpp"
|
||||
#include "hyprerror/HyprError.hpp"
|
||||
#include "plugins/PluginSystem.hpp"
|
||||
#include "helpers/Watchdog.hpp"
|
||||
#include "protocols/types/ColorManagement.hpp"
|
||||
|
||||
#include <aquamarine/backend/Backend.hpp>
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
|
||||
class CWLSurfaceResource;
|
||||
struct SWorkspaceRule;
|
||||
|
||||
enum eManagersInitStage : uint8_t {
|
||||
STAGE_PRIORITY = 0,
|
||||
@ -42,57 +22,56 @@ enum eManagersInitStage : uint8_t {
|
||||
|
||||
class CCompositor {
|
||||
public:
|
||||
CCompositor();
|
||||
CCompositor(bool onlyConfig = false);
|
||||
~CCompositor();
|
||||
|
||||
wl_display* m_sWLDisplay;
|
||||
wl_event_loop* m_sWLEventLoop;
|
||||
int m_iDRMFD = -1;
|
||||
bool m_bInitialized = false;
|
||||
SP<Aquamarine::CBackend> m_pAqBackend;
|
||||
wl_display* m_wlDisplay = nullptr;
|
||||
wl_event_loop* m_wlEventLoop = nullptr;
|
||||
int m_drmFD = -1;
|
||||
bool m_initialized = false;
|
||||
SP<Aquamarine::CBackend> m_aqBackend;
|
||||
|
||||
std::string m_szHyprTempDataRoot = "";
|
||||
std::string m_hyprTempDataRoot = "";
|
||||
|
||||
std::string m_szWLDisplaySocket = "";
|
||||
std::string m_szInstanceSignature = "";
|
||||
std::string m_szInstancePath = "";
|
||||
std::string m_szCurrentSplash = "error";
|
||||
std::string m_wlDisplaySocket = "";
|
||||
std::string m_instanceSignature = "";
|
||||
std::string m_instancePath = "";
|
||||
std::string m_currentSplash = "error";
|
||||
|
||||
std::vector<PHLMONITOR> m_vMonitors;
|
||||
std::vector<PHLMONITOR> m_vRealMonitors; // for all monitors, even those turned off
|
||||
std::vector<PHLWINDOW> m_vWindows;
|
||||
std::vector<PHLLS> m_vLayers;
|
||||
std::vector<PHLWORKSPACE> m_vWorkspaces;
|
||||
std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
|
||||
std::vector<PHLLSREF> m_vSurfacesFadingOut;
|
||||
std::vector<PHLMONITOR> m_monitors;
|
||||
std::vector<PHLMONITOR> m_realMonitors; // for all monitors, even those turned off
|
||||
std::vector<PHLWINDOW> m_windows;
|
||||
std::vector<PHLLS> m_layers;
|
||||
std::vector<PHLWORKSPACE> m_workspaces;
|
||||
std::vector<PHLWINDOWREF> m_windowsFadingOut;
|
||||
std::vector<PHLLSREF> m_surfacesFadingOut;
|
||||
|
||||
std::unordered_map<std::string, MONITORID> m_mMonitorIDMap;
|
||||
std::unordered_map<std::string, MONITORID> m_monitorIDMap;
|
||||
std::unordered_map<std::string, WORKSPACEID> m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs
|
||||
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
void cleanup();
|
||||
void createLockFile();
|
||||
void removeLockFile();
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
void cleanup();
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
|
||||
WP<CWLSurfaceResource> m_pLastFocus;
|
||||
PHLWINDOWREF m_pLastWindow;
|
||||
PHLMONITORREF m_pLastMonitor;
|
||||
WP<CWLSurfaceResource> m_lastFocus;
|
||||
PHLWINDOWREF m_lastWindow;
|
||||
PHLMONITORREF m_lastMonitor;
|
||||
|
||||
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
|
||||
std::vector<PHLWINDOWREF> m_windowFocusHistory; // first element is the most recently focused
|
||||
|
||||
bool m_bReadyToProcess = false;
|
||||
bool m_bSessionActive = true;
|
||||
bool m_bDPMSStateON = true;
|
||||
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
|
||||
bool m_bNextIsUnsafe = false;
|
||||
PHLMONITORREF m_pUnsafeOutput; // fallback output for the unsafe state
|
||||
bool m_bIsShuttingDown = false;
|
||||
bool m_bFinalRequests = false;
|
||||
bool m_bDesktopEnvSet = false;
|
||||
bool m_bEnableXwayland = true;
|
||||
bool m_readyToProcess = false;
|
||||
bool m_sessionActive = true;
|
||||
bool m_dpmsStateOn = true;
|
||||
bool m_unsafeState = false; // unsafe state is when there is no monitors
|
||||
PHLMONITORREF m_unsafeOutput; // fallback output for the unsafe state
|
||||
bool m_isShuttingDown = false;
|
||||
bool m_finalRequests = false;
|
||||
bool m_desktopEnvSet = false;
|
||||
bool m_wantsXwayland = true;
|
||||
bool m_onlyConfigVerification = false;
|
||||
|
||||
// ------------------------------------------------- //
|
||||
|
||||
@ -102,11 +81,11 @@ class CCompositor {
|
||||
PHLMONITOR getMonitorFromCursor();
|
||||
PHLMONITOR getMonitorFromVector(const Vector2D&);
|
||||
void removeWindowFromVectorSafe(PHLWINDOW);
|
||||
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr);
|
||||
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr, bool preserveFocusHistory = false);
|
||||
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
|
||||
bool monitorExists(PHLMONITOR);
|
||||
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
|
||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*);
|
||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
|
||||
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, PHLMONITOR monitor, Vector2D*, PHLLS*);
|
||||
SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
|
||||
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
|
||||
@ -123,8 +102,9 @@ class CCompositor {
|
||||
void changeWindowZOrder(PHLWINDOW, bool);
|
||||
void cleanupFadingOut(const MONITORID& monid);
|
||||
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
|
||||
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
|
||||
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
|
||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char 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 getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false);
|
||||
WORKSPACEID getNextAvailableNamedWorkspace();
|
||||
bool isPointOnAnyMonitor(const Vector2D&);
|
||||
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
||||
@ -140,7 +120,6 @@ class CCompositor {
|
||||
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state);
|
||||
void changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
|
||||
PHLWINDOW getX11Parent(PHLWINDOW);
|
||||
@ -167,10 +146,13 @@ class CCompositor {
|
||||
void setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, double scale);
|
||||
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
|
||||
void updateSuspendedStates();
|
||||
PHLWINDOW windowForCPointer(CWindow*);
|
||||
void onNewMonitor(SP<Aquamarine::IOutput> output);
|
||||
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
|
||||
|
||||
std::string explicitConfigPath;
|
||||
NColorManagement::SImageDescription getPreferredImageDescription();
|
||||
bool shouldChangePreferredImageDescription();
|
||||
|
||||
std::string m_explicitConfigPath;
|
||||
|
||||
private:
|
||||
void initAllSignals();
|
||||
@ -179,10 +161,13 @@ class CCompositor {
|
||||
void setRandomSplash();
|
||||
void initManagers(eManagersInitStage stage);
|
||||
void prepareFallbackOutput();
|
||||
void createLockFile();
|
||||
void removeLockFile();
|
||||
void setMallocThreshold();
|
||||
|
||||
uint64_t m_iHyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_sOriginalNofile = {0};
|
||||
uint64_t m_hyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_originalNofile = {};
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CCompositor> g_pCompositor;
|
||||
inline UP<CCompositor> g_pCompositor;
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "helpers/math/Math.hpp"
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <hyprutils/math/Box.hpp>
|
||||
|
||||
enum eIcons : uint8_t {
|
||||
@ -50,6 +52,12 @@ struct SHyprCtlCommand {
|
||||
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
|
||||
};
|
||||
|
||||
struct SDispatchResult {
|
||||
bool passEvent = false;
|
||||
bool success = true;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
typedef int64_t WINDOWID;
|
||||
typedef int64_t MONITORID;
|
||||
typedef int64_t WORKSPACEID;
|
||||
|
@ -2,16 +2,18 @@
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/varlist/VarList.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
enum eConfigValueDataTypes : int8_t {
|
||||
CVD_TYPE_INVALID = -1,
|
||||
CVD_TYPE_GRADIENT = 0,
|
||||
CVD_TYPE_CSS_VALUE = 1
|
||||
CVD_TYPE_INVALID = -1,
|
||||
CVD_TYPE_GRADIENT = 0,
|
||||
CVD_TYPE_CSS_VALUE = 1,
|
||||
CVD_TYPE_FONT_WEIGHT = 2,
|
||||
};
|
||||
|
||||
class ICustomConfigValueData {
|
||||
public:
|
||||
virtual ~ICustomConfigValueData() = 0;
|
||||
virtual ~ICustomConfigValueData() = default;
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() = 0;
|
||||
|
||||
@ -22,7 +24,7 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CGradientValueData() = default;
|
||||
CGradientValueData(CHyprColor col) {
|
||||
m_vColors.push_back(col);
|
||||
m_colors.push_back(col);
|
||||
updateColorsOk();
|
||||
};
|
||||
virtual ~CGradientValueData() = default;
|
||||
@ -32,39 +34,39 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
void reset(CHyprColor col) {
|
||||
m_vColors.clear();
|
||||
m_vColors.emplace_back(col);
|
||||
m_fAngle = 0;
|
||||
m_colors.clear();
|
||||
m_colors.emplace_back(col);
|
||||
m_angle = 0;
|
||||
updateColorsOk();
|
||||
}
|
||||
|
||||
void updateColorsOk() {
|
||||
m_vColorsOkLabA.clear();
|
||||
for (auto& c : m_vColors) {
|
||||
m_colorsOkLabA.clear();
|
||||
for (auto& c : m_colors) {
|
||||
const auto OKLAB = c.asOkLab();
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.l);
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.a);
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.b);
|
||||
m_vColorsOkLabA.emplace_back(c.a);
|
||||
m_colorsOkLabA.emplace_back(OKLAB.l);
|
||||
m_colorsOkLabA.emplace_back(OKLAB.a);
|
||||
m_colorsOkLabA.emplace_back(OKLAB.b);
|
||||
m_colorsOkLabA.emplace_back(c.a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Vector containing the colors */
|
||||
std::vector<CHyprColor> m_vColors;
|
||||
std::vector<CHyprColor> m_colors;
|
||||
|
||||
/* Vector containing pure colors for shoving into opengl */
|
||||
std::vector<float> m_vColorsOkLabA;
|
||||
std::vector<float> m_colorsOkLabA;
|
||||
|
||||
/* Float corresponding to the angle (rad) */
|
||||
float m_fAngle = 0;
|
||||
float m_angle = 0;
|
||||
|
||||
//
|
||||
bool operator==(const CGradientValueData& other) const {
|
||||
if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle)
|
||||
if (other.m_colors.size() != m_colors.size() || m_angle != other.m_angle)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m_vColors.size(); ++i)
|
||||
if (m_vColors[i] != other.m_vColors[i])
|
||||
for (size_t i = 0; i < m_colors.size(); ++i)
|
||||
if (m_colors[i] != other.m_colors[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -72,28 +74,28 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
|
||||
virtual std::string toString() {
|
||||
std::string result;
|
||||
for (auto& c : m_vColors) {
|
||||
for (auto& c : m_colors) {
|
||||
result += std::format("{:x} ", c.getAsHex());
|
||||
}
|
||||
|
||||
result += std::format("{}deg", (int)(m_fAngle * 180.0 / M_PI));
|
||||
result += std::format("{}deg", (int)(m_angle * 180.0 / M_PI));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class CCssGapData : public ICustomConfigValueData {
|
||||
public:
|
||||
CCssGapData() : top(0), right(0), bottom(0), left(0) {};
|
||||
CCssGapData(int64_t global) : top(global), right(global), bottom(global), left(global) {};
|
||||
CCssGapData(int64_t vertical, int64_t horizontal) : top(vertical), right(horizontal), bottom(vertical), left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t horizontal, int64_t bottom) : top(top), right(horizontal), bottom(bottom), left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t right, int64_t bottom, int64_t left) : top(top), right(right), bottom(bottom), left(left) {};
|
||||
CCssGapData() : m_top(0), m_right(0), m_bottom(0), m_left(0) {};
|
||||
CCssGapData(int64_t global) : m_top(global), m_right(global), m_bottom(global), m_left(global) {};
|
||||
CCssGapData(int64_t vertical, int64_t horizontal) : m_top(vertical), m_right(horizontal), m_bottom(vertical), m_left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t horizontal, int64_t bottom) : m_top(top), m_right(horizontal), m_bottom(bottom), m_left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t right, int64_t bottom, int64_t left) : m_top(top), m_right(right), m_bottom(bottom), m_left(left) {};
|
||||
|
||||
/* Css like directions */
|
||||
int64_t top;
|
||||
int64_t right;
|
||||
int64_t bottom;
|
||||
int64_t left;
|
||||
int64_t m_top;
|
||||
int64_t m_right;
|
||||
int64_t m_bottom;
|
||||
int64_t m_left;
|
||||
|
||||
void parseGapData(CVarList varlist) {
|
||||
switch (varlist.size()) {
|
||||
@ -122,10 +124,10 @@ class CCssGapData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
void reset(int64_t global) {
|
||||
top = global;
|
||||
right = global;
|
||||
bottom = global;
|
||||
left = global;
|
||||
m_top = global;
|
||||
m_right = global;
|
||||
m_bottom = global;
|
||||
m_left = global;
|
||||
}
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
@ -133,6 +135,44 @@ class CCssGapData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
virtual std::string toString() {
|
||||
return std::format("{} {} {} {}", top, right, bottom, left);
|
||||
return std::format("{} {} {} {}", m_top, m_right, m_bottom, m_left);
|
||||
}
|
||||
};
|
||||
|
||||
class CFontWeightConfigValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CFontWeightConfigValueData() = default;
|
||||
CFontWeightConfigValueData(const char* weight) {
|
||||
parseWeight(weight);
|
||||
}
|
||||
|
||||
int64_t m_value = 400; // default to normal weight
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
return CVD_TYPE_FONT_WEIGHT;
|
||||
}
|
||||
|
||||
virtual std::string toString() {
|
||||
return std::format("{}", m_value);
|
||||
}
|
||||
|
||||
void parseWeight(const std::string& strWeight) {
|
||||
auto lcWeight{strWeight};
|
||||
transform(strWeight.begin(), strWeight.end(), lcWeight.begin(), ::tolower);
|
||||
|
||||
// values taken from Pango weight enums
|
||||
const auto WEIGHTS = std::map<std::string, int>{
|
||||
{"thin", 100}, {"ultralight", 200}, {"light", 300}, {"semilight", 350}, {"book", 380}, {"normal", 400},
|
||||
{"medium", 500}, {"semibold", 600}, {"bold", 700}, {"ultrabold", 800}, {"heavy", 900}, {"ultraheavy", 1000},
|
||||
};
|
||||
|
||||
auto weight = WEIGHTS.find(lcWeight);
|
||||
if (weight != WEIGHTS.end())
|
||||
m_value = weight->second;
|
||||
else {
|
||||
int w_i = std::stoi(strWeight);
|
||||
if (w_i < 100 || w_i > 1000)
|
||||
m_value = 400;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -139,6 +139,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "decoration:rounding_power",
|
||||
.description = "rouding power of corners (2 is a circle)",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{2, 2, 10},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "decoration:active_opacity",
|
||||
.description = "opacity of active windows. [0.0 - 1.0]",
|
||||
@ -241,104 +247,110 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_STRING_LONG,
|
||||
.data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "decoration:border_part_of_window",
|
||||
.description = "whether the border should be treated as a part of the window.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
|
||||
/*
|
||||
* blur:
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:enabled",
|
||||
.value = "decoration:blur:enabled",
|
||||
.description = "enable kawase window background blur",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:size",
|
||||
.value = "decoration:blur:size",
|
||||
.description = "blur size (distance)",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{8, 0, 100},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:passes",
|
||||
.value = "decoration:blur:passes",
|
||||
.description = "the amount of passes to perform",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 0, 10},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:ignore_opacity",
|
||||
.value = "decoration:blur:ignore_opacity",
|
||||
.description = "make the blur layer ignore the opacity of the window",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:new_optimizations",
|
||||
.value = "decoration:blur:new_optimizations",
|
||||
.description = "whether to enable further optimizations to the blur. Recommended to leave on, as it will massively improve performance.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:xray",
|
||||
.value = "decoration:blur:xray",
|
||||
.description = "if enabled, floating windows will ignore tiled windows in their blur. Only available if blur_new_optimizations is true. Will reduce overhead on floating "
|
||||
"blur significantly.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:noise",
|
||||
.value = "decoration:blur:noise",
|
||||
.description = "how much noise to apply. [0.0 - 1.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.0117, 0, 1},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:contrast",
|
||||
.value = "decoration:blur:contrast",
|
||||
.description = "contrast modulation for blur. [0.0 - 2.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.8916, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:brightness",
|
||||
.value = "decoration:blur:brightness",
|
||||
.description = "brightness modulation for blur. [0.0 - 2.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.8172, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:vibrancy",
|
||||
.value = "decoration:blur:vibrancy",
|
||||
.description = "Increase saturation of blurred colors. [0.0 - 1.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.1696, 0, 1},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:vibrancy_darkness",
|
||||
.value = "decoration:blur:vibrancy_darkness",
|
||||
.description = "How strong the effect of vibrancy is on dark areas . [0.0 - 1.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0, 0, 1},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:special",
|
||||
.value = "decoration:blur:special",
|
||||
.description = "whether to blur behind the special workspace (note: expensive)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:popups",
|
||||
.value = "decoration:blur:popups",
|
||||
.description = "whether to blur popups (e.g. right-click menus)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:popups_ignorealpha",
|
||||
.value = "decoration:blur:popups_ignorealpha",
|
||||
.description = "works like ignorealpha in layer rules. If pixel opacity is below set value, will not blur. [0.0 - 1.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:input_methods",
|
||||
.value = "decoration:blur:input_methods",
|
||||
.description = "whether to blur input methods (e.g. fcitx5)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "blur:input_methods_ignorealpha",
|
||||
.value = "decoration:blur:input_methods_ignorealpha",
|
||||
.description = "works like ignorealpha in layer rules. If pixel opacity is below set value, will not blur. [0.0 - 1.0]",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
|
||||
@ -360,6 +372,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "animations:workspace_wraparound",
|
||||
.description = "changes the direction of slide animations between the first and last workspaces",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
|
||||
/*
|
||||
* input:
|
||||
@ -495,6 +513,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 0, 3},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:follow_mouse_threshold",
|
||||
.description = "The smallest distance in logical pixels the mouse needs to travel for the window under it to get focused. Works only with follow_mouse = 1.",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:focus_on_close",
|
||||
.description = "Controls the window focus behavior when a window is closed. When set to 0, focus will shift to the next window candidate. When set to 1, focus will shift "
|
||||
@ -596,6 +620,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:touchpad:flip_x",
|
||||
.description = "Inverts the horizontal movement of the touchpad",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:touchpad:flip_y",
|
||||
.description = "Inverts the vertical movement of the touchpad",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* input:touchdevice:
|
||||
@ -803,25 +839,25 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:col.border_active",
|
||||
.value = "group:col.border_active",
|
||||
.description = "border color for inactive windows",
|
||||
.type = CONFIG_OPTION_GRADIENT,
|
||||
.data = SConfigOptionDescription::SGradientData{"0x66ffff00"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:col.border_inactive",
|
||||
.value = "group:col.border_inactive",
|
||||
.description = "border color for the active window",
|
||||
.type = CONFIG_OPTION_GRADIENT,
|
||||
.data = SConfigOptionDescription::SGradientData{"0x66777700"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:col.border_locked_active",
|
||||
.value = "group:col.border_locked_inactive",
|
||||
.description = "inactive border color for window that cannot be added to a group (see denywindowfromgroup dispatcher)",
|
||||
.type = CONFIG_OPTION_GRADIENT,
|
||||
.data = SConfigOptionDescription::SGradientData{"0x66ff5500"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:col.border_locked_inactive",
|
||||
.value = "group:col.border_locked_active",
|
||||
.description = "active border color for window that cannot be added to a group",
|
||||
.type = CONFIG_OPTION_GRADIENT,
|
||||
.data = SConfigOptionDescription::SGradientData{"0x66775500"},
|
||||
@ -867,6 +903,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{STRVAL_EMPTY}, //##TODO UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_weight_active",
|
||||
.description = "weight of the font used to display active groupbar titles",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"normal"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_weight_inactive",
|
||||
.description = "weight of the font used to display inactive groupbar titles",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"normal"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_size",
|
||||
.description = "font size of groupbar title",
|
||||
@ -877,7 +925,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.value = "group:groupbar:gradients",
|
||||
.description = "enables gradients",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:height",
|
||||
@ -885,6 +933,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{14, 1, 64},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:indicator_gap",
|
||||
.description = "height of the gap between the groupbar indicator and title",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 64},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:indicator_height",
|
||||
.description = "height of the groupbar indicator",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{3, 1, 64},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:stacked",
|
||||
.description = "render the groupbar as a vertical stack",
|
||||
@ -909,6 +969,30 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:rounding",
|
||||
.description = "how much to round the groupbar",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:gradient_rounding",
|
||||
.description = "how much to round the groupbar gradient",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:round_only_edges",
|
||||
.description = "if yes, will only round at the groupbar edges",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:gradient_round_only_edges",
|
||||
.description = "if yes, will only round at the groupbar gradient edges",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_color",
|
||||
.description = "controls the group bar text color",
|
||||
@ -939,6 +1023,30 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_COLOR,
|
||||
.data = SConfigOptionDescription::SColorData{0x66775500},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:gaps_out",
|
||||
.description = "gap between gradients and window",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:gaps_in",
|
||||
.description = "gap between gradients",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:keep_upper_gap",
|
||||
.description = "keep an upper gap above gradient",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_offset",
|
||||
.description = "set an offset for a text",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SRangeData{0, -20, 20},
|
||||
},
|
||||
|
||||
/*
|
||||
* misc:
|
||||
@ -988,9 +1096,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:vrr",
|
||||
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only [0/1/2]",
|
||||
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only, 3 - fullscreen with game or video content type [0/1/2/3]",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 3},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:mouse_move_enables_dpms",
|
||||
@ -1145,6 +1253,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1000, 0, 5000},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:enable_anr_dialog",
|
||||
.description = "whether to enable the ANR (app not responding) dialog when your apps hang",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:anr_missed_pings",
|
||||
.description = "number of missed pings before showing the ANR dialog",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 1, 10},
|
||||
},
|
||||
|
||||
/*
|
||||
* binds:
|
||||
@ -1168,6 +1288,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:hide_special_on_workspace_change",
|
||||
.description = "If enabled, changing the active workspace (including to itself) will hide the special workspace on the monitor where the newly active workspace resides.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:allow_workspace_cycles",
|
||||
.description = "If enabled, workspaces don’t forget their previous workspace, so cycles can be created by switching to the first workspace in a sequence, then endlessly "
|
||||
@ -1198,7 +1324,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.value = "binds:movefocus_cycles_fullscreen",
|
||||
.description = "If enabled, when on a fullscreen window, movefocus will cycle fullscreen, if not, it will move the focus in a direction.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:movefocus_cycles_groupfirst",
|
||||
@ -1224,6 +1350,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:drag_threshold",
|
||||
.description = "Movement threshold in pixels for window dragging and c/g bind flags. 0 to disable and grab on mousedown.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, INT_MAX},
|
||||
},
|
||||
|
||||
/*
|
||||
* xwayland:
|
||||
@ -1247,6 +1379,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "xwayland:create_abstract_socket",
|
||||
.description = "Create the abstract Unix domain socket for XWayland",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* opengl:
|
||||
@ -1258,13 +1396,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "opengl:force_introspection",
|
||||
.description = "forces introspection at all times. Introspection is aimed at reducing GPU usage in certain cases, but might cause graphical glitches on nvidia. 0 - "
|
||||
"nothing, 1 - force always on, 2 - force always on if nvidia",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
},
|
||||
|
||||
/*
|
||||
* render:
|
||||
@ -1285,33 +1416,63 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
SConfigOptionDescription{
|
||||
.value = "render:direct_scanout",
|
||||
.description = "Enables direct scanout. Direct scanout attempts to reduce lag when there is only one fullscreen application on a screen (e.g. game). It is also "
|
||||
"recommended to set this to false if the fullscreen application shows graphical glitches.",
|
||||
"recommended to set this to false if the fullscreen application shows graphical glitches. 0 - off, 1 - on, 2 - auto (on with content type 'game')",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:expand_undersized_textures",
|
||||
.description = "Whether to expand textures that have not yet resized to be larger, or to just stretch them instead.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:xp_mode",
|
||||
.description = "Disable back buffer and bottom layer rendering.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:ctm_animation",
|
||||
.description = "Whether to enable a fade animation for CTM changes (hyprsunset). 2 means 'auto' (Yes on everything but Nvidia).",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_fs_passthrough",
|
||||
.description = "Passthrough color settings for fullscreen apps when possible",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_enabled",
|
||||
.description = "Enable Color Management pipelines (requires restart to fully take effect)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:send_content_type",
|
||||
.description = "Report content type to allow monitor profile autoswitch (may result in a black screen during the switch)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
|
||||
/*
|
||||
* cursor:
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:use_nearest_neighbor",
|
||||
.description = "sync xcursor theme with gsettings, it applies cursor-theme and cursor-size on theme load to gsettings making most CSD gtk based clients use same xcursor "
|
||||
"theme and size.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:no_hardware_cursors",
|
||||
.description = "disables hardware cursors",
|
||||
.description = "disables hardware cursors. Auto = disable when tearing",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Auto"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:no_break_fs_vrr",
|
||||
.description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (requires no_hardware_cursors = true)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
.description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (may require no_hardware_cursors = true) "
|
||||
"0 - off, 1 - on, 2 - auto (on with content type 'game')",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:min_refresh_rate",
|
||||
@ -1349,6 +1510,13 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:warp_on_toggle_special",
|
||||
.description = "Move the cursor to the last focused window when toggling a special workspace. Options: 0 (Disabled), 1 (Enabled), "
|
||||
"2 (Force - ignores cursor:no_warps option)",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:default_monitor",
|
||||
.description = "the name of a default monitor for the cursor to be set to on startup (see hyprctl monitors for names)",
|
||||
@ -1391,6 +1559,35 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:sync_gsettings_theme",
|
||||
.description = "sync xcursor theme with gsettings, it applies cursor-theme and cursor-size on theme load to gsettings making most CSD gtk based clients use same xcursor "
|
||||
"theme and size.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:warp_back_after_non_mouse_input",
|
||||
.description = "warp the cursor back to where it was after using a non-mouse input to move it, and then returning back to mouse.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* ecosystem:
|
||||
*/
|
||||
SConfigOptionDescription{
|
||||
.value = "ecosystem:no_update_news",
|
||||
.description = "disable the popup that shows up when you update hyprland to a new version.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "ecosystem:no_donation_nag",
|
||||
.description = "disable the popup that shows up twice a year encouraging to donate.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* debug:
|
||||
@ -1444,12 +1641,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:watchdog_timeout",
|
||||
.description = "sets the timeout in seconds for watchdog to abort processing of a signal of the main thread. Set to 0 to disable.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{5, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:disable_scale_checks",
|
||||
.description = "disables verification of the scale factors. Will result in pixel alignment and rounding errors.",
|
||||
@ -1474,6 +1665,24 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:log_damage",
|
||||
.description = "enables logging the damage.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:pass",
|
||||
.description = "enables render pass debugging.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:full_cm_proto",
|
||||
.description = "claims support for all cm proto features (requires restart)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* dwindle:
|
||||
@ -1603,11 +1812,15 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:always_center_master",
|
||||
.description = "when using orientation=center, keep the master window centered, even when it is the only window in the workspace.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
.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)",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 10}, //##TODO RANGE?
|
||||
},
|
||||
SConfigOptionDescription{.value = "master:center_master_fallback",
|
||||
.description = "Set fallback for center master when slaves are less than slave_count_for_center_master, can be left ,right ,top ,bottom",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"left"}},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:center_ignores_reserved",
|
||||
.description = "centers the master window on monitor ignoring reserved areas",
|
||||
@ -1628,4 +1841,16 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "experimental:xx_color_management_v4",
|
||||
.description = "enable color management protocol",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:always_keep_position",
|
||||
.description = "whether to keep the master window in its configured position when there are no slave windows",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/animation/AnimationConfig.hpp>
|
||||
#define CONFIG_MANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include "../debug/Log.hpp"
|
||||
#include <unordered_map>
|
||||
#include "../defines.hpp"
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <xf86drmMode.h>
|
||||
#include "../helpers/WLClasses.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../helpers/varlist/VarList.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../desktop/LayerRule.hpp"
|
||||
|
||||
#include "defaultConfig.hpp"
|
||||
#include "ConfigDataValues.hpp"
|
||||
#include "../SharedDefs.hpp"
|
||||
#include "../helpers/Color.hpp"
|
||||
#include "../desktop/DesktopTypes.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "../desktop/WindowRule.hpp"
|
||||
#include "../managers/XWaylandManager.hpp"
|
||||
|
||||
#include <hyprlang.hpp>
|
||||
|
||||
#define INITANIMCFG(name) animationConfig[name] = {}
|
||||
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]}
|
||||
|
||||
#define HANDLE void*
|
||||
|
||||
struct SWorkspaceRule {
|
||||
@ -54,18 +53,6 @@ struct SMonitorAdditionalReservedArea {
|
||||
int right = 0;
|
||||
};
|
||||
|
||||
struct SAnimationPropertyConfig {
|
||||
bool overridden = true;
|
||||
|
||||
std::string internalBezier = "";
|
||||
std::string internalStyle = "";
|
||||
float internalSpeed = 0.f;
|
||||
int internalEnabled = -1;
|
||||
|
||||
SAnimationPropertyConfig* pValues = nullptr;
|
||||
SAnimationPropertyConfig* pParentAnimation = nullptr;
|
||||
};
|
||||
|
||||
struct SPluginKeyword {
|
||||
HANDLE handle = nullptr;
|
||||
std::string name = "";
|
||||
@ -146,12 +133,49 @@ struct SConfigOptionDescription {
|
||||
std::variant<SBoolData, SRangeData, SFloatData, SStringData, SColorData, SChoiceData, SGradientData, SVectorData> data;
|
||||
};
|
||||
|
||||
struct SFirstExecRequest {
|
||||
std::string exec = "";
|
||||
bool withRules = false;
|
||||
};
|
||||
|
||||
struct SFloatCache {
|
||||
size_t hash;
|
||||
|
||||
SFloatCache(PHLWINDOW window, bool initial) {
|
||||
// Base hash from class/title
|
||||
size_t baseHash = initial ? (std::hash<std::string>{}(window->m_initialClass) ^ (std::hash<std::string>{}(window->m_initialTitle) << 1)) :
|
||||
(std::hash<std::string>{}(window->m_class) ^ (std::hash<std::string>{}(window->m_title) << 1));
|
||||
|
||||
// Use empty string as default tag value
|
||||
std::string tagValue = "";
|
||||
if (auto xdgTag = window->xdgTag())
|
||||
tagValue = xdgTag.value();
|
||||
|
||||
// Combine hashes
|
||||
hash = baseHash ^ (std::hash<std::string>{}(tagValue) << 2);
|
||||
}
|
||||
|
||||
bool operator==(const SFloatCache& other) const {
|
||||
return hash == other.hash;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<SFloatCache> {
|
||||
size_t operator()(const SFloatCache& id) const {
|
||||
return id.hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CConfigManager {
|
||||
public:
|
||||
CConfigManager();
|
||||
|
||||
void tick();
|
||||
void init();
|
||||
void reload();
|
||||
std::string verify();
|
||||
|
||||
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
@ -163,8 +187,7 @@ class CConfigManager {
|
||||
|
||||
void* const* getConfigValuePtr(const std::string&);
|
||||
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
|
||||
void onPluginLoadUnload(const std::string& name, bool load);
|
||||
static std::string getMainConfigPath();
|
||||
std::string getMainConfigPath();
|
||||
std::string getConfigString();
|
||||
|
||||
SMonitorRule getMonitorRuleFor(const PHLMONITOR);
|
||||
@ -177,141 +200,124 @@ class CConfigManager {
|
||||
|
||||
std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
|
||||
std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
|
||||
void ensurePersistentWorkspacesPresent();
|
||||
|
||||
const std::vector<SConfigOptionDescription>& getAllDescriptions();
|
||||
|
||||
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
|
||||
|
||||
std::unordered_map<std::string, 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);
|
||||
void addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fun, Hyprlang::SHandlerOptions opts = {});
|
||||
void removePluginConfig(HANDLE handle);
|
||||
|
||||
// no-op when done.
|
||||
void dispatchExecOnce();
|
||||
void dispatchExecShutdown();
|
||||
void dispatchExecOnce();
|
||||
void dispatchExecShutdown();
|
||||
|
||||
void performMonitorReload();
|
||||
void appendMonitorRule(const SMonitorRule&);
|
||||
bool replaceMonitorRule(const SMonitorRule&);
|
||||
void ensureMonitorStatus();
|
||||
void ensureVRR(PHLMONITOR pMonitor = nullptr);
|
||||
void performMonitorReload();
|
||||
void ensureMonitorStatus();
|
||||
void ensureVRR(PHLMONITOR pMonitor = nullptr);
|
||||
|
||||
bool shouldUseSoftwareCursors();
|
||||
bool shouldUseSoftwareCursors(PHLMONITOR pMonitor);
|
||||
void updateWatcher();
|
||||
|
||||
std::string parseKeyword(const std::string&, const std::string&);
|
||||
std::string parseKeyword(const std::string&, const std::string&);
|
||||
|
||||
void addParseError(const std::string&);
|
||||
void addParseError(const std::string&);
|
||||
|
||||
SAnimationPropertyConfig* getAnimationPropertyConfig(const std::string&);
|
||||
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
|
||||
|
||||
void addExecRule(const SExecRequestedRule&);
|
||||
void addExecRule(const SExecRequestedRule&);
|
||||
|
||||
void handlePluginLoads();
|
||||
std::string getErrors();
|
||||
void handlePluginLoads();
|
||||
std::string getErrors();
|
||||
|
||||
// keywords
|
||||
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExecShutdown(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> 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> handleWindowRuleV2(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExec(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExecRawOnce(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleExecShutdown(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> handleUnbind(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePermission(const std::string&, const std::string&);
|
||||
|
||||
std::string configCurrentPath;
|
||||
std::string m_configCurrentPath;
|
||||
|
||||
std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> mbWindowProperties = {
|
||||
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.allowsInput; }},
|
||||
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.dimAround; }},
|
||||
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.decorate; }},
|
||||
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }},
|
||||
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }},
|
||||
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }},
|
||||
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noAnim; }},
|
||||
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBlur; }},
|
||||
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBorder; }},
|
||||
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noDim; }},
|
||||
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noFocus; }},
|
||||
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
|
||||
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noRounding; }},
|
||||
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShadow; }},
|
||||
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
|
||||
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.opaque; }},
|
||||
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.RGBX; }},
|
||||
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
|
||||
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.tearing; }},
|
||||
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.xray; }},
|
||||
};
|
||||
bool m_wantsMonitorReload = false;
|
||||
bool m_noMonitorReload = false;
|
||||
bool m_isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
|
||||
bool m_lastConfigVerificationWasSuccessful = true;
|
||||
|
||||
std::unordered_map<std::string, std::function<CWindowOverridableVar<int>*(const PHLWINDOW&)>> miWindowProperties = {
|
||||
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.rounding; }},
|
||||
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.borderSize; }},
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::function<CWindowOverridableVar<float>*(PHLWINDOW)>> mfWindowProperties = {
|
||||
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
|
||||
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
|
||||
|
||||
bool m_bWantsMonitorReload = false;
|
||||
bool m_bForceReload = false;
|
||||
bool m_bNoMonitorReload = false;
|
||||
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
|
||||
void storeFloatingSize(PHLWINDOW window, const Vector2D& size);
|
||||
std::optional<Vector2D> getStoredFloatingSize(PHLWINDOW window);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Hyprlang::CConfig> m_pConfig;
|
||||
UP<Hyprlang::CConfig> m_config;
|
||||
|
||||
std::vector<std::string> configPaths; // stores all the config paths
|
||||
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
|
||||
std::vector<std::string> m_configPaths;
|
||||
|
||||
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; // stores all the animations with their set values
|
||||
Hyprutils::Animation::CAnimationConfigTree m_animationTree;
|
||||
|
||||
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
|
||||
std::string m_currentSubmap = ""; // For storing the current keybind submap
|
||||
|
||||
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||
std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||
|
||||
std::vector<std::string> m_vDeclaredPlugins;
|
||||
std::vector<SPluginKeyword> pluginKeywords;
|
||||
std::vector<SPluginVariable> pluginVariables;
|
||||
std::vector<std::string> m_declaredPlugins;
|
||||
std::vector<SPluginKeyword> m_pluginKeywords;
|
||||
std::vector<SPluginVariable> m_pluginVariables;
|
||||
|
||||
bool isFirstLaunch = true; // For exec-once
|
||||
bool m_isFirstLaunch = true; // For exec-once
|
||||
|
||||
std::vector<SMonitorRule> m_vMonitorRules;
|
||||
std::vector<SWorkspaceRule> m_vWorkspaceRules;
|
||||
std::vector<SP<CWindowRule>> m_vWindowRules;
|
||||
std::vector<SP<CLayerRule>> m_vLayerRules;
|
||||
std::vector<std::string> m_dBlurLSNamespaces;
|
||||
std::vector<SMonitorRule> m_monitorRules;
|
||||
std::vector<SWorkspaceRule> m_workspaceRules;
|
||||
std::vector<SP<CWindowRule>> m_windowRules;
|
||||
std::vector<SP<CLayerRule>> m_layerRules;
|
||||
std::vector<std::string> m_blurLSNamespaces;
|
||||
|
||||
bool firstExecDispatched = false;
|
||||
bool m_bManualCrashInitiated = false;
|
||||
std::vector<std::string> firstExecRequests;
|
||||
std::vector<std::string> finalExecRequests;
|
||||
bool m_firstExecDispatched = false;
|
||||
bool m_manualCrashInitiated = false;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
|
||||
std::string m_szConfigErrors = "";
|
||||
std::vector<SFirstExecRequest> m_firstExecRequests; // bool is for if with rules
|
||||
std::vector<std::string> m_finalExecRequests;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> m_failedPluginConfigValues; // for plugin values of unloaded plugins
|
||||
std::string m_configErrors = "";
|
||||
|
||||
uint32_t m_configValueNumber = 0;
|
||||
|
||||
// internal methods
|
||||
void setAnimForChildren(SAnimationPropertyConfig* const);
|
||||
void updateBlurredLS(const std::string&, const bool);
|
||||
void setDefaultAnimationVars();
|
||||
std::optional<std::string> resetHLConfig();
|
||||
static std::optional<std::string> generateConfig(std::string configPath);
|
||||
static std::optional<std::string> verifyConfigExists();
|
||||
void postConfigReload(const Hyprlang::CParseResult& result);
|
||||
void reload();
|
||||
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
||||
void updateBlurredLS(const std::string&, const bool);
|
||||
void setDefaultAnimationVars();
|
||||
std::optional<std::string> resetHLConfig();
|
||||
std::optional<std::string> generateConfig(std::string configPath);
|
||||
std::optional<std::string> verifyConfigExists();
|
||||
void postConfigReload(const Hyprlang::CParseResult& result);
|
||||
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
||||
|
||||
void registerConfigVar(const char* name, const Hyprlang::INT& val);
|
||||
void registerConfigVar(const char* name, const Hyprlang::FLOAT& val);
|
||||
void registerConfigVar(const char* name, const Hyprlang::VEC2& val);
|
||||
void registerConfigVar(const char* name, const Hyprlang::STRING& val);
|
||||
void registerConfigVar(const char* name, Hyprlang::CUSTOMTYPE&& val);
|
||||
|
||||
std::unordered_map<SFloatCache, Vector2D> m_mStoredFloatingSizes;
|
||||
|
||||
friend struct SConfigOptionDescription;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
||||
inline UP<CConfigManager> g_pConfigManager;
|
||||
|
15
src/config/ConfigValue.cpp
Normal file
15
src/config/ConfigValue.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "ConfigValue.hpp"
|
||||
#include "ConfigManager.hpp"
|
||||
#include "../macros.hpp"
|
||||
|
||||
void local__configValuePopulate(void* const** p, const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
|
||||
*p = PVHYPRLANG->getDataStaticPtr();
|
||||
}
|
||||
|
||||
std::type_index local__configValueTypeIdx(const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
const auto ANY = PVHYPRLANG->getValue();
|
||||
return std::type_index(ANY.type());
|
||||
}
|
@ -3,23 +3,20 @@
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <hyprlang.hpp>
|
||||
#include "../debug/Log.hpp"
|
||||
#include "../macros.hpp"
|
||||
#include "ConfigManager.hpp"
|
||||
|
||||
// giga hack to avoid including configManager here
|
||||
// NOLINTNEXTLINE
|
||||
void local__configValuePopulate(void* const** p, const std::string& val);
|
||||
std::type_index local__configValueTypeIdx(const std::string& val);
|
||||
|
||||
template <typename T>
|
||||
class CConfigValue {
|
||||
public:
|
||||
CConfigValue(const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
p_ = PVHYPRLANG->getDataStaticPtr();
|
||||
|
||||
#ifdef HYPRLAND_DEBUG
|
||||
// verify type
|
||||
const auto ANY = PVHYPRLANG->getValue();
|
||||
const auto TYPE = std::type_index(ANY.type());
|
||||
const auto TYPE = local__configValueTypeIdx(val);
|
||||
|
||||
// exceptions
|
||||
const bool STRINGEX = (typeid(T) == typeid(std::string) && TYPE == typeid(Hyprlang::STRING));
|
||||
@ -27,6 +24,8 @@ class CConfigValue {
|
||||
|
||||
RASSERT(typeid(T) == TYPE || STRINGEX || CUSTOMEX, "Mismatched type in CConfigValue<T>, got {} but has {}", typeid(T).name(), TYPE.name());
|
||||
#endif
|
||||
|
||||
local__configValuePopulate(&p_, val);
|
||||
}
|
||||
|
||||
T* ptr() const {
|
||||
@ -71,4 +70,4 @@ template <>
|
||||
inline Hyprlang::CUSTOMTYPE CConfigValue<Hyprlang::CUSTOMTYPE>::operator*() const {
|
||||
RASSERT(false, "Impossible to implement operator* of CConfigValue<Hyprlang::CUSTOMTYPE>, use ptr()");
|
||||
return *ptr();
|
||||
}
|
||||
}
|
||||
|
81
src/config/ConfigWatcher.cpp
Normal file
81
src/config/ConfigWatcher.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include "ConfigWatcher.hpp"
|
||||
#include <sys/inotify.h>
|
||||
#include "../debug/Log.hpp"
|
||||
#include <ranges>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
|
||||
if (!m_inotifyFd.isValid()) {
|
||||
Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
|
||||
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
|
||||
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
|
||||
Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
|
||||
m_inotifyFd.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CFileDescriptor& CConfigWatcher::getInotifyFD() {
|
||||
return m_inotifyFd;
|
||||
}
|
||||
|
||||
void CConfigWatcher::setWatchList(const std::vector<std::string>& paths) {
|
||||
|
||||
// we clear all watches first, because whichever fired is now invalid
|
||||
// or that is at least what it seems to be.
|
||||
// since we don't know which fired,
|
||||
// plus it doesn't matter that much, these ops are done rarely and fast anyways.
|
||||
|
||||
// cleanup old paths
|
||||
for (auto& watch : m_watches) {
|
||||
inotify_rm_watch(m_inotifyFd.get(), watch.wd);
|
||||
}
|
||||
|
||||
m_watches.clear();
|
||||
|
||||
// add new paths
|
||||
for (const auto& path : paths) {
|
||||
m_watches.emplace_back(SInotifyWatch{
|
||||
.wd = inotify_add_watch(m_inotifyFd.get(), path.c_str(), IN_MODIFY | IN_DONT_FOLLOW),
|
||||
.file = path,
|
||||
});
|
||||
|
||||
std::error_code ec, ec2;
|
||||
const auto CANONICAL = std::filesystem::canonical(path, ec);
|
||||
const auto IS_SYMLINK = std::filesystem::is_symlink(path, ec2);
|
||||
if (!ec && !ec2 && IS_SYMLINK) {
|
||||
m_watches.emplace_back(SInotifyWatch{
|
||||
.wd = inotify_add_watch(m_inotifyFd.get(), CANONICAL.c_str(), IN_MODIFY),
|
||||
.file = path,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CConfigWatcher::setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn) {
|
||||
m_watchCallback = fn;
|
||||
}
|
||||
|
||||
void CConfigWatcher::onInotifyEvent() {
|
||||
inotify_event ev;
|
||||
while (read(m_inotifyFd.get(), &ev, sizeof(ev)) > 0) {
|
||||
const auto WD = std::ranges::find_if(m_watches.begin(), m_watches.end(), [wd = ev.wd](const auto& e) { return e.wd == wd; });
|
||||
|
||||
if (WD == m_watches.end()) {
|
||||
Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev.wd);
|
||||
return;
|
||||
}
|
||||
|
||||
m_watchCallback(SConfigWatchEvent{
|
||||
.file = WD->file,
|
||||
});
|
||||
}
|
||||
}
|
33
src/config/ConfigWatcher.hpp
Normal file
33
src/config/ConfigWatcher.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
class CConfigWatcher {
|
||||
public:
|
||||
CConfigWatcher();
|
||||
~CConfigWatcher() = default;
|
||||
|
||||
struct SConfigWatchEvent {
|
||||
std::string file;
|
||||
};
|
||||
|
||||
Hyprutils::OS::CFileDescriptor& getInotifyFD();
|
||||
void setWatchList(const std::vector<std::string>& paths);
|
||||
void setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn);
|
||||
void onInotifyEvent();
|
||||
|
||||
private:
|
||||
struct SInotifyWatch {
|
||||
int wd = -1;
|
||||
std::string file;
|
||||
};
|
||||
|
||||
std::function<void(const SConfigWatchEvent&)> m_watchCallback;
|
||||
std::vector<SInotifyWatch> m_watches;
|
||||
Hyprutils::OS::CFileDescriptor m_inotifyFd;
|
||||
};
|
||||
|
||||
inline UP<CConfigWatcher> g_pConfigWatcher = makeUnique<CConfigWatcher>();
|
@ -65,6 +65,23 @@ env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
###################
|
||||
### PERMISSIONS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Permissions/
|
||||
# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
|
||||
# for security reasons
|
||||
|
||||
# ecosystem {
|
||||
# enforce_permissions = 1
|
||||
# }
|
||||
|
||||
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
|
||||
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
|
||||
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
@ -94,6 +111,7 @@ general {
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
@ -151,10 +169,10 @@ animations {
|
||||
# uncomment all if you wish to use that.
|
||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||
# workspace = f[1], gapsout:0, gapsin:0
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
@ -268,12 +286,12 @@ bindm = $mainMod, mouse:272, movewindow
|
||||
bindm = $mainMod, mouse:273, resizewindow
|
||||
|
||||
# Laptop multimedia keys for volume and LCD brightness
|
||||
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
@ -288,15 +306,12 @@ bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
# Example windowrule
|
||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
)#";
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <cerrno>
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
#include "../plugins/PluginSystem.hpp"
|
||||
#include "../signal-safe.hpp"
|
||||
@ -108,10 +109,7 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
finalCrashReport += "\nDate: ";
|
||||
finalCrashReport += GIT_COMMIT_DATE;
|
||||
finalCrashReport += "\nFlags:\n";
|
||||
#ifdef LEGACY_RENDERER
|
||||
finalCrashReport += "legacyrenderer\n";
|
||||
#endif
|
||||
#ifndef ISDEBUG
|
||||
#if ISDEBUG
|
||||
finalCrashReport += "debug\n";
|
||||
#endif
|
||||
#ifdef NO_XWAYLAND
|
||||
@ -129,11 +127,11 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = plugins[i];
|
||||
finalCrashReport += '\t';
|
||||
finalCrashReport += p->name;
|
||||
finalCrashReport += p->m_name;
|
||||
finalCrashReport += " (";
|
||||
finalCrashReport += p->author;
|
||||
finalCrashReport += p->m_author;
|
||||
finalCrashReport += ") ";
|
||||
finalCrashReport += p->version;
|
||||
finalCrashReport += p->m_version;
|
||||
finalCrashReport += '\n';
|
||||
}
|
||||
|
||||
@ -161,7 +159,7 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
finalCrashReport.writeCmdOutput("pciconf -lv | grep -F -A4 vga");
|
||||
#else
|
||||
finalCrashReport.writeCmdOutput("lspci -vnn | grep VGA");
|
||||
finalCrashReport.writeCmdOutput("lspci -vnn | grep -E '(VGA|Display|3D)'");
|
||||
#endif
|
||||
|
||||
finalCrashReport += "\n\nos-release:\n";
|
||||
@ -191,7 +189,7 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
#endif
|
||||
};
|
||||
u_int miblen = sizeof(mib) / sizeof(mib[0]);
|
||||
char exe[PATH_MAX] = "";
|
||||
char exe[PATH_MAX] = "/nonexistent";
|
||||
size_t sz = sizeof(exe);
|
||||
sysctl(mib, miblen, &exe, &sz, NULL, 0);
|
||||
const auto FPATH = std::filesystem::canonical(exe);
|
||||
@ -241,5 +239,5 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
|
||||
finalCrashReport += "\n\nLog tail:\n";
|
||||
|
||||
finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find('\n') + 1);
|
||||
finalCrashReport += std::string_view(Debug::m_rollingLog).substr(Debug::m_rollingLog.find('\n') + 1);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Compositor.hpp"
|
||||
#include <fstream>
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/defer/Promise.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
// exposed for main.cpp
|
||||
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request);
|
||||
@ -14,17 +17,19 @@ class CHyprCtl {
|
||||
CHyprCtl();
|
||||
~CHyprCtl();
|
||||
|
||||
std::string makeDynamicCall(const std::string& input);
|
||||
SP<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
|
||||
void unregisterCommand(const SP<SHyprCtlCommand>& cmd);
|
||||
std::string getReply(std::string);
|
||||
std::string makeDynamicCall(const std::string& input);
|
||||
SP<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
|
||||
void unregisterCommand(const SP<SHyprCtlCommand>& cmd);
|
||||
std::string getReply(std::string);
|
||||
|
||||
int m_iSocketFD = -1;
|
||||
Hyprutils::OS::CFileDescriptor m_socketFD;
|
||||
|
||||
struct {
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
} m_sCurrentRequestParams;
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
pid_t pid = 0;
|
||||
SP<CPromise<std::string>> pendingPromise;
|
||||
} m_currentRequestParams;
|
||||
|
||||
static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format);
|
||||
static std::string getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format);
|
||||
@ -33,9 +38,9 @@ class CHyprCtl {
|
||||
private:
|
||||
void startHyprCtlSocket();
|
||||
|
||||
std::vector<SP<SHyprCtlCommand>> m_vCommands;
|
||||
std::vector<SP<SHyprCtlCommand>> m_commands;
|
||||
wl_event_source* m_eventSource = nullptr;
|
||||
std::string m_socketPath;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;
|
||||
inline UP<CHyprCtl> g_pHyprCtl;
|
||||
|
@ -2,9 +2,12 @@
|
||||
#include "HyprDebugOverlay.hpp"
|
||||
#include "config/ConfigValue.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../render/pass/TexPassElement.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/AnimationManager.hpp"
|
||||
|
||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||
m_pTexture = makeShared<CTexture>();
|
||||
m_texture = makeShared<CTexture>();
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
@ -13,13 +16,13 @@ void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs)
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_dLastRenderTimes.emplace_back(durationUs / 1000.f);
|
||||
m_lastRenderTimes.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastRenderTimes.pop_front();
|
||||
if (m_lastRenderTimes.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastRenderTimes.pop_front();
|
||||
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
@ -28,13 +31,13 @@ void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float du
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_dLastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
|
||||
m_lastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_dLastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastRenderTimesNoOverlay.pop_front();
|
||||
if (m_lastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastRenderTimesNoOverlay.pop_front();
|
||||
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
@ -43,36 +46,36 @@ void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_dLastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f);
|
||||
m_lastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_lastFrame).count() / 1000.f);
|
||||
|
||||
if (m_dLastFrametimes.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastFrametimes.pop_front();
|
||||
if (m_lastFrametimes.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastFrametimes.pop_front();
|
||||
|
||||
m_tpLastFrame = std::chrono::high_resolution_clock::now();
|
||||
m_lastFrame = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
|
||||
// anim data too
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_pMostHzMonitor ? g_pHyprRenderer->m_pMostHzMonitor.lock() : g_pCompositor->m_pLastMonitor.lock();
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : g_pCompositor->m_lastMonitor.lock();
|
||||
if (PMONITORFORTICKS) {
|
||||
if (m_dLastAnimationTicks.size() > (long unsigned int)PMONITORFORTICKS->refreshRate)
|
||||
m_dLastAnimationTicks.pop_front();
|
||||
if (m_lastAnimationTicks.size() > (long unsigned int)PMONITORFORTICKS->m_refreshRate)
|
||||
m_lastAnimationTicks.pop_front();
|
||||
|
||||
m_dLastAnimationTicks.push_back(g_pAnimationManager->m_fLastTickTime);
|
||||
m_lastAnimationTicks.push_back(g_pAnimationManager->m_lastTickTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
|
||||
if (!m_pMonitor)
|
||||
if (!m_monitor)
|
||||
return 0;
|
||||
|
||||
// get avg fps
|
||||
float avgFrametime = 0;
|
||||
float maxFrametime = 0;
|
||||
float minFrametime = 9999;
|
||||
for (auto const& ft : m_dLastFrametimes) {
|
||||
for (auto const& ft : m_lastFrametimes) {
|
||||
if (ft > maxFrametime)
|
||||
maxFrametime = ft;
|
||||
if (ft < minFrametime)
|
||||
@ -80,12 +83,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgFrametime += ft;
|
||||
}
|
||||
float varFrametime = maxFrametime - minFrametime;
|
||||
avgFrametime /= m_dLastFrametimes.size() == 0 ? 1 : m_dLastFrametimes.size();
|
||||
avgFrametime /= m_lastFrametimes.size() == 0 ? 1 : m_lastFrametimes.size();
|
||||
|
||||
float avgRenderTime = 0;
|
||||
float maxRenderTime = 0;
|
||||
float minRenderTime = 9999;
|
||||
for (auto const& rt : m_dLastRenderTimes) {
|
||||
for (auto const& rt : m_lastRenderTimes) {
|
||||
if (rt > maxRenderTime)
|
||||
maxRenderTime = rt;
|
||||
if (rt < minRenderTime)
|
||||
@ -93,12 +96,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgRenderTime += rt;
|
||||
}
|
||||
float varRenderTime = maxRenderTime - minRenderTime;
|
||||
avgRenderTime /= m_dLastRenderTimes.size() == 0 ? 1 : m_dLastRenderTimes.size();
|
||||
avgRenderTime /= m_lastRenderTimes.size() == 0 ? 1 : m_lastRenderTimes.size();
|
||||
|
||||
float avgRenderTimeNoOverlay = 0;
|
||||
float maxRenderTimeNoOverlay = 0;
|
||||
float minRenderTimeNoOverlay = 9999;
|
||||
for (auto const& rt : m_dLastRenderTimesNoOverlay) {
|
||||
for (auto const& rt : m_lastRenderTimesNoOverlay) {
|
||||
if (rt > maxRenderTimeNoOverlay)
|
||||
maxRenderTimeNoOverlay = rt;
|
||||
if (rt < minRenderTimeNoOverlay)
|
||||
@ -106,12 +109,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgRenderTimeNoOverlay += rt;
|
||||
}
|
||||
float varRenderTimeNoOverlay = maxRenderTimeNoOverlay - minRenderTimeNoOverlay;
|
||||
avgRenderTimeNoOverlay /= m_dLastRenderTimes.size() == 0 ? 1 : m_dLastRenderTimes.size();
|
||||
avgRenderTimeNoOverlay /= m_lastRenderTimes.size() == 0 ? 1 : m_lastRenderTimes.size();
|
||||
|
||||
float avgAnimMgrTick = 0;
|
||||
float maxAnimMgrTick = 0;
|
||||
float minAnimMgrTick = 9999;
|
||||
for (auto const& at : m_dLastAnimationTicks) {
|
||||
for (auto const& at : m_lastAnimationTicks) {
|
||||
if (at > maxAnimMgrTick)
|
||||
maxAnimMgrTick = at;
|
||||
if (at < minAnimMgrTick)
|
||||
@ -119,13 +122,13 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgAnimMgrTick += at;
|
||||
}
|
||||
float varAnimMgrTick = maxAnimMgrTick - minAnimMgrTick;
|
||||
avgAnimMgrTick /= m_dLastAnimationTicks.size() == 0 ? 1 : m_dLastAnimationTicks.size();
|
||||
avgAnimMgrTick /= m_lastAnimationTicks.size() == 0 ? 1 : m_lastAnimationTicks.size();
|
||||
|
||||
const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms
|
||||
const float idealFPS = m_dLastFrametimes.size();
|
||||
const float idealFPS = m_lastFrametimes.size();
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_pCairo);
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_cairo);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
|
||||
@ -134,7 +137,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
|
||||
float maxTextW = 0;
|
||||
int fontSize = 0;
|
||||
auto cr = g_pDebugOverlay->m_pCairo;
|
||||
auto cr = g_pDebugOverlay->m_cairo;
|
||||
|
||||
auto showText = [cr, layoutText, pangoFD, &maxTextW, &fontSize](const char* text, int size) {
|
||||
if (fontSize != size) {
|
||||
@ -160,22 +163,22 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
const int MARGIN_TOP = 8;
|
||||
const int MARGIN_LEFT = 4;
|
||||
cairo_move_to(cr, MARGIN_LEFT, MARGIN_TOP + offset);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
std::string text;
|
||||
showText(m_pMonitor->szName.c_str(), 10);
|
||||
showText(m_monitor->m_name.c_str(), 10);
|
||||
|
||||
if (FPS > idealFPS * 0.95f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 0.2f, 1.f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 0.2f, 1.f, 0.2f, 1.f);
|
||||
else if (FPS > idealFPS * 0.8f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 0.2f, 1.f);
|
||||
else
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 0.2f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 0.2f, 0.2f, 1.f);
|
||||
|
||||
text = std::format("{} FPS", (int)FPS);
|
||||
showText(text.c_str(), 16);
|
||||
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime);
|
||||
showText(text.c_str(), 10);
|
||||
@ -195,10 +198,10 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
double posX = 0, posY = 0;
|
||||
cairo_get_current_point(cr, &posX, &posY);
|
||||
|
||||
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
|
||||
m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset + MARGIN_TOP - 1,
|
||||
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
|
||||
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
m_lastDrawnBox = {(int)g_pCompositor->m_monitors.front()->m_position.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_monitors.front()->m_position.y + offset + MARGIN_TOP - 1,
|
||||
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
|
||||
return posY - offset;
|
||||
}
|
||||
@ -209,7 +212,7 @@ void CHyprDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_mMonitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
m_monitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
@ -218,7 +221,7 @@ void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationU
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_mMonitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
m_monitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
@ -227,47 +230,46 @@ void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_mMonitorOverlays[pMonitor].frameData(pMonitor);
|
||||
m_monitorOverlays[pMonitor].frameData(pMonitor);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::draw() {
|
||||
|
||||
const auto PMONITOR = g_pCompositor->m_vMonitors.front();
|
||||
const auto PMONITOR = g_pCompositor->m_monitors.front();
|
||||
|
||||
if (!m_pCairoSurface || !m_pCairo) {
|
||||
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y);
|
||||
m_pCairo = cairo_create(m_pCairoSurface);
|
||||
if (!m_cairoSurface || !m_cairo) {
|
||||
m_cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y);
|
||||
m_cairo = cairo_create(m_cairoSurface);
|
||||
}
|
||||
|
||||
// clear the pixmap
|
||||
cairo_save(m_pCairo);
|
||||
cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_pCairo);
|
||||
cairo_restore(m_pCairo);
|
||||
cairo_save(m_cairo);
|
||||
cairo_set_operator(m_cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_cairo);
|
||||
cairo_restore(m_cairo);
|
||||
|
||||
// draw the things
|
||||
int offsetY = 0;
|
||||
for (auto const& m : g_pCompositor->m_vMonitors) {
|
||||
offsetY += m_mMonitorOverlays[m].draw(offsetY);
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
offsetY += m_monitorOverlays[m].draw(offsetY);
|
||||
offsetY += 5; // for padding between mons
|
||||
}
|
||||
|
||||
cairo_surface_flush(m_pCairoSurface);
|
||||
cairo_surface_flush(m_cairoSurface);
|
||||
|
||||
// copy the data to an OpenGL texture we have
|
||||
const auto DATA = cairo_image_surface_get_data(m_pCairoSurface);
|
||||
m_pTexture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID);
|
||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||
m_texture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_texture->m_texID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
#ifndef GLES2
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
#endif
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
CBox pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
|
||||
g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f);
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = m_texture;
|
||||
data.box = {0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y};
|
||||
g_pHyprRenderer->m_renderPass.add(makeShared<CTexPassElement>(data));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../render/Texture.hpp"
|
||||
#include <cairo/cairo.h>
|
||||
#include <map>
|
||||
@ -18,13 +17,13 @@ class CHyprMonitorDebugOverlay {
|
||||
void frameData(PHLMONITOR pMonitor);
|
||||
|
||||
private:
|
||||
std::deque<float> m_dLastFrametimes;
|
||||
std::deque<float> m_dLastRenderTimes;
|
||||
std::deque<float> m_dLastRenderTimesNoOverlay;
|
||||
std::deque<float> m_dLastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_tpLastFrame;
|
||||
PHLMONITORREF m_pMonitor;
|
||||
CBox m_wbLastDrawnBox;
|
||||
std::deque<float> m_lastFrametimes;
|
||||
std::deque<float> m_lastRenderTimes;
|
||||
std::deque<float> m_lastRenderTimesNoOverlay;
|
||||
std::deque<float> m_lastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_lastFrame;
|
||||
PHLMONITORREF m_monitor;
|
||||
CBox m_lastDrawnBox;
|
||||
|
||||
friend class CHyprRenderer;
|
||||
};
|
||||
@ -38,15 +37,15 @@ class CHyprDebugOverlay {
|
||||
void frameData(PHLMONITOR);
|
||||
|
||||
private:
|
||||
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_mMonitorOverlays;
|
||||
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_monitorOverlays;
|
||||
|
||||
cairo_surface_t* m_pCairoSurface = nullptr;
|
||||
cairo_t* m_pCairo = nullptr;
|
||||
cairo_surface_t* m_cairoSurface = nullptr;
|
||||
cairo_t* m_cairo = nullptr;
|
||||
|
||||
SP<CTexture> m_pTexture;
|
||||
SP<CTexture> m_texture;
|
||||
|
||||
friend class CHyprMonitorDebugOverlay;
|
||||
friend class CHyprRenderer;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprDebugOverlay> g_pDebugOverlay;
|
||||
inline UP<CHyprDebugOverlay> g_pDebugOverlay;
|
||||
|
@ -3,8 +3,13 @@
|
||||
#include "HyprNotificationOverlay.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "../render/pass/TexPassElement.hpp"
|
||||
|
||||
inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||
#include "../managers/AnimationManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
|
||||
static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||
// preference: Nerd > FontAwesome > text
|
||||
auto eIconBackendChecks = std::array<eIconBackend, 2>{ICONS_BACKEND_NF, ICONS_BACKEND_FA};
|
||||
for (auto iconID : eIconBackendChecks) {
|
||||
@ -18,24 +23,24 @@ inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||
|
||||
CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
||||
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
|
||||
if (m_vNotifications.size() == 0)
|
||||
if (m_notifications.size() == 0)
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->damageBox(&m_bLastDamage);
|
||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||
});
|
||||
|
||||
m_pTexture = makeShared<CTexture>();
|
||||
m_texture = makeShared<CTexture>();
|
||||
}
|
||||
|
||||
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
||||
if (m_pCairo)
|
||||
cairo_destroy(m_pCairo);
|
||||
if (m_pCairoSurface)
|
||||
cairo_surface_destroy(m_pCairoSurface);
|
||||
if (m_cairo)
|
||||
cairo_destroy(m_cairo);
|
||||
if (m_cairoSurface)
|
||||
cairo_surface_destroy(m_cairoSurface);
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::addNotification(const std::string& text, const CHyprColor& color, const float timeMs, const eIcons icon, const float fontSize) {
|
||||
const auto PNOTIF = m_vNotifications.emplace_back(std::make_unique<SNotification>()).get();
|
||||
const auto PNOTIF = m_notifications.emplace_back(makeUnique<SNotification>()).get();
|
||||
|
||||
PNOTIF->text = icon != eIcons::ICON_NONE ? " " + text /* tiny bit of padding otherwise icon touches text */ : text;
|
||||
PNOTIF->color = color == CHyprColor(0) ? ICONS_COLORS[icon] : color;
|
||||
@ -44,19 +49,19 @@ void CHyprNotificationOverlay::addNotification(const std::string& text, const CH
|
||||
PNOTIF->icon = icon;
|
||||
PNOTIF->fontSize = fontSize;
|
||||
|
||||
for (auto const& m : g_pCompositor->m_vMonitors) {
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
g_pCompositor->scheduleFrameForMonitor(m);
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::dismissNotifications(const int amount) {
|
||||
if (amount == -1)
|
||||
m_vNotifications.clear();
|
||||
m_notifications.clear();
|
||||
else {
|
||||
const int AMT = std::min(amount, static_cast<int>(m_vNotifications.size()));
|
||||
const int AMT = std::min(amount, static_cast<int>(m_notifications.size()));
|
||||
|
||||
for (int i = 0; i < AMT; ++i) {
|
||||
m_vNotifications.erase(m_vNotifications.begin());
|
||||
m_notifications.erase(m_notifications.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,12 +77,12 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
float offsetY = 10;
|
||||
float maxWidth = 0;
|
||||
|
||||
const auto SCALE = pMonitor->scale;
|
||||
const auto MONSIZE = pMonitor->vecTransformedSize;
|
||||
const auto SCALE = pMonitor->m_scale;
|
||||
const auto MONSIZE = pMonitor->m_transformedSize;
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
|
||||
PangoLayout* layout = pango_cairo_create_layout(m_pCairo);
|
||||
PangoLayout* layout = pango_cairo_create_layout(m_cairo);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
|
||||
@ -87,9 +92,9 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
const auto iconBackendID = iconBackendFromLayout(layout);
|
||||
const auto PBEZIER = g_pAnimationManager->getBezier("default");
|
||||
|
||||
for (auto const& notif : m_vNotifications) {
|
||||
for (auto const& notif : m_notifications) {
|
||||
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
|
||||
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
|
||||
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->m_pixelSize.x * SCALE) / 1920.f)), 8, 40);
|
||||
|
||||
// first rect (bg, col)
|
||||
const float FIRSTRECTANIMP =
|
||||
@ -134,17 +139,17 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
const auto NOTIFSIZE = Vector2D{textW + 20.0 + iconW + 2 * ICONPADFORNOTIF, textH + 10.0};
|
||||
|
||||
// draw rects
|
||||
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_pCairo);
|
||||
cairo_set_source_rgba(m_cairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_cairo);
|
||||
|
||||
cairo_set_source_rgb(m_pCairo, 0.f, 0.f, 0.f);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_pCairo);
|
||||
cairo_set_source_rgb(m_cairo, 0.f, 0.f, 0.f);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_cairo);
|
||||
|
||||
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
|
||||
cairo_fill(m_pCairo);
|
||||
cairo_set_source_rgba(m_cairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
|
||||
cairo_fill(m_cairo);
|
||||
|
||||
// draw gradient
|
||||
if (notif->icon != ICON_NONE) {
|
||||
@ -153,23 +158,23 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC + GRADIENT_SIZE, offsetY);
|
||||
cairo_pattern_add_color_stop_rgba(pattern, 0, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, ICONCOLOR.a / 3.0);
|
||||
cairo_pattern_add_color_stop_rgba(pattern, 1, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, 0);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, GRADIENT_SIZE, NOTIFSIZE.y);
|
||||
cairo_set_source(m_pCairo, pattern);
|
||||
cairo_fill(m_pCairo);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, GRADIENT_SIZE, NOTIFSIZE.y);
|
||||
cairo_set_source(m_cairo, pattern);
|
||||
cairo_fill(m_cairo);
|
||||
cairo_pattern_destroy(pattern);
|
||||
|
||||
// draw icon
|
||||
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0));
|
||||
cairo_set_source_rgb(m_cairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0));
|
||||
pango_layout_set_text(layout, ICON.c_str(), -1);
|
||||
pango_cairo_show_layout(m_pCairo, layout);
|
||||
pango_cairo_show_layout(m_cairo, layout);
|
||||
}
|
||||
|
||||
// draw text
|
||||
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0));
|
||||
cairo_set_source_rgb(m_cairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0));
|
||||
pango_layout_set_text(layout, notif->text.c_str(), -1);
|
||||
pango_cairo_show_layout(m_pCairo, layout);
|
||||
pango_cairo_show_layout(m_cairo, layout);
|
||||
|
||||
// adjust offset and move on
|
||||
offsetY += NOTIFSIZE.y + 10;
|
||||
@ -182,69 +187,70 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
g_object_unref(layout);
|
||||
|
||||
// cleanup notifs
|
||||
std::erase_if(m_vNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||
std::erase_if(m_notifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||
|
||||
return CBox{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
|
||||
return CBox{(int)(pMonitor->m_position.x + pMonitor->m_size.x - maxWidth - 20), (int)pMonitor->m_position.y, (int)maxWidth + 20, (int)offsetY + 10};
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
|
||||
|
||||
const auto MONSIZE = pMonitor->vecTransformedSize;
|
||||
const auto MONSIZE = pMonitor->m_transformedSize;
|
||||
|
||||
if (m_pLastMonitor != pMonitor || m_vecLastSize != MONSIZE || !m_pCairo || !m_pCairoSurface) {
|
||||
if (m_lastMonitor != pMonitor || m_lastSize != MONSIZE || !m_cairo || !m_cairoSurface) {
|
||||
|
||||
if (m_pCairo && m_pCairoSurface) {
|
||||
cairo_destroy(m_pCairo);
|
||||
cairo_surface_destroy(m_pCairoSurface);
|
||||
if (m_cairo && m_cairoSurface) {
|
||||
cairo_destroy(m_cairo);
|
||||
cairo_surface_destroy(m_cairoSurface);
|
||||
}
|
||||
|
||||
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MONSIZE.x, MONSIZE.y);
|
||||
m_pCairo = cairo_create(m_pCairoSurface);
|
||||
m_pLastMonitor = pMonitor;
|
||||
m_vecLastSize = MONSIZE;
|
||||
m_cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MONSIZE.x, MONSIZE.y);
|
||||
m_cairo = cairo_create(m_cairoSurface);
|
||||
m_lastMonitor = pMonitor;
|
||||
m_lastSize = MONSIZE;
|
||||
}
|
||||
|
||||
// Draw the notifications
|
||||
if (m_vNotifications.size() == 0)
|
||||
if (m_notifications.size() == 0)
|
||||
return;
|
||||
|
||||
// Render to the monitor
|
||||
|
||||
// clear the pixmap
|
||||
cairo_save(m_pCairo);
|
||||
cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_pCairo);
|
||||
cairo_restore(m_pCairo);
|
||||
cairo_save(m_cairo);
|
||||
cairo_set_operator(m_cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_cairo);
|
||||
cairo_restore(m_cairo);
|
||||
|
||||
cairo_surface_flush(m_pCairoSurface);
|
||||
cairo_surface_flush(m_cairoSurface);
|
||||
|
||||
CBox damage = drawNotifications(pMonitor);
|
||||
|
||||
g_pHyprRenderer->damageBox(&damage);
|
||||
g_pHyprRenderer->damageBox(&m_bLastDamage);
|
||||
g_pHyprRenderer->damageBox(damage);
|
||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||
|
||||
g_pCompositor->scheduleFrameForMonitor(pMonitor);
|
||||
|
||||
m_bLastDamage = damage;
|
||||
m_lastDamage = damage;
|
||||
|
||||
// copy the data to an OpenGL texture we have
|
||||
const auto DATA = cairo_image_surface_get_data(m_pCairoSurface);
|
||||
m_pTexture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID);
|
||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||
m_texture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_texture->m_texID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
#ifndef GLES2
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
#endif
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
CBox pMonBox = {0, 0, MONSIZE.x, MONSIZE.y};
|
||||
g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f);
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = m_texture;
|
||||
data.box = {0, 0, MONSIZE.x, MONSIZE.y};
|
||||
data.a = 1.F;
|
||||
|
||||
g_pHyprRenderer->m_renderPass.add(makeShared<CTexPassElement>(data));
|
||||
}
|
||||
|
||||
bool CHyprNotificationOverlay::hasAny() {
|
||||
return !m_vNotifications.empty();
|
||||
return !m_notifications.empty();
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/Timer.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../helpers/time/Timer.hpp"
|
||||
#include "../render/Texture.hpp"
|
||||
#include "../SharedDefs.hpp"
|
||||
|
||||
@ -47,18 +46,18 @@ class CHyprNotificationOverlay {
|
||||
bool hasAny();
|
||||
|
||||
private:
|
||||
CBox drawNotifications(PHLMONITOR pMonitor);
|
||||
CBox m_bLastDamage;
|
||||
CBox drawNotifications(PHLMONITOR pMonitor);
|
||||
CBox m_lastDamage;
|
||||
|
||||
std::vector<std::unique_ptr<SNotification>> m_vNotifications;
|
||||
std::vector<UP<SNotification>> m_notifications;
|
||||
|
||||
cairo_surface_t* m_pCairoSurface = nullptr;
|
||||
cairo_t* m_pCairo = nullptr;
|
||||
cairo_surface_t* m_cairoSurface = nullptr;
|
||||
cairo_t* m_cairo = nullptr;
|
||||
|
||||
PHLMONITORREF m_pLastMonitor;
|
||||
Vector2D m_vecLastSize = Vector2D(-1, -1);
|
||||
PHLMONITORREF m_lastMonitor;
|
||||
Vector2D m_lastSize = Vector2D(-1, -1);
|
||||
|
||||
SP<CTexture> m_pTexture;
|
||||
SP<CTexture> m_texture;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
||||
inline UP<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "Log.hpp"
|
||||
#include "../defines.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "RollingLogFollow.hpp"
|
||||
|
||||
#include <fstream>
|
||||
@ -8,21 +7,21 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
void Debug::init(const std::string& IS) {
|
||||
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
logOfs.open(logFile, std::ios::out | std::ios::app);
|
||||
auto handle = logOfs.native_handle();
|
||||
m_logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
m_logOfs.open(m_logFile, std::ios::out | std::ios::app);
|
||||
auto handle = m_logOfs.native_handle();
|
||||
fcntl(handle, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
void Debug::close() {
|
||||
logOfs.close();
|
||||
m_logOfs.close();
|
||||
}
|
||||
|
||||
void Debug::log(eLogLevel level, std::string str) {
|
||||
if (level == TRACE && !trace)
|
||||
if (level == TRACE && !m_trace)
|
||||
return;
|
||||
|
||||
if (shuttingDown)
|
||||
if (m_shuttingDown)
|
||||
return;
|
||||
|
||||
std::string coloredStr = str;
|
||||
@ -56,20 +55,20 @@ void Debug::log(eLogLevel level, std::string str) {
|
||||
}
|
||||
//NOLINTEND
|
||||
|
||||
rollingLog += str + "\n";
|
||||
if (rollingLog.size() > ROLLING_LOG_SIZE)
|
||||
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
|
||||
m_rollingLog += str + "\n";
|
||||
if (m_rollingLog.size() > ROLLING_LOG_SIZE)
|
||||
m_rollingLog = m_rollingLog.substr(m_rollingLog.size() - ROLLING_LOG_SIZE);
|
||||
|
||||
if (SRollingLogFollow::get().isRunning())
|
||||
SRollingLogFollow::get().addLog(str);
|
||||
|
||||
if (!disableLogs || !**disableLogs) {
|
||||
if (!m_disableLogs || !**m_disableLogs) {
|
||||
// log to a file
|
||||
logOfs << str << "\n";
|
||||
logOfs.flush();
|
||||
m_logOfs << str << "\n";
|
||||
m_logOfs.flush();
|
||||
}
|
||||
|
||||
// log it to the stdout too.
|
||||
if (!disableStdout)
|
||||
std::println("{}", ((coloredLogs && !**coloredLogs) ? str : coloredStr));
|
||||
if (!m_disableStdout)
|
||||
std::println("{}", ((m_coloredLogs && !**m_coloredLogs) ? str : coloredStr));
|
||||
}
|
||||
|
@ -5,8 +5,6 @@
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include "../includes.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
#define LOGMESSAGESIZE 1024
|
||||
#define ROLLING_LOG_SIZE 4096
|
||||
@ -23,17 +21,17 @@ enum eLogLevel : int8_t {
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
inline std::string logFile;
|
||||
inline std::ofstream logOfs;
|
||||
inline int64_t* const* disableLogs = nullptr;
|
||||
inline int64_t* const* disableTime = nullptr;
|
||||
inline bool disableStdout = false;
|
||||
inline bool trace = false;
|
||||
inline bool shuttingDown = false;
|
||||
inline int64_t* const* coloredLogs = nullptr;
|
||||
inline std::string m_logFile;
|
||||
inline std::ofstream m_logOfs;
|
||||
inline int64_t* const* m_disableLogs = nullptr;
|
||||
inline int64_t* const* m_disableTime = nullptr;
|
||||
inline bool m_disableStdout = false;
|
||||
inline bool m_trace = false;
|
||||
inline bool m_shuttingDown = false;
|
||||
inline int64_t* const* m_coloredLogs = nullptr;
|
||||
|
||||
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex logMutex;
|
||||
inline std::string m_rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex m_logMutex;
|
||||
|
||||
void init(const std::string& IS);
|
||||
void close();
|
||||
@ -44,18 +42,18 @@ namespace Debug {
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::lock_guard<std::mutex> guard(logMutex);
|
||||
std::lock_guard<std::mutex> guard(m_logMutex);
|
||||
|
||||
if (level == TRACE && !trace)
|
||||
if (level == TRACE && !m_trace)
|
||||
return;
|
||||
|
||||
if (shuttingDown)
|
||||
if (m_shuttingDown)
|
||||
return;
|
||||
|
||||
std::string logMsg = "";
|
||||
|
||||
// print date and time to the ofs
|
||||
if (disableTime && !**disableTime) {
|
||||
if (m_disableTime && !**m_disableTime) {
|
||||
#ifndef _LIBCPP_VERSION
|
||||
static auto current_zone = std::chrono::current_zone();
|
||||
const auto zt = std::chrono::zoned_time{current_zone, std::chrono::system_clock::now()};
|
||||
|
@ -5,55 +5,55 @@
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
struct SRollingLogFollow {
|
||||
std::unordered_map<int, std::string> socketToRollingLogFollowQueue;
|
||||
std::shared_mutex m;
|
||||
bool running = false;
|
||||
std::unordered_map<int, std::string> m_socketToRollingLogFollowQueue;
|
||||
std::shared_mutex m_mutex;
|
||||
bool m_running = false;
|
||||
static constexpr size_t ROLLING_LOG_FOLLOW_TOO_BIG = 8192;
|
||||
|
||||
// Returns true if the queue is empty for the given socket
|
||||
bool isEmpty(int socket) {
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return socketToRollingLogFollowQueue[socket].empty();
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return m_socketToRollingLogFollowQueue[socket].empty();
|
||||
}
|
||||
|
||||
std::string debugInfo() {
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size());
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return std::format("RollingLogFollow, got {} connections", m_socketToRollingLogFollowQueue.size());
|
||||
}
|
||||
|
||||
std::string getLog(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
|
||||
const std::string ret = socketToRollingLogFollowQueue[socket];
|
||||
socketToRollingLogFollowQueue[socket] = "";
|
||||
const std::string ret = m_socketToRollingLogFollowQueue[socket];
|
||||
m_socketToRollingLogFollowQueue[socket] = "";
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
void addLog(const std::string& log) {
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
running = true;
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_running = true;
|
||||
std::vector<int> to_erase;
|
||||
for (const auto& p : socketToRollingLogFollowQueue)
|
||||
socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||
for (const auto& p : m_socketToRollingLogFollowQueue)
|
||||
m_socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return running;
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void stopFor(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
socketToRollingLogFollowQueue.erase(socket);
|
||||
if (socketToRollingLogFollowQueue.empty())
|
||||
running = false;
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_socketToRollingLogFollowQueue.erase(socket);
|
||||
if (m_socketToRollingLogFollowQueue.empty())
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void startFor(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
|
||||
running = true;
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
static SRollingLogFollow& get() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "helpers/Color.hpp"
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "../macros.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
class CWorkspace;
|
||||
class CWindow;
|
||||
class CLayerSurface;
|
||||
|
@ -1,37 +1,40 @@
|
||||
#include <re2/re2.h>
|
||||
#include "LayerRule.hpp"
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order"};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
|
||||
|
||||
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : targetNamespace(ns_), rule(rule_) {
|
||||
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
|
||||
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
|
||||
const bool VALID = RULES.contains(m_rule) || std::ranges::any_of(RULES_PREFIX, [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
|
||||
|
||||
if (!VALID)
|
||||
return;
|
||||
|
||||
if (rule == "noanim")
|
||||
ruleType = RULE_NOANIM;
|
||||
else if (rule == "blur")
|
||||
ruleType = RULE_BLUR;
|
||||
else if (rule == "blurpopups")
|
||||
ruleType = RULE_BLURPOPUPS;
|
||||
else if (rule == "dimaround")
|
||||
ruleType = RULE_DIMAROUND;
|
||||
else if (rule.starts_with("ignorealpha"))
|
||||
ruleType = RULE_IGNOREALPHA;
|
||||
else if (rule.starts_with("ignorezero"))
|
||||
ruleType = RULE_IGNOREZERO;
|
||||
else if (rule.starts_with("xray"))
|
||||
ruleType = RULE_XRAY;
|
||||
else if (rule.starts_with("animation"))
|
||||
ruleType = RULE_ANIMATION;
|
||||
else if (rule.starts_with("order"))
|
||||
ruleType = RULE_ORDER;
|
||||
if (m_rule == "noanim")
|
||||
m_ruleType = RULE_NOANIM;
|
||||
else if (m_rule == "blur")
|
||||
m_ruleType = RULE_BLUR;
|
||||
else if (m_rule == "blurpopups")
|
||||
m_ruleType = RULE_BLURPOPUPS;
|
||||
else if (m_rule == "dimaround")
|
||||
m_ruleType = RULE_DIMAROUND;
|
||||
else if (m_rule.starts_with("ignorealpha"))
|
||||
m_ruleType = RULE_IGNOREALPHA;
|
||||
else if (m_rule.starts_with("ignorezero"))
|
||||
m_ruleType = RULE_IGNOREZERO;
|
||||
else if (m_rule.starts_with("xray"))
|
||||
m_ruleType = RULE_XRAY;
|
||||
else if (m_rule.starts_with("animation"))
|
||||
m_ruleType = RULE_ANIMATION;
|
||||
else if (m_rule.starts_with("order"))
|
||||
m_ruleType = RULE_ORDER;
|
||||
else if (m_rule.starts_with("abovelock"))
|
||||
m_ruleType = RULE_ABOVELOCK;
|
||||
else {
|
||||
Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
|
||||
ruleType = RULE_INVALID;
|
||||
m_ruleType = RULE_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include "Rule.hpp"
|
||||
|
||||
class CLayerRule {
|
||||
public:
|
||||
@ -13,6 +14,7 @@ class CLayerRule {
|
||||
RULE_BLUR,
|
||||
RULE_BLURPOPUPS,
|
||||
RULE_DIMAROUND,
|
||||
RULE_ABOVELOCK,
|
||||
RULE_IGNOREALPHA,
|
||||
RULE_IGNOREZERO,
|
||||
RULE_XRAY,
|
||||
@ -21,8 +23,10 @@ class CLayerRule {
|
||||
RULE_ZUMBA,
|
||||
};
|
||||
|
||||
eRuleType ruleType = RULE_INVALID;
|
||||
eRuleType m_ruleType = RULE_INVALID;
|
||||
|
||||
const std::string targetNamespace;
|
||||
const std::string rule;
|
||||
};
|
||||
const std::string m_targetNamespace;
|
||||
const std::string m_rule;
|
||||
|
||||
CRuleRegexContainer m_targetNamespaceRegex;
|
||||
};
|
||||
|
@ -4,450 +4,469 @@
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../managers/SeatManager.hpp"
|
||||
#include "../managers/AnimationManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
|
||||
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
|
||||
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
|
||||
|
||||
auto pMonitor = resource->monitor.empty() ? g_pCompositor->m_pLastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->monitor);
|
||||
auto pMonitor = resource->m_monitor.empty() ? g_pCompositor->m_lastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->m_monitor);
|
||||
|
||||
pLS->surface->assign(resource->surface.lock(), pLS);
|
||||
pLS->m_surface->assign(resource->m_surface.lock(), pLS);
|
||||
|
||||
if (!pMonitor) {
|
||||
Debug::log(ERR, "New LS has no monitor??");
|
||||
return pLS;
|
||||
}
|
||||
|
||||
if (pMonitor->pMirrorOf)
|
||||
pMonitor = g_pCompositor->m_vMonitors.front();
|
||||
if (pMonitor->m_mirrorOf)
|
||||
pMonitor = g_pCompositor->m_monitors.front();
|
||||
|
||||
pLS->self = pLS;
|
||||
pLS->m_self = pLS;
|
||||
|
||||
pLS->szNamespace = resource->layerNamespace;
|
||||
pLS->m_namespace = resource->m_layerNamespace;
|
||||
|
||||
pLS->layer = resource->current.layer;
|
||||
pLS->popupHead = std::make_unique<CPopup>(pLS);
|
||||
pLS->monitor = pMonitor;
|
||||
pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS);
|
||||
pLS->m_layer = resource->m_current.layer;
|
||||
pLS->m_popupHead = CPopup::create(pLS);
|
||||
pLS->m_monitor = pMonitor;
|
||||
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
|
||||
|
||||
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
|
||||
pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace);
|
||||
|
||||
pLS->alpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
pLS->realPosition.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
pLS->realSize.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
pLS->alpha.registerVar();
|
||||
pLS->realPosition.registerVar();
|
||||
pLS->realSize.registerVar();
|
||||
g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
|
||||
pLS->registerCallbacks();
|
||||
|
||||
pLS->alpha.setValueAndWarp(0.f);
|
||||
pLS->m_alpha->setValueAndWarp(0.f);
|
||||
|
||||
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->layerNamespace, (int)pLS->layer, pMonitor->szName);
|
||||
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->m_layerNamespace, (int)pLS->m_layer, pMonitor->m_name);
|
||||
|
||||
return pLS;
|
||||
}
|
||||
|
||||
void CLayerSurface::registerCallbacks() {
|
||||
alpha.setUpdateCallback([this](void*) {
|
||||
if (dimAround)
|
||||
g_pHyprRenderer->damageMonitor(monitor.lock());
|
||||
m_alpha->setUpdateCallback([this](auto) {
|
||||
if (m_dimAround)
|
||||
g_pHyprRenderer->damageMonitor(m_monitor.lock());
|
||||
});
|
||||
}
|
||||
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : layerSurface(resource_) {
|
||||
listeners.commit = layerSurface->events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||
listeners.map = layerSurface->events.map.registerListener([this](std::any d) { onMap(); });
|
||||
listeners.unmap = layerSurface->events.unmap.registerListener([this](std::any d) { onUnmap(); });
|
||||
listeners.destroy = layerSurface->events.destroy.registerListener([this](std::any d) { onDestroy(); });
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : m_layerSurface(resource_) {
|
||||
m_listeners.commit = m_layerSurface->m_events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||
m_listeners.map = m_layerSurface->m_events.map.registerListener([this](std::any d) { onMap(); });
|
||||
m_listeners.unmap = m_layerSurface->m_events.unmap.registerListener([this](std::any d) { onUnmap(); });
|
||||
m_listeners.destroy = m_layerSurface->m_events.destroy.registerListener([this](std::any d) { onDestroy(); });
|
||||
|
||||
surface = CWLSurface::create();
|
||||
m_surface = CWLSurface::create();
|
||||
}
|
||||
|
||||
CLayerSurface::~CLayerSurface() {
|
||||
if (!g_pHyprOpenGL)
|
||||
return;
|
||||
|
||||
if (surface)
|
||||
surface->unassign();
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); });
|
||||
std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); });
|
||||
|
||||
for (auto const& mon : g_pCompositor->m_vRealMonitors) {
|
||||
for (auto& lsl : mon->m_aLayerSurfaceLayers) {
|
||||
for (auto const& mon : g_pCompositor->m_realMonitors) {
|
||||
for (auto& lsl : mon->m_layerSurfaceLayers) {
|
||||
std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLayerSurface::onDestroy() {
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)m_layerSurface.get());
|
||||
|
||||
const auto PMONITOR = monitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
|
||||
|
||||
if (!fadingOut) {
|
||||
if (mapped) {
|
||||
if (!m_fadingOut) {
|
||||
if (m_mapped) {
|
||||
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
|
||||
onUnmap();
|
||||
} else {
|
||||
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
|
||||
alpha.setValueAndWarp(0.f);
|
||||
fadingOut = true;
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
if (m_alpha)
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
m_fadingOut = true;
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
}
|
||||
}
|
||||
|
||||
popupHead.reset();
|
||||
m_popupHead.reset();
|
||||
|
||||
noProcess = true;
|
||||
m_noProcess = true;
|
||||
|
||||
// rearrange to fix the reserved areas
|
||||
if (PMONITOR) {
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
|
||||
// and damage
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
}
|
||||
|
||||
readyToDelete = true;
|
||||
layerSurface.reset();
|
||||
if (surface)
|
||||
surface->unassign();
|
||||
m_readyToDelete = true;
|
||||
m_layerSurface.reset();
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
|
||||
listeners.unmap.reset();
|
||||
listeners.destroy.reset();
|
||||
listeners.map.reset();
|
||||
listeners.commit.reset();
|
||||
m_listeners.unmap.reset();
|
||||
m_listeners.destroy.reset();
|
||||
m_listeners.map.reset();
|
||||
m_listeners.commit.reset();
|
||||
}
|
||||
|
||||
void CLayerSurface::onMap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)m_layerSurface.get());
|
||||
|
||||
mapped = true;
|
||||
interactivity = layerSurface->current.interactivity;
|
||||
m_mapped = true;
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
|
||||
layerSurface->surface->map();
|
||||
m_layerSurface->m_surface->map();
|
||||
|
||||
// this layer might be re-mapped.
|
||||
fadingOut = false;
|
||||
g_pCompositor->removeFromFadingOutSafe(self.lock());
|
||||
m_fadingOut = false;
|
||||
g_pCompositor->removeFromFadingOutSafe(m_self.lock());
|
||||
|
||||
// fix if it changed its mon
|
||||
const auto PMONITOR = monitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
applyRules();
|
||||
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
|
||||
surface->resource()->enter(PMONITOR->self.lock());
|
||||
m_surface->resource()->enter(PMONITOR->m_self.lock());
|
||||
|
||||
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
|
||||
if (ISEXCLUSIVE)
|
||||
g_pInputManager->m_dExclusiveLSes.push_back(self);
|
||||
g_pInputManager->m_exclusiveLSes.push_back(m_self);
|
||||
|
||||
const bool GRABSFOCUS = ISEXCLUSIVE ||
|
||||
(layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
||||
(m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
||||
// don't focus if constrained
|
||||
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()));
|
||||
(g_pSeatManager->m_mouse.expired() || !g_pInputManager->isConstrained()));
|
||||
|
||||
if (GRABSFOCUS) {
|
||||
// TODO: use the new superb really very cool grab
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
g_pCompositor->focusSurface(surface->resource());
|
||||
if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_surface->resource()))
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
|
||||
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
|
||||
g_pInputManager->m_bEmptyFocusCursorSet = false;
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
}
|
||||
|
||||
position = Vector2D(geometry.x, geometry.y);
|
||||
m_position = Vector2D(m_geometry.x, m_geometry.y);
|
||||
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
const bool FULLSCREEN = PMONITOR->activeWorkspace && PMONITOR->activeWorkspace->m_bHasFullscreenWindow && PMONITOR->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
const bool FULLSCREEN = PMONITOR->m_activeWorkspace && PMONITOR->m_activeWorkspace->m_hasFullscreenWindow && PMONITOR->m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN;
|
||||
|
||||
startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
|
||||
readyToDelete = false;
|
||||
fadingOut = false;
|
||||
startAnimation(!(m_layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
|
||||
m_readyToDelete = false;
|
||||
m_fadingOut = false;
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", szNamespace});
|
||||
EMIT_HOOK_EVENT("openLayer", self.lock());
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace});
|
||||
EMIT_HOOK_EVENT("openLayer", m_self.lock());
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::onUnmap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)m_layerSurface.get());
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", layerSurface->layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", self.lock());
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", m_self.lock());
|
||||
|
||||
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
|
||||
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; });
|
||||
|
||||
if (!monitor || g_pCompositor->m_bUnsafeState) {
|
||||
if (!m_monitor || g_pCompositor->m_unsafeState) {
|
||||
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
m_mapped = false;
|
||||
if (m_layerSurface && m_layerSurface->m_surface)
|
||||
m_layerSurface->m_surface->unmap();
|
||||
|
||||
startAnimation(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// end any pending animations so that snapshot has right dimensions
|
||||
m_realPosition->warp();
|
||||
m_realSize->warp();
|
||||
|
||||
// make a snapshot and start fade
|
||||
g_pHyprOpenGL->makeLayerSnapshot(self.lock());
|
||||
g_pHyprRenderer->makeLayerSnapshot(m_self.lock());
|
||||
|
||||
startAnimation(false);
|
||||
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
m_mapped = false;
|
||||
if (m_layerSurface && m_layerSurface->m_surface)
|
||||
m_layerSurface->m_surface->unmap();
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
|
||||
const auto PMONITOR = monitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == surface->resource() || g_pSeatManager->state.pointerFocus == surface->resource();
|
||||
const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_surface->resource() || g_pSeatManager->m_state.pointerFocus == m_surface->resource();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
// refocus if needed
|
||||
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable()))
|
||||
g_pInputManager->refocusLastWindow(PMONITOR);
|
||||
else if (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus != surface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus->m_hlSurface && !g_pCompositor->m_lastFocus->m_hlSurface->keyboardFocusable())) {
|
||||
if (!g_pInputManager->refocusLastWindow(PMONITOR))
|
||||
g_pInputManager->refocus();
|
||||
} else if (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus != m_surface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_lastFocus.lock());
|
||||
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.size.x,
|
||||
(int)layerSurface->surface->current.size.y};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
geomFixed = {m_geometry.x + (int)PMONITOR->m_position.x, m_geometry.y + (int)PMONITOR->m_position.y, (int)m_layerSurface->m_surface->m_current.size.x,
|
||||
(int)m_layerSurface->m_surface->m_current.size.y};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
g_pInputManager->sendMotionEventsToFocused();
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
}
|
||||
|
||||
void CLayerSurface::onCommit() {
|
||||
if (!layerSurface)
|
||||
if (!m_layerSurface)
|
||||
return;
|
||||
|
||||
if (!mapped) {
|
||||
if (!m_mapped) {
|
||||
// we're re-mapping if this is the case
|
||||
if (layerSurface->surface && !layerSurface->surface->current.texture) {
|
||||
fadingOut = false;
|
||||
geometry = {};
|
||||
if (m_layerSurface->m_surface && !m_layerSurface->m_surface->m_current.texture) {
|
||||
m_fadingOut = false;
|
||||
m_geometry = {};
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(monitorID());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PMONITOR = monitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
|
||||
|
||||
CBox geomFixed = {geometry.x, geometry.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
CBox geomFixed = {m_geometry.x, m_geometry.y, m_geometry.width, m_geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
if (layerSurface->current.committed != 0) {
|
||||
if (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
|
||||
if (m_layerSurface->m_current.committed != 0) {
|
||||
if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
|
||||
|
||||
for (auto it = PMONITOR->m_aLayerSurfaceLayers[layer].begin(); it != PMONITOR->m_aLayerSurfaceLayers[layer].end(); it++) {
|
||||
if (*it == self) {
|
||||
if (layerSurface->current.layer == layer)
|
||||
for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) {
|
||||
if (*it == m_self) {
|
||||
if (m_layerSurface->m_current.layer == m_layer)
|
||||
break;
|
||||
PMONITOR->m_aLayerSurfaceLayers[layerSurface->current.layer].emplace_back(*it);
|
||||
PMONITOR->m_aLayerSurfaceLayers[layer].erase(it);
|
||||
PMONITOR->m_layerSurfaceLayers[m_layerSurface->m_current.layer].emplace_back(*it);
|
||||
PMONITOR->m_layerSurfaceLayers[m_layer].erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
layer = layerSurface->current.layer;
|
||||
m_layer = m_layerSurface->m_current.layer;
|
||||
|
||||
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
|
||||
}
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
} else {
|
||||
position = Vector2D(geometry.x, geometry.y);
|
||||
m_position = Vector2D(m_geometry.x, m_geometry.y);
|
||||
|
||||
// update geom if it changed
|
||||
if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.hasDestination) {
|
||||
if (m_layerSurface->m_surface->m_current.scale == 1 && PMONITOR->m_scale != 1.f && m_layerSurface->m_surface->m_current.viewport.hasDestination) {
|
||||
// fractional scaling. Dirty hack.
|
||||
geometry = {geometry.pos(), layerSurface->surface->current.viewport.destination};
|
||||
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.viewport.destination};
|
||||
} else {
|
||||
// this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly.
|
||||
geometry = {geometry.pos(), layerSurface->surface->current.size};
|
||||
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.size};
|
||||
}
|
||||
}
|
||||
|
||||
if (realPosition.goal() != geometry.pos()) {
|
||||
if (realPosition.isBeingAnimated())
|
||||
realPosition = geometry.pos();
|
||||
if (m_realPosition->goal() != m_geometry.pos()) {
|
||||
if (m_realPosition->isBeingAnimated())
|
||||
*m_realPosition = m_geometry.pos();
|
||||
else
|
||||
realPosition.setValueAndWarp(geometry.pos());
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
}
|
||||
if (realSize.goal() != geometry.size()) {
|
||||
if (realSize.isBeingAnimated())
|
||||
realSize = geometry.size();
|
||||
if (m_realSize->goal() != m_geometry.size()) {
|
||||
if (m_realSize->isBeingAnimated())
|
||||
*m_realSize = m_geometry.size();
|
||||
else
|
||||
realSize.setValueAndWarp(geometry.size());
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
}
|
||||
|
||||
if (mapped && (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
|
||||
if (m_mapped && (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
|
||||
bool WASLASTFOCUS = false;
|
||||
layerSurface->surface->breadthfirst(
|
||||
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->state.keyboardFocus == surf; },
|
||||
m_layerSurface->m_surface->breadthfirst(
|
||||
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->m_state.keyboardFocus == surf; },
|
||||
nullptr);
|
||||
if (!WASLASTFOCUS && popupHead) {
|
||||
popupHead->breadthfirst(
|
||||
[&WASLASTFOCUS](CPopup* popup, void* data) {
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->m_pWLSurface && g_pSeatManager->state.keyboardFocus == popup->m_pWLSurface->resource());
|
||||
if (!WASLASTFOCUS && m_popupHead) {
|
||||
m_popupHead->breadthfirst(
|
||||
[&WASLASTFOCUS](WP<CPopup> popup, void* data) {
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->m_wlSurface && g_pSeatManager->m_state.keyboardFocus == popup->m_wlSurface->resource());
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
const bool WASEXCLUSIVE = interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool WASEXCLUSIVE = m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
|
||||
if (!WASEXCLUSIVE && ISEXCLUSIVE)
|
||||
g_pInputManager->m_dExclusiveLSes.push_back(self);
|
||||
g_pInputManager->m_exclusiveLSes.push_back(m_self);
|
||||
else if (WASEXCLUSIVE && !ISEXCLUSIVE)
|
||||
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
|
||||
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == m_self.lock(); });
|
||||
|
||||
// if the surface was focused and interactive but now isn't, refocus
|
||||
if (WASLASTFOCUS && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
|
||||
if (WASLASTFOCUS && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
|
||||
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
|
||||
// so unfocus the surface here.
|
||||
g_pCompositor->focusSurface(nullptr);
|
||||
g_pInputManager->refocusLastWindow(monitor.lock());
|
||||
} else if (WASLASTFOCUS && WASEXCLUSIVE && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||
g_pInputManager->refocusLastWindow(m_monitor.lock());
|
||||
} else if (WASLASTFOCUS && WASEXCLUSIVE && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
} else if (!WASEXCLUSIVE && ISEXCLUSIVE) {
|
||||
// if now exclusive and not previously
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
g_pCompositor->focusSurface(surface->resource());
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
|
||||
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
|
||||
g_pInputManager->m_bEmptyFocusCursorSet = false;
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
interactivity = layerSurface->current.interactivity;
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
|
||||
g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y);
|
||||
g_pHyprRenderer->damageSurface(m_surface->resource(), m_position.x, m_position.y);
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::applyRules() {
|
||||
noAnimations = false;
|
||||
forceBlur = false;
|
||||
ignoreAlpha = false;
|
||||
ignoreAlphaValue = 0.f;
|
||||
dimAround = false;
|
||||
xray = -1;
|
||||
animationStyle.reset();
|
||||
m_noAnimations = false;
|
||||
m_forceBlur = false;
|
||||
m_ignoreAlpha = false;
|
||||
m_ignoreAlphaValue = 0.f;
|
||||
m_dimAround = false;
|
||||
m_xray = -1;
|
||||
m_animationStyle.reset();
|
||||
|
||||
for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) {
|
||||
switch (rule->ruleType) {
|
||||
for (auto const& rule : g_pConfigManager->getMatchingRules(m_self.lock())) {
|
||||
switch (rule->m_ruleType) {
|
||||
case CLayerRule::RULE_NOANIM: {
|
||||
noAnimations = true;
|
||||
m_noAnimations = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_BLUR: {
|
||||
forceBlur = true;
|
||||
m_forceBlur = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_BLURPOPUPS: {
|
||||
forceBlurPopups = true;
|
||||
m_forceBlurPopups = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_IGNOREALPHA:
|
||||
case CLayerRule::RULE_IGNOREZERO: {
|
||||
const auto FIRST_SPACE_POS = rule->rule.find_first_of(' ');
|
||||
const auto FIRST_SPACE_POS = rule->m_rule.find_first_of(' ');
|
||||
std::string alphaValue = "";
|
||||
if (FIRST_SPACE_POS != std::string::npos)
|
||||
alphaValue = rule->rule.substr(FIRST_SPACE_POS + 1);
|
||||
alphaValue = rule->m_rule.substr(FIRST_SPACE_POS + 1);
|
||||
|
||||
try {
|
||||
ignoreAlpha = true;
|
||||
m_ignoreAlpha = true;
|
||||
if (!alphaValue.empty())
|
||||
ignoreAlphaValue = std::stof(alphaValue);
|
||||
m_ignoreAlphaValue = std::stof(alphaValue);
|
||||
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_DIMAROUND: {
|
||||
dimAround = true;
|
||||
m_dimAround = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_XRAY: {
|
||||
CVarList vars{rule->rule, 0, ' '};
|
||||
try {
|
||||
xray = configStringToInt(vars[1]).value_or(false);
|
||||
} catch (...) {}
|
||||
CVarList vars{rule->m_rule, 0, ' '};
|
||||
m_xray = configStringToInt(vars[1]).value_or(false);
|
||||
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ANIMATION: {
|
||||
CVarList vars{rule->rule, 2, 's'};
|
||||
animationStyle = vars[1];
|
||||
CVarList vars{rule->m_rule, 2, 's'};
|
||||
m_animationStyle = vars[1];
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ORDER: {
|
||||
CVarList vars{rule->rule, 2, 's'};
|
||||
CVarList vars{rule->m_rule, 2, 's'};
|
||||
try {
|
||||
order = std::stoi(vars[1]);
|
||||
m_order = std::stoi(vars[1]);
|
||||
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ABOVELOCK: {
|
||||
m_aboveLockscreen = true;
|
||||
|
||||
CVarList vars{rule->m_rule, 0, ' '};
|
||||
m_aboveLockscreenInteractable = configStringToInt(vars[1]).value_or(false);
|
||||
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
const auto ANIMSTYLE = animationStyle.value_or(realPosition.m_pConfig->pValues->internalStyle);
|
||||
if (in) {
|
||||
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
|
||||
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
|
||||
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn");
|
||||
m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"));
|
||||
} else {
|
||||
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
|
||||
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
|
||||
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut");
|
||||
m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
|
||||
}
|
||||
|
||||
const auto ANIMSTYLE = m_animationStyle.value_or(m_realPosition->getStyle());
|
||||
if (ANIMSTYLE.starts_with("slide")) {
|
||||
// get closest edge
|
||||
const auto MIDDLE = geometry.middle();
|
||||
const auto MIDDLE = m_geometry.middle();
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE);
|
||||
|
||||
@ -467,10 +486,10 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
}
|
||||
|
||||
const std::array<Vector2D, 4> edgePoints = {
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, 0.0},
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, PMONITOR->vecSize.y},
|
||||
PMONITOR->vecPosition + Vector2D{0.0, PMONITOR->vecSize.y},
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x, PMONITOR->vecSize.y / 2},
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, 0.0},
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, PMONITOR->m_size.y},
|
||||
PMONITOR->m_position + Vector2D{0.0, PMONITOR->m_size.y},
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x, PMONITOR->m_size.y / 2},
|
||||
};
|
||||
|
||||
float closest = std::numeric_limits<float>::max();
|
||||
@ -485,38 +504,38 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
}
|
||||
}
|
||||
|
||||
realSize.setValueAndWarp(geometry.size());
|
||||
alpha.setValueAndWarp(in ? 0.f : 1.f);
|
||||
alpha = in ? 1.f : 0.f;
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
m_alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
|
||||
Vector2D prePos;
|
||||
|
||||
switch (leader) {
|
||||
case 0:
|
||||
// TOP
|
||||
prePos = {geometry.x, PMONITOR->vecPosition.y - geometry.h};
|
||||
prePos = {m_geometry.x, PMONITOR->m_position.y - m_geometry.h};
|
||||
break;
|
||||
case 1:
|
||||
// BOTTOM
|
||||
prePos = {geometry.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y};
|
||||
prePos = {m_geometry.x, PMONITOR->m_position.y + PMONITOR->m_size.y};
|
||||
break;
|
||||
case 2:
|
||||
// LEFT
|
||||
prePos = {PMONITOR->vecPosition.x - geometry.w, geometry.y};
|
||||
prePos = {PMONITOR->m_position.x - m_geometry.w, m_geometry.y};
|
||||
break;
|
||||
case 3:
|
||||
// RIGHT
|
||||
prePos = {PMONITOR->vecPosition.x + PMONITOR->vecSize.x, geometry.y};
|
||||
prePos = {PMONITOR->m_position.x + PMONITOR->m_size.x, m_geometry.y};
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
if (in) {
|
||||
realPosition.setValueAndWarp(prePos);
|
||||
realPosition = geometry.pos();
|
||||
m_realPosition->setValueAndWarp(prePos);
|
||||
*m_realPosition = m_geometry.pos();
|
||||
} else {
|
||||
realPosition.setValueAndWarp(geometry.pos());
|
||||
realPosition = prePos;
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
*m_realPosition = prePos;
|
||||
}
|
||||
|
||||
} else if (ANIMSTYLE.starts_with("popin")) {
|
||||
@ -532,50 +551,62 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
|
||||
minPerc *= 0.01;
|
||||
|
||||
const auto GOALSIZE = (geometry.size() * minPerc).clamp({5, 5});
|
||||
const auto GOALPOS = geometry.pos() + (geometry.size() - GOALSIZE) / 2.f;
|
||||
const auto GOALSIZE = (m_geometry.size() * minPerc).clamp({5, 5});
|
||||
const auto GOALPOS = m_geometry.pos() + (m_geometry.size() - GOALSIZE) / 2.f;
|
||||
|
||||
alpha.setValueAndWarp(in ? 0.f : 1.f);
|
||||
alpha = in ? 1.f : 0.f;
|
||||
m_alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
|
||||
if (in) {
|
||||
realSize.setValueAndWarp(GOALSIZE);
|
||||
realPosition.setValueAndWarp(GOALPOS);
|
||||
realSize = geometry.size();
|
||||
realPosition = geometry.pos();
|
||||
m_realSize->setValueAndWarp(GOALSIZE);
|
||||
m_realPosition->setValueAndWarp(GOALPOS);
|
||||
*m_realSize = m_geometry.size();
|
||||
*m_realPosition = m_geometry.pos();
|
||||
} else {
|
||||
realSize.setValueAndWarp(geometry.size());
|
||||
realPosition.setValueAndWarp(geometry.pos());
|
||||
realSize = GOALSIZE;
|
||||
realPosition = GOALPOS;
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
*m_realSize = GOALSIZE;
|
||||
*m_realPosition = GOALPOS;
|
||||
}
|
||||
} else {
|
||||
// fade
|
||||
realPosition.setValueAndWarp(geometry.pos());
|
||||
realSize.setValueAndWarp(geometry.size());
|
||||
alpha = in ? 1.f : 0.f;
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
}
|
||||
|
||||
if (!in)
|
||||
fadingOut = true;
|
||||
m_fadingOut = true;
|
||||
}
|
||||
|
||||
bool CLayerSurface::isFadedOut() {
|
||||
if (!fadingOut)
|
||||
if (!m_fadingOut)
|
||||
return false;
|
||||
|
||||
return !realPosition.isBeingAnimated() && !realSize.isBeingAnimated() && !alpha.isBeingAnimated();
|
||||
return !m_realPosition->isBeingAnimated() && !m_realSize->isBeingAnimated() && !m_alpha->isBeingAnimated();
|
||||
}
|
||||
|
||||
int CLayerSurface::popupsCount() {
|
||||
if (!layerSurface || !mapped || fadingOut)
|
||||
if (!m_layerSurface || !m_mapped || m_fadingOut)
|
||||
return 0;
|
||||
|
||||
int no = -1; // we have one dummy
|
||||
popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no);
|
||||
m_popupHead->breadthfirst([](WP<CPopup> p, void* data) { *(int*)data += 1; }, &no);
|
||||
return no;
|
||||
}
|
||||
|
||||
MONITORID CLayerSurface::monitorID() {
|
||||
return monitor ? monitor->ID : MONITOR_INVALID;
|
||||
return m_monitor ? m_monitor->m_id : MONITOR_INVALID;
|
||||
}
|
||||
|
||||
pid_t CLayerSurface::getPID() {
|
||||
pid_t PID = -1;
|
||||
|
||||
if (!m_layerSurface || !m_layerSurface->m_surface || !m_layerSurface->m_surface->getResource() || !m_layerSurface->m_surface->getResource()->resource() ||
|
||||
!m_layerSurface->m_surface->getResource()->resource()->client)
|
||||
return -1;
|
||||
|
||||
wl_client_get_credentials(m_layerSurface->m_surface->getResource()->resource()->client, &PID, nullptr, nullptr);
|
||||
|
||||
return PID;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "../defines.hpp"
|
||||
#include "WLSurface.hpp"
|
||||
#include "../helpers/AnimatedVariable.hpp"
|
||||
#include "LayerRule.hpp"
|
||||
|
||||
class CLayerShellResource;
|
||||
|
||||
@ -18,49 +17,52 @@ class CLayerSurface {
|
||||
public:
|
||||
~CLayerSurface();
|
||||
|
||||
void applyRules();
|
||||
void startAnimation(bool in, bool instant = false);
|
||||
bool isFadedOut();
|
||||
int popupsCount();
|
||||
void applyRules();
|
||||
void startAnimation(bool in, bool instant = false);
|
||||
bool isFadedOut();
|
||||
int popupsCount();
|
||||
|
||||
CAnimatedVariable<Vector2D> realPosition;
|
||||
CAnimatedVariable<Vector2D> realSize;
|
||||
CAnimatedVariable<float> alpha;
|
||||
PHLANIMVAR<Vector2D> m_realPosition;
|
||||
PHLANIMVAR<Vector2D> m_realSize;
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
|
||||
WP<CLayerShellResource> layerSurface;
|
||||
wl_list link;
|
||||
WP<CLayerShellResource> m_layerSurface;
|
||||
|
||||
// the header providing the enum type cannot be imported here
|
||||
int interactivity = 0;
|
||||
int m_interactivity = 0;
|
||||
|
||||
SP<CWLSurface> surface;
|
||||
SP<CWLSurface> m_surface;
|
||||
|
||||
bool mapped = false;
|
||||
uint32_t layer = 0;
|
||||
bool m_mapped = false;
|
||||
uint32_t m_layer = 0;
|
||||
|
||||
PHLMONITORREF monitor;
|
||||
PHLMONITORREF m_monitor;
|
||||
|
||||
bool fadingOut = false;
|
||||
bool readyToDelete = false;
|
||||
bool noProcess = false;
|
||||
bool noAnimations = false;
|
||||
bool m_fadingOut = false;
|
||||
bool m_readyToDelete = false;
|
||||
bool m_noProcess = false;
|
||||
bool m_noAnimations = false;
|
||||
|
||||
bool forceBlur = false;
|
||||
bool forceBlurPopups = false;
|
||||
int64_t xray = -1;
|
||||
bool ignoreAlpha = false;
|
||||
float ignoreAlphaValue = 0.f;
|
||||
bool dimAround = false;
|
||||
int64_t order = 0;
|
||||
bool m_forceBlur = false;
|
||||
bool m_forceBlurPopups = false;
|
||||
int64_t m_xray = -1;
|
||||
bool m_ignoreAlpha = false;
|
||||
float m_ignoreAlphaValue = 0.f;
|
||||
bool m_dimAround = false;
|
||||
int64_t m_order = 0;
|
||||
bool m_aboveLockscreen = false;
|
||||
bool m_aboveLockscreenInteractable = false;
|
||||
|
||||
std::optional<std::string> animationStyle;
|
||||
std::optional<std::string> m_animationStyle;
|
||||
|
||||
PHLLSREF self;
|
||||
PHLLSREF m_self;
|
||||
|
||||
CBox geometry = {0, 0, 0, 0};
|
||||
Vector2D position;
|
||||
std::string szNamespace = "";
|
||||
std::unique_ptr<CPopup> popupHead;
|
||||
CBox m_geometry = {0, 0, 0, 0};
|
||||
Vector2D m_position;
|
||||
std::string m_namespace = "";
|
||||
UP<CPopup> m_popupHead;
|
||||
|
||||
pid_t getPID();
|
||||
|
||||
void onDestroy();
|
||||
void onMap();
|
||||
@ -74,12 +76,12 @@ class CLayerSurface {
|
||||
CHyprSignalListener map;
|
||||
CHyprSignalListener unmap;
|
||||
CHyprSignalListener commit;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
|
||||
void registerCallbacks();
|
||||
|
||||
// For the list lookup
|
||||
bool operator==(const CLayerSurface& rhs) const {
|
||||
return layerSurface == rhs.layerSurface && monitor == rhs.monitor;
|
||||
return m_layerSurface == rhs.m_layerSurface && m_monitor == rhs.m_monitor;
|
||||
}
|
||||
};
|
||||
|
@ -5,192 +5,222 @@
|
||||
#include "../protocols/XDGShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../managers/SeatManager.hpp"
|
||||
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../render/OpenGL.hpp"
|
||||
#include <ranges>
|
||||
|
||||
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
|
||||
initAllSignals();
|
||||
UP<CPopup> CPopup::create(PHLWINDOW pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_windowOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) {
|
||||
initAllSignals();
|
||||
UP<CPopup> CPopup::create(PHLLS pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_layerOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
CPopup::CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner) : m_pWindowOwner(pOwner->m_pWindowOwner), m_pLayerOwner(pOwner->m_pLayerOwner), m_pParent(pOwner), m_pResource(popup) {
|
||||
m_pWLSurface = CWLSurface::create();
|
||||
m_pWLSurface->assign(popup->surface->surface.lock(), this);
|
||||
UP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_resource = resource;
|
||||
popup->m_windowOwner = pOwner->m_windowOwner;
|
||||
popup->m_layerOwner = pOwner->m_layerOwner;
|
||||
popup->m_parent = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->m_wlSurface = CWLSurface::create();
|
||||
popup->m_wlSurface->assign(resource->m_surface->m_surface.lock(), popup.get());
|
||||
|
||||
m_vLastSize = popup->surface->current.geometry.size();
|
||||
reposition();
|
||||
popup->m_lastSize = resource->m_surface->m_current.geometry.size();
|
||||
popup->reposition();
|
||||
|
||||
initAllSignals();
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
CPopup::~CPopup() {
|
||||
if (m_pWLSurface)
|
||||
m_pWLSurface->unassign();
|
||||
if (m_wlSurface)
|
||||
m_wlSurface->unassign();
|
||||
}
|
||||
|
||||
void CPopup::initAllSignals() {
|
||||
|
||||
if (!m_pResource) {
|
||||
if (!m_pWindowOwner.expired())
|
||||
listeners.newPopup = m_pWindowOwner->m_pXDGSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
else if (!m_pLayerOwner.expired())
|
||||
listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
if (!m_resource) {
|
||||
if (!m_windowOwner.expired())
|
||||
m_listeners.newPopup =
|
||||
m_windowOwner->m_xdgSurface->m_events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
else if (!m_layerOwner.expired())
|
||||
m_listeners.newPopup =
|
||||
m_layerOwner->m_layerSurface->m_events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
else
|
||||
ASSERT(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); });
|
||||
listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); });
|
||||
listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
listeners.dismissed = m_pResource->events.dismissed.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); });
|
||||
listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); });
|
||||
listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
m_listeners.reposition = m_resource->m_events.reposition.registerListener([this](std::any d) { this->onReposition(); });
|
||||
m_listeners.map = m_resource->m_surface->m_events.map.registerListener([this](std::any d) { this->onMap(); });
|
||||
m_listeners.unmap = m_resource->m_surface->m_events.unmap.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
m_listeners.dismissed = m_resource->m_events.dismissed.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
m_listeners.destroy = m_resource->m_surface->m_events.destroy.registerListener([this](std::any d) { this->onDestroy(); });
|
||||
m_listeners.commit = m_resource->m_surface->m_events.commit.registerListener([this](std::any d) { this->onCommit(); });
|
||||
m_listeners.newPopup = m_resource->m_surface->m_events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
}
|
||||
|
||||
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
|
||||
const auto POPUP = m_vChildren.emplace_back(makeShared<CPopup>(popup, this)).get();
|
||||
const auto& POPUP = m_children.emplace_back(CPopup::create(popup, m_self));
|
||||
POPUP->m_self = POPUP;
|
||||
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
|
||||
}
|
||||
|
||||
void CPopup::onDestroy() {
|
||||
m_bInert = true;
|
||||
m_inert = true;
|
||||
|
||||
if (!m_pParent)
|
||||
if (!m_parent)
|
||||
return; // head node
|
||||
|
||||
std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; });
|
||||
std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; });
|
||||
}
|
||||
|
||||
void CPopup::onMap() {
|
||||
if (m_bMapped)
|
||||
if (m_mapped)
|
||||
return;
|
||||
|
||||
m_bMapped = true;
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
m_mapped = true;
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
|
||||
|
||||
CBox box = m_pWLSurface->resource()->extends();
|
||||
CBox box = m_wlSurface->resource()->extends();
|
||||
box.translate(COORDS).expand(4);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
m_vLastPos = coordsRelativeToParent();
|
||||
m_lastPos = coordsRelativeToParent();
|
||||
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
m_pSubsurfaceHead = std::make_unique<CSubsurface>(this);
|
||||
m_subsurfaceHead = CSubsurface::create(m_self);
|
||||
|
||||
//unconstrain();
|
||||
sendScale();
|
||||
m_pResource->surface->surface->enter(PMONITOR->self.lock());
|
||||
m_resource->m_surface->m_surface->enter(PMONITOR->m_self.lock());
|
||||
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
}
|
||||
|
||||
void CPopup::onUnmap() {
|
||||
if (!m_bMapped)
|
||||
if (!m_mapped)
|
||||
return;
|
||||
|
||||
if (!m_pResource || !m_pResource->surface) {
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
m_bMapped = false;
|
||||
m_mapped = false;
|
||||
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
// if the popup committed a different size right now, we also need to damage the old size.
|
||||
const Vector2D MAX_DAMAGE_SIZE = {std::max(m_lastSize.x, m_resource->m_surface->m_surface->m_current.size.x),
|
||||
std::max(m_lastSize.y, m_resource->m_surface->m_surface->m_current.size.y)};
|
||||
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
|
||||
CBox box = m_pWLSurface->resource()->extends();
|
||||
CBox box = m_wlSurface->resource()->extends();
|
||||
box.translate(COORDS).expand(4);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
m_pSubsurfaceHead.reset();
|
||||
// damage the last popup's explicit max size as well
|
||||
box = CBox{COORDS, MAX_DAMAGE_SIZE}.expand(4);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
m_subsurfaceHead.reset();
|
||||
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
|
||||
// damage all children
|
||||
breadthfirst(
|
||||
[](CPopup* p, void* data) {
|
||||
if (!p->m_pResource)
|
||||
[](WP<CPopup> p, void* data) {
|
||||
if (!p->m_resource)
|
||||
return;
|
||||
|
||||
auto box = CBox{p->coordsGlobal(), p->size()};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
|
||||
// TODO: probably refocus, but without a motion event?
|
||||
// const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
|
||||
|
||||
if (WASLASTFOCUS)
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
// if (WASLASTFOCUS)
|
||||
// g_pInputManager->simulateMouseMovement();
|
||||
}
|
||||
|
||||
void CPopup::onCommit(bool ignoreSiblings) {
|
||||
if (!m_pResource || !m_pResource->surface) {
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pResource->surface->initialCommit) {
|
||||
m_pResource->surface->scheduleConfigure();
|
||||
if (m_resource->m_surface->m_initialCommit) {
|
||||
m_resource->m_surface->scheduleConfigure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) {
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
if (!m_windowOwner.expired() && (!m_windowOwner->m_isMapped || !m_windowOwner->m_workspace->m_visible)) {
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
|
||||
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
|
||||
if (*PLOGDAMAGE)
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowOwner.lock());
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_pResource->surface->mapped)
|
||||
if (!m_resource->m_surface->m_mapped)
|
||||
return;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
const auto COORDSLOCAL = coordsRelativeToParent();
|
||||
|
||||
if (m_vLastSize != m_pResource->surface->surface->current.size || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) {
|
||||
CBox box = {localToGlobal(m_vLastPos), m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
box = {COORDS, m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
if (m_lastSize != m_resource->m_surface->m_surface->m_current.size || m_requestedReposition || m_lastPos != COORDSLOCAL) {
|
||||
CBox box = {localToGlobal(m_lastPos), m_lastSize};
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
box = {COORDS, m_lastSize};
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
m_vLastPos = COORDSLOCAL;
|
||||
m_lastPos = COORDSLOCAL;
|
||||
}
|
||||
|
||||
if (!ignoreSiblings && m_pSubsurfaceHead)
|
||||
m_pSubsurfaceHead->recheckDamageForSubsurfaces();
|
||||
if (!ignoreSiblings && m_subsurfaceHead)
|
||||
m_subsurfaceHead->recheckDamageForSubsurfaces();
|
||||
|
||||
g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y);
|
||||
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y);
|
||||
|
||||
m_bRequestedReposition = false;
|
||||
m_requestedReposition = false;
|
||||
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
}
|
||||
|
||||
void CPopup::onReposition() {
|
||||
Debug::log(LOG, "Popup {:x} requests reposition", (uintptr_t)this);
|
||||
|
||||
m_bRequestedReposition = true;
|
||||
m_requestedReposition = true;
|
||||
|
||||
m_vLastPos = coordsRelativeToParent();
|
||||
m_lastPos = coordsRelativeToParent();
|
||||
|
||||
reposition();
|
||||
}
|
||||
@ -202,32 +232,32 @@ void CPopup::reposition() {
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||
m_pResource->applyPositioning(box, COORDS);
|
||||
CBox box = {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
|
||||
m_resource->applyPositioning(box, COORDS);
|
||||
}
|
||||
|
||||
SP<CWLSurface> CPopup::getT1Owner() {
|
||||
if (m_pWindowOwner)
|
||||
return m_pWindowOwner->m_pWLSurface;
|
||||
if (m_windowOwner)
|
||||
return m_windowOwner->m_wlSurface;
|
||||
else
|
||||
return m_pLayerOwner->surface;
|
||||
return m_layerOwner->m_surface;
|
||||
}
|
||||
|
||||
Vector2D CPopup::coordsRelativeToParent() {
|
||||
Vector2D offset;
|
||||
|
||||
if (!m_pResource)
|
||||
if (!m_resource)
|
||||
return {};
|
||||
|
||||
CPopup* current = this;
|
||||
offset -= current->m_pResource->surface->current.geometry.pos();
|
||||
WP<CPopup> current = m_self;
|
||||
offset -= current->m_resource->m_surface->m_current.geometry.pos();
|
||||
|
||||
while (current->m_pParent && current->m_pResource) {
|
||||
while (current->m_parent && current->m_resource) {
|
||||
|
||||
offset += current->m_pWLSurface->resource()->current.offset;
|
||||
offset += current->m_pResource->geometry.pos();
|
||||
offset += current->m_wlSurface->resource()->m_current.offset;
|
||||
offset += current->m_resource->m_geometry.pos();
|
||||
|
||||
current = current->m_pParent;
|
||||
current = current->m_parent;
|
||||
}
|
||||
|
||||
return offset;
|
||||
@ -242,26 +272,30 @@ Vector2D CPopup::localToGlobal(const Vector2D& rel) {
|
||||
}
|
||||
|
||||
Vector2D CPopup::t1ParentCoords() {
|
||||
if (!m_pWindowOwner.expired())
|
||||
return m_pWindowOwner->m_vRealPosition.value();
|
||||
if (!m_pLayerOwner.expired())
|
||||
return m_pLayerOwner->realPosition.value();
|
||||
if (!m_windowOwner.expired())
|
||||
return m_windowOwner->m_realPosition->value();
|
||||
if (!m_layerOwner.expired())
|
||||
return m_layerOwner->m_realPosition->value();
|
||||
|
||||
ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
void CPopup::recheckTree() {
|
||||
CPopup* curr = this;
|
||||
while (curr->m_pParent) {
|
||||
curr = curr->m_pParent;
|
||||
WP<CPopup> curr = m_self;
|
||||
while (curr->m_parent) {
|
||||
curr = curr->m_parent;
|
||||
}
|
||||
|
||||
curr->recheckChildrenRecursive();
|
||||
}
|
||||
|
||||
void CPopup::recheckChildrenRecursive() {
|
||||
auto cpy = m_vChildren;
|
||||
if (m_inert || !m_wlSurface)
|
||||
return;
|
||||
|
||||
std::vector<WP<CPopup>> cpy;
|
||||
std::ranges::for_each(m_children, [&cpy](const auto& el) { cpy.emplace_back(el); });
|
||||
for (auto const& c : cpy) {
|
||||
c->onCommit(true);
|
||||
c->recheckChildrenRecursive();
|
||||
@ -269,40 +303,40 @@ void CPopup::recheckChildrenRecursive() {
|
||||
}
|
||||
|
||||
Vector2D CPopup::size() {
|
||||
return m_vLastSize;
|
||||
return m_lastSize;
|
||||
}
|
||||
|
||||
void CPopup::sendScale() {
|
||||
if (!m_pWindowOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pWindowOwner->m_pWLSurface->m_fLastScale);
|
||||
else if (!m_pLayerOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pLayerOwner->surface->m_fLastScale);
|
||||
if (!m_windowOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->m_wlSurface->m_lastScaleFloat);
|
||||
else if (!m_layerOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->m_surface->m_lastScaleFloat);
|
||||
else
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool CPopup::visible() {
|
||||
if (!m_pWindowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_pWindowOwner.lock());
|
||||
if (!m_pLayerOwner.expired())
|
||||
if (!m_windowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
|
||||
if (!m_layerOwner.expired())
|
||||
return true;
|
||||
if (m_pParent)
|
||||
return m_pParent->visible();
|
||||
if (m_parent)
|
||||
return m_parent->visible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPopup::bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data) {
|
||||
void CPopup::bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data) {
|
||||
for (auto const& n : nodes) {
|
||||
fn(n, data);
|
||||
}
|
||||
|
||||
std::vector<CPopup*> nodes2;
|
||||
std::vector<WP<CPopup>> nodes2;
|
||||
nodes2.reserve(nodes.size() * 2);
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
for (auto const& c : n->m_vChildren) {
|
||||
nodes2.push_back(c.get());
|
||||
for (auto const& c : n->m_children) {
|
||||
nodes2.push_back(c->m_self);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,35 +344,42 @@ void CPopup::bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPop
|
||||
bfHelper(nodes2, fn, data);
|
||||
}
|
||||
|
||||
void CPopup::breadthfirst(std::function<void(CPopup*, void*)> fn, void* data) {
|
||||
std::vector<CPopup*> popups;
|
||||
popups.push_back(this);
|
||||
void CPopup::breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data) {
|
||||
std::vector<WP<CPopup>> popups;
|
||||
popups.push_back(m_self);
|
||||
bfHelper(popups, fn, data);
|
||||
}
|
||||
|
||||
CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
std::vector<CPopup*> popups;
|
||||
breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups);
|
||||
WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
std::vector<WP<CPopup>> popups;
|
||||
breadthfirst([&popups](WP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
|
||||
|
||||
for (auto const& p : popups | std::views::reverse) {
|
||||
if (!p->m_pResource || !p->m_bMapped)
|
||||
if (!p->m_resource || !p->m_mapped)
|
||||
continue;
|
||||
|
||||
if (!allowsInput) {
|
||||
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
|
||||
const Vector2D size = p->m_pResource ? p->m_pResource->geometry.size() : p->size();
|
||||
const bool HASSURFACE = p->m_resource && p->m_resource->m_surface;
|
||||
|
||||
const auto BOX = CBox{p->coordsGlobal() + offset, size};
|
||||
Vector2D offset = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.pos() : Vector2D{};
|
||||
Vector2D size = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.size() : p->size();
|
||||
|
||||
if (size == Vector2D{})
|
||||
size = p->size();
|
||||
|
||||
const auto BOX = CBox{p->coordsGlobal() + offset, size};
|
||||
if (BOX.containsPoint(globalCoords))
|
||||
return p;
|
||||
} else {
|
||||
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
|
||||
const auto REGION =
|
||||
CRegion{p->m_pWLSurface->resource()->current.input}.intersect(CBox{{}, p->m_pWLSurface->resource()->current.size}).translate(p->coordsGlobal() + offset);
|
||||
const auto REGION = CRegion{p->m_wlSurface->resource()->m_current.input}.intersect(CBox{{}, p->m_wlSurface->resource()->m_current.size}).translate(p->coordsGlobal());
|
||||
if (REGION.containsPoint(globalCoords))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CPopup::inert() const {
|
||||
return m_inert;
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "Subsurface.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
|
||||
class CXDGPopupResource;
|
||||
|
||||
class CPopup {
|
||||
public:
|
||||
// dummy head nodes
|
||||
CPopup(PHLWINDOW pOwner);
|
||||
CPopup(PHLLS pOwner);
|
||||
static UP<CPopup> create(PHLWINDOW pOwner);
|
||||
static UP<CPopup> create(PHLLS pOwner);
|
||||
|
||||
// real nodes
|
||||
CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner);
|
||||
static UP<CPopup> create(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
|
||||
|
||||
~CPopup();
|
||||
|
||||
@ -34,35 +34,39 @@ class CPopup {
|
||||
void recheckTree();
|
||||
|
||||
bool visible();
|
||||
bool inert() const;
|
||||
|
||||
// will also loop over this node
|
||||
void breadthfirst(std::function<void(CPopup*, void*)> fn, void* data);
|
||||
CPopup* at(const Vector2D& globalCoords, bool allowsInput = false);
|
||||
void breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data);
|
||||
WP<CPopup> at(const Vector2D& globalCoords, bool allowsInput = false);
|
||||
|
||||
//
|
||||
SP<CWLSurface> m_pWLSurface;
|
||||
bool m_bMapped = false;
|
||||
SP<CWLSurface> m_wlSurface;
|
||||
WP<CPopup> m_self;
|
||||
bool m_mapped = false;
|
||||
|
||||
private:
|
||||
CPopup() = default;
|
||||
|
||||
// T1 owners, each popup has to have one of these
|
||||
PHLWINDOWREF m_pWindowOwner;
|
||||
PHLLSREF m_pLayerOwner;
|
||||
PHLWINDOWREF m_windowOwner;
|
||||
PHLLSREF m_layerOwner;
|
||||
|
||||
// T2 owners
|
||||
CPopup* m_pParent = nullptr;
|
||||
WP<CPopup> m_parent;
|
||||
|
||||
WP<CXDGPopupResource> m_pResource;
|
||||
WP<CXDGPopupResource> m_resource;
|
||||
|
||||
Vector2D m_vLastSize = {};
|
||||
Vector2D m_vLastPos = {};
|
||||
Vector2D m_lastSize = {};
|
||||
Vector2D m_lastPos = {};
|
||||
|
||||
bool m_bRequestedReposition = false;
|
||||
bool m_requestedReposition = false;
|
||||
|
||||
bool m_bInert = false;
|
||||
bool m_inert = false;
|
||||
|
||||
//
|
||||
std::vector<SP<CPopup>> m_vChildren;
|
||||
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
||||
std::vector<UP<CPopup>> m_children;
|
||||
UP<CSubsurface> m_subsurfaceHead;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener newPopup;
|
||||
@ -72,7 +76,7 @@ class CPopup {
|
||||
CHyprSignalListener commit;
|
||||
CHyprSignalListener dismissed;
|
||||
CHyprSignalListener reposition;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
|
||||
void initAllSignals();
|
||||
void reposition();
|
||||
@ -81,5 +85,5 @@ class CPopup {
|
||||
|
||||
Vector2D localToGlobal(const Vector2D& rel);
|
||||
Vector2D t1ParentCoords();
|
||||
static void bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data);
|
||||
static void bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data);
|
||||
};
|
||||
|
22
src/desktop/Rule.cpp
Normal file
22
src/desktop/Rule.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include <re2/re2.h>
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "Rule.hpp"
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) {
|
||||
const bool NEGATIVE = regex_.starts_with("negative:");
|
||||
|
||||
m_negative = NEGATIVE;
|
||||
m_regex = makeUnique<RE2>(NEGATIVE ? regex_.substr(9) : regex_);
|
||||
|
||||
// TODO: maybe pop an error?
|
||||
if (!m_regex->ok())
|
||||
Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_);
|
||||
}
|
||||
|
||||
bool CRuleRegexContainer::passes(const std::string& str) const {
|
||||
if (!m_regex)
|
||||
return false;
|
||||
|
||||
return RE2::FullMatch(str, *m_regex) != m_negative;
|
||||
}
|
21
src/desktop/Rule.hpp
Normal file
21
src/desktop/Rule.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
namespace re2 {
|
||||
class RE2;
|
||||
};
|
||||
|
||||
class CRuleRegexContainer {
|
||||
public:
|
||||
CRuleRegexContainer() = default;
|
||||
|
||||
CRuleRegexContainer(const std::string& regex);
|
||||
|
||||
bool passes(const std::string& str) const;
|
||||
|
||||
private:
|
||||
Hyprutils::Memory::CUniquePointer<re2::RE2> m_regex;
|
||||
bool m_negative = false;
|
||||
};
|
@ -4,49 +4,66 @@
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/core/Subcompositor.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
|
||||
CSubsurface::CSubsurface(PHLWINDOW pOwner) : m_pWindowParent(pOwner) {
|
||||
initSignals();
|
||||
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
|
||||
UP<CSubsurface> CSubsurface::create(PHLWINDOW pOwner) {
|
||||
auto subsurface = UP<CSubsurface>(new CSubsurface());
|
||||
subsurface->m_windowParent = pOwner;
|
||||
subsurface->m_self = subsurface;
|
||||
|
||||
subsurface->initSignals();
|
||||
subsurface->initExistingSubsurfaces(pOwner->m_wlSurface->resource());
|
||||
return subsurface;
|
||||
}
|
||||
|
||||
CSubsurface::CSubsurface(CPopup* pOwner) : m_pPopupParent(pOwner) {
|
||||
initSignals();
|
||||
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
|
||||
UP<CSubsurface> CSubsurface::create(WP<CPopup> pOwner) {
|
||||
auto subsurface = UP<CSubsurface>(new CSubsurface());
|
||||
subsurface->m_popupParent = pOwner;
|
||||
subsurface->m_self = subsurface;
|
||||
subsurface->initSignals();
|
||||
subsurface->initExistingSubsurfaces(pOwner->m_wlSurface->resource());
|
||||
return subsurface;
|
||||
}
|
||||
|
||||
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) {
|
||||
m_pWLSurface = CWLSurface::create();
|
||||
m_pWLSurface->assign(pSubsurface->surface.lock(), this);
|
||||
initSignals();
|
||||
initExistingSubsurfaces(pSubsurface->surface.lock());
|
||||
UP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner) {
|
||||
auto subsurface = UP<CSubsurface>(new CSubsurface());
|
||||
subsurface->m_windowParent = pOwner;
|
||||
subsurface->m_subsurface = pSubsurface;
|
||||
subsurface->m_self = subsurface;
|
||||
subsurface->m_wlSurface = CWLSurface::create();
|
||||
subsurface->m_wlSurface->assign(pSubsurface->m_surface.lock(), subsurface.get());
|
||||
subsurface->initSignals();
|
||||
subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock());
|
||||
return subsurface;
|
||||
}
|
||||
|
||||
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, CPopup* pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) {
|
||||
m_pWLSurface = CWLSurface::create();
|
||||
m_pWLSurface->assign(pSubsurface->surface.lock(), this);
|
||||
initSignals();
|
||||
initExistingSubsurfaces(pSubsurface->surface.lock());
|
||||
}
|
||||
|
||||
CSubsurface::~CSubsurface() {
|
||||
;
|
||||
UP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner) {
|
||||
auto subsurface = UP<CSubsurface>(new CSubsurface());
|
||||
subsurface->m_popupParent = pOwner;
|
||||
subsurface->m_subsurface = pSubsurface;
|
||||
subsurface->m_self = subsurface;
|
||||
subsurface->m_wlSurface = CWLSurface::create();
|
||||
subsurface->m_wlSurface->assign(pSubsurface->m_surface.lock(), subsurface.get());
|
||||
subsurface->initSignals();
|
||||
subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock());
|
||||
return subsurface;
|
||||
}
|
||||
|
||||
void CSubsurface::initSignals() {
|
||||
if (m_pSubsurface) {
|
||||
listeners.commitSubsurface = m_pSubsurface->surface->events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||
listeners.destroySubsurface = m_pSubsurface->events.destroy.registerListener([this](std::any d) { onDestroy(); });
|
||||
listeners.mapSubsurface = m_pSubsurface->surface->events.map.registerListener([this](std::any d) { onMap(); });
|
||||
listeners.unmapSubsurface = m_pSubsurface->surface->events.unmap.registerListener([this](std::any d) { onUnmap(); });
|
||||
listeners.newSubsurface =
|
||||
m_pSubsurface->surface->events.newSubsurface.registerListener([this](std::any d) { onNewSubsurface(std::any_cast<SP<CWLSubsurfaceResource>>(d)); });
|
||||
if (m_subsurface) {
|
||||
m_listeners.commitSubsurface = m_subsurface->m_surface->m_events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||
m_listeners.destroySubsurface = m_subsurface->m_events.destroy.registerListener([this](std::any d) { onDestroy(); });
|
||||
m_listeners.mapSubsurface = m_subsurface->m_surface->m_events.map.registerListener([this](std::any d) { onMap(); });
|
||||
m_listeners.unmapSubsurface = m_subsurface->m_surface->m_events.unmap.registerListener([this](std::any d) { onUnmap(); });
|
||||
m_listeners.newSubsurface =
|
||||
m_subsurface->m_surface->m_events.newSubsurface.registerListener([this](std::any d) { onNewSubsurface(std::any_cast<SP<CWLSubsurfaceResource>>(d)); });
|
||||
} else {
|
||||
if (m_pWindowParent)
|
||||
listeners.newSubsurface = m_pWindowParent->m_pWLSurface->resource()->events.newSubsurface.registerListener(
|
||||
if (m_windowParent)
|
||||
m_listeners.newSubsurface = m_windowParent->m_wlSurface->resource()->m_events.newSubsurface.registerListener(
|
||||
[this](std::any d) { onNewSubsurface(std::any_cast<SP<CWLSubsurfaceResource>>(d)); });
|
||||
else if (m_pPopupParent)
|
||||
listeners.newSubsurface = m_pPopupParent->m_pWLSurface->resource()->events.newSubsurface.registerListener(
|
||||
else if (m_popupParent)
|
||||
m_listeners.newSubsurface = m_popupParent->m_wlSurface->resource()->m_events.newSubsurface.registerListener(
|
||||
[this](std::any d) { onNewSubsurface(std::any_cast<SP<CWLSubsurfaceResource>>(d)); });
|
||||
else
|
||||
ASSERT(false);
|
||||
@ -54,104 +71,102 @@ void CSubsurface::initSignals() {
|
||||
}
|
||||
|
||||
void CSubsurface::checkSiblingDamage() {
|
||||
if (!m_pParent)
|
||||
if (!m_parent)
|
||||
return; // ??????????
|
||||
|
||||
const double SCALE = m_pWindowParent.lock() && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
|
||||
const double SCALE = m_windowParent.lock() && m_windowParent->m_isX11 ? 1.0 / m_windowParent->m_X11SurfaceScaledBy : 1.0;
|
||||
|
||||
for (auto const& n : m_pParent->m_vChildren) {
|
||||
for (auto const& n : m_parent->m_children) {
|
||||
if (n.get() == this)
|
||||
continue;
|
||||
|
||||
const auto COORDS = n->coordsGlobal();
|
||||
g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y, SCALE);
|
||||
g_pHyprRenderer->damageSurface(n->m_wlSurface->resource(), COORDS.x, COORDS.y, SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
void CSubsurface::recheckDamageForSubsurfaces() {
|
||||
for (auto const& n : m_vChildren) {
|
||||
for (auto const& n : m_children) {
|
||||
const auto COORDS = n->coordsGlobal();
|
||||
g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y);
|
||||
g_pHyprRenderer->damageSurface(n->m_wlSurface->resource(), COORDS.x, COORDS.y);
|
||||
}
|
||||
}
|
||||
|
||||
void CSubsurface::onCommit() {
|
||||
// no damaging if it's not visible
|
||||
if (!m_pWindowParent.expired() && (!m_pWindowParent->m_bIsMapped || !m_pWindowParent->m_pWorkspace->m_bVisible)) {
|
||||
m_vLastSize = m_pWLSurface->resource()->current.size;
|
||||
if (!m_windowParent.expired() && (!m_windowParent->m_isMapped || !m_windowParent->m_workspace->m_visible)) {
|
||||
m_lastSize = m_wlSurface->resource()->m_current.size;
|
||||
|
||||
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
|
||||
if (*PLOGDAMAGE)
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowParent.lock());
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowParent.lock());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
|
||||
g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y);
|
||||
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y);
|
||||
|
||||
if (m_pPopupParent)
|
||||
m_pPopupParent->recheckTree();
|
||||
if (!m_pWindowParent.expired()) // I hate you firefox why are you doing this
|
||||
m_pWindowParent->m_pPopupHead->recheckTree();
|
||||
if (m_popupParent && !m_popupParent->inert() && m_popupParent->m_wlSurface)
|
||||
m_popupParent->recheckTree();
|
||||
if (!m_windowParent.expired()) // I hate you firefox why are you doing this
|
||||
m_windowParent->m_popupHead->recheckTree();
|
||||
|
||||
// I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox)
|
||||
checkSiblingDamage();
|
||||
|
||||
if (m_vLastSize != m_pWLSurface->resource()->current.size) {
|
||||
CBox box{COORDS, m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
m_vLastSize = m_pWLSurface->resource()->current.size;
|
||||
box = {COORDS, m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
if (m_lastSize != m_wlSurface->resource()->m_current.size || m_lastPosition != m_subsurface->m_position) {
|
||||
damageLastArea();
|
||||
m_lastSize = m_wlSurface->resource()->m_current.size;
|
||||
m_lastPosition = m_subsurface->m_position;
|
||||
}
|
||||
}
|
||||
|
||||
void CSubsurface::onDestroy() {
|
||||
// destroy children
|
||||
m_vChildren.clear();
|
||||
m_children.clear();
|
||||
|
||||
m_bInert = true;
|
||||
m_inert = true;
|
||||
|
||||
if (!m_pSubsurface)
|
||||
if (!m_subsurface)
|
||||
return; // dummy node, nothing to do, it's the parent dying
|
||||
|
||||
// kill ourselves
|
||||
std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; });
|
||||
std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; });
|
||||
}
|
||||
|
||||
void CSubsurface::onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface) {
|
||||
CSubsurface* PSUBSURFACE = nullptr;
|
||||
WP<CSubsurface> PSUBSURFACE;
|
||||
|
||||
if (!m_pWindowParent.expired())
|
||||
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pWindowParent.lock())).get();
|
||||
else if (m_pPopupParent)
|
||||
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pPopupParent)).get();
|
||||
if (!m_windowParent.expired())
|
||||
PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_windowParent.lock()));
|
||||
else if (m_popupParent)
|
||||
PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_popupParent));
|
||||
|
||||
PSUBSURFACE->m_self = PSUBSURFACE;
|
||||
|
||||
ASSERT(PSUBSURFACE);
|
||||
|
||||
PSUBSURFACE->m_pParent = this;
|
||||
PSUBSURFACE->m_parent = m_self;
|
||||
}
|
||||
|
||||
void CSubsurface::onMap() {
|
||||
m_vLastSize = m_pWLSurface->resource()->current.size;
|
||||
m_lastSize = m_wlSurface->resource()->m_current.size;
|
||||
m_lastPosition = m_subsurface->m_position;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
CBox box{COORDS, m_vLastSize};
|
||||
CBox box{COORDS, m_lastSize};
|
||||
box.expand(4);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
if (!m_pWindowParent.expired())
|
||||
m_pWindowParent->updateSurfaceScaleTransformDetails();
|
||||
if (!m_windowParent.expired())
|
||||
m_windowParent->updateSurfaceScaleTransformDetails();
|
||||
}
|
||||
|
||||
void CSubsurface::onUnmap() {
|
||||
const auto COORDS = coordsGlobal();
|
||||
CBox box{COORDS, m_vLastSize};
|
||||
box.expand(4);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
damageLastArea();
|
||||
|
||||
if (m_pWLSurface->resource() == g_pCompositor->m_pLastFocus)
|
||||
if (m_wlSurface->resource() == g_pCompositor->m_lastFocus)
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
@ -159,42 +174,49 @@ void CSubsurface::onUnmap() {
|
||||
// TODO: should this remove children? Currently it won't, only on .destroy
|
||||
}
|
||||
|
||||
void CSubsurface::damageLastArea() {
|
||||
const auto COORDS = coordsGlobal() + m_lastPosition - m_subsurface->m_position;
|
||||
CBox box{COORDS, m_lastSize};
|
||||
box.expand(4);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
}
|
||||
|
||||
Vector2D CSubsurface::coordsRelativeToParent() {
|
||||
if (!m_pSubsurface)
|
||||
if (!m_subsurface)
|
||||
return {};
|
||||
return m_pSubsurface->posRelativeToParent();
|
||||
return m_subsurface->posRelativeToParent();
|
||||
}
|
||||
|
||||
Vector2D CSubsurface::coordsGlobal() {
|
||||
Vector2D coords = coordsRelativeToParent();
|
||||
|
||||
if (!m_pWindowParent.expired())
|
||||
coords += m_pWindowParent->m_vRealPosition.value();
|
||||
else if (m_pPopupParent)
|
||||
coords += m_pPopupParent->coordsGlobal();
|
||||
if (!m_windowParent.expired())
|
||||
coords += m_windowParent->m_realPosition->value();
|
||||
else if (m_popupParent)
|
||||
coords += m_popupParent->coordsGlobal();
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
void CSubsurface::initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface) {
|
||||
for (auto const& s : pSurface->subsurfaces) {
|
||||
if (!s || s->surface->hlSurface /* already assigned */)
|
||||
for (auto const& s : pSurface->m_subsurfaces) {
|
||||
if (!s || s->m_surface->m_hlSurface /* already assigned */)
|
||||
continue;
|
||||
onNewSubsurface(s.lock());
|
||||
}
|
||||
}
|
||||
|
||||
Vector2D CSubsurface::size() {
|
||||
return m_pWLSurface->resource()->current.size;
|
||||
return m_wlSurface->resource()->m_current.size;
|
||||
}
|
||||
|
||||
bool CSubsurface::visible() {
|
||||
if (!m_pWindowParent.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_pWindowParent.lock());
|
||||
if (m_pPopupParent)
|
||||
return m_pPopupParent->visible();
|
||||
if (m_pParent)
|
||||
return m_pParent->visible();
|
||||
if (!m_windowParent.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowParent.lock());
|
||||
if (m_popupParent)
|
||||
return m_popupParent->visible();
|
||||
if (m_parent)
|
||||
return m_parent->visible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -10,54 +10,60 @@ class CWLSubsurfaceResource;
|
||||
class CSubsurface {
|
||||
public:
|
||||
// root dummy nodes
|
||||
CSubsurface(PHLWINDOW pOwner);
|
||||
CSubsurface(CPopup* pOwner);
|
||||
static UP<CSubsurface> create(PHLWINDOW pOwner);
|
||||
static UP<CSubsurface> create(WP<CPopup> pOwner);
|
||||
|
||||
// real nodes
|
||||
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
|
||||
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, CPopup* pOwner);
|
||||
static UP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
|
||||
static UP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner);
|
||||
|
||||
~CSubsurface();
|
||||
~CSubsurface() = default;
|
||||
|
||||
Vector2D coordsRelativeToParent();
|
||||
Vector2D coordsGlobal();
|
||||
Vector2D coordsRelativeToParent();
|
||||
Vector2D coordsGlobal();
|
||||
|
||||
Vector2D size();
|
||||
Vector2D size();
|
||||
|
||||
void onCommit();
|
||||
void onDestroy();
|
||||
void onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface);
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
void onCommit();
|
||||
void onDestroy();
|
||||
void onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface);
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
|
||||
bool visible();
|
||||
bool visible();
|
||||
|
||||
void recheckDamageForSubsurfaces();
|
||||
void recheckDamageForSubsurfaces();
|
||||
|
||||
WP<CSubsurface> m_self;
|
||||
|
||||
private:
|
||||
CSubsurface() = default;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroySubsurface;
|
||||
CHyprSignalListener commitSubsurface;
|
||||
CHyprSignalListener mapSubsurface;
|
||||
CHyprSignalListener unmapSubsurface;
|
||||
CHyprSignalListener newSubsurface;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
|
||||
WP<CWLSubsurfaceResource> m_pSubsurface;
|
||||
SP<CWLSurface> m_pWLSurface;
|
||||
Vector2D m_vLastSize = {};
|
||||
WP<CWLSubsurfaceResource> m_subsurface;
|
||||
SP<CWLSurface> m_wlSurface;
|
||||
Vector2D m_lastSize = {};
|
||||
Vector2D m_lastPosition = {};
|
||||
|
||||
// if nullptr, means it's a dummy node
|
||||
CSubsurface* m_pParent = nullptr;
|
||||
WP<CSubsurface> m_parent;
|
||||
|
||||
PHLWINDOWREF m_pWindowParent;
|
||||
CPopup* m_pPopupParent = nullptr;
|
||||
PHLWINDOWREF m_windowParent;
|
||||
WP<CPopup> m_popupParent;
|
||||
|
||||
std::vector<std::unique_ptr<CSubsurface>> m_vChildren;
|
||||
std::vector<UP<CSubsurface>> m_children;
|
||||
|
||||
bool m_bInert = false;
|
||||
bool m_inert = false;
|
||||
|
||||
void initSignals();
|
||||
void initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface);
|
||||
void checkSiblingDamage();
|
||||
};
|
||||
void initSignals();
|
||||
void initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface);
|
||||
void checkSiblingDamage();
|
||||
void damageLastArea();
|
||||
};
|
||||
|
@ -1,40 +1,42 @@
|
||||
#include "WLSurface.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "LayerSurface.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface) {
|
||||
m_pResource = pSurface;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
}
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface, PHLWINDOW pOwner) {
|
||||
m_pWindowOwner = pOwner;
|
||||
m_pResource = pSurface;
|
||||
m_windowOwner = pOwner;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
}
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface, PHLLS pOwner) {
|
||||
m_pLayerOwner = pOwner;
|
||||
m_pResource = pSurface;
|
||||
m_layerOwner = pOwner;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
}
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface, CSubsurface* pOwner) {
|
||||
m_pSubsurfaceOwner = pOwner;
|
||||
m_pResource = pSurface;
|
||||
m_subsurfaceOwner = pOwner;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
}
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface, CPopup* pOwner) {
|
||||
m_pPopupOwner = pOwner;
|
||||
m_pResource = pSurface;
|
||||
m_popupOwner = pOwner;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
}
|
||||
|
||||
void CWLSurface::unassign() {
|
||||
@ -46,66 +48,67 @@ CWLSurface::~CWLSurface() {
|
||||
}
|
||||
|
||||
bool CWLSurface::exists() const {
|
||||
return m_pResource;
|
||||
return m_resource;
|
||||
}
|
||||
|
||||
SP<CWLSurfaceResource> CWLSurface::resource() const {
|
||||
return m_pResource.lock();
|
||||
return m_resource.lock();
|
||||
}
|
||||
|
||||
bool CWLSurface::small() const {
|
||||
if (!validMapped(m_pWindowOwner) || !exists())
|
||||
if (!validMapped(m_windowOwner) || !exists())
|
||||
return false;
|
||||
|
||||
if (!m_pResource->current.texture)
|
||||
if (!m_resource->m_current.texture)
|
||||
return false;
|
||||
|
||||
const auto O = m_pWindowOwner.lock();
|
||||
const auto O = m_windowOwner.lock();
|
||||
|
||||
return O->m_vReportedSize.x > m_pResource->current.size.x + 1 || O->m_vReportedSize.y > m_pResource->current.size.y + 1;
|
||||
return O->m_reportedSize.x > m_resource->m_current.size.x + 1 || O->m_reportedSize.y > m_resource->m_current.size.y + 1;
|
||||
}
|
||||
|
||||
Vector2D CWLSurface::correctSmallVec() const {
|
||||
if (!validMapped(m_pWindowOwner) || !exists() || !small() || m_bFillIgnoreSmall)
|
||||
if (!validMapped(m_windowOwner) || !exists() || !small() || m_fillIgnoreSmall)
|
||||
return {};
|
||||
|
||||
const auto SIZE = getViewporterCorrectedSize();
|
||||
const auto O = m_pWindowOwner.lock();
|
||||
const auto O = m_windowOwner.lock();
|
||||
|
||||
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize.value() / O->m_vReportedSize);
|
||||
return Vector2D{(O->m_reportedSize.x - SIZE.x) / 2, (O->m_reportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_realSize->value() / O->m_reportedSize);
|
||||
}
|
||||
|
||||
Vector2D CWLSurface::correctSmallVecBuf() const {
|
||||
if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.texture)
|
||||
if (!exists() || !small() || m_fillIgnoreSmall || !m_resource->m_current.texture)
|
||||
return {};
|
||||
|
||||
const auto SIZE = getViewporterCorrectedSize();
|
||||
const auto BS = m_pResource->current.bufferSize;
|
||||
const auto BS = m_resource->m_current.bufferSize;
|
||||
|
||||
return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY});
|
||||
}
|
||||
|
||||
Vector2D CWLSurface::getViewporterCorrectedSize() const {
|
||||
if (!exists() || !m_pResource->current.texture)
|
||||
if (!exists() || !m_resource->m_current.texture)
|
||||
return {};
|
||||
|
||||
return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.bufferSize;
|
||||
return m_resource->m_current.viewport.hasDestination ? m_resource->m_current.viewport.destination : m_resource->m_current.bufferSize;
|
||||
}
|
||||
|
||||
CRegion CWLSurface::computeDamage() const {
|
||||
if (!m_pResource->current.texture)
|
||||
if (!m_resource->m_current.texture)
|
||||
return {};
|
||||
|
||||
CRegion damage = m_pResource->accumulateCurrentBufferDamage();
|
||||
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
|
||||
CRegion damage = m_resource->m_current.accumulateBufferDamage();
|
||||
damage.transform(wlTransformToHyprutils(m_resource->m_current.transform), m_resource->m_current.bufferSize.x, m_resource->m_current.bufferSize.y);
|
||||
|
||||
const auto BUFSIZE = m_pResource->current.bufferSize;
|
||||
const auto BUFSIZE = m_resource->m_current.bufferSize;
|
||||
const auto CORRECTVEC = correctSmallVecBuf();
|
||||
|
||||
if (m_pResource->current.viewport.hasSource)
|
||||
damage.intersect(m_pResource->current.viewport.source);
|
||||
if (m_resource->m_current.viewport.hasSource)
|
||||
damage.intersect(m_resource->m_current.viewport.source);
|
||||
|
||||
const auto SCALEDSRCSIZE = m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.bufferSize;
|
||||
const auto SCALEDSRCSIZE =
|
||||
m_resource->m_current.viewport.hasSource ? m_resource->m_current.viewport.source.size() * m_resource->m_current.scale : m_resource->m_current.bufferSize;
|
||||
|
||||
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
|
||||
damage.translate(CORRECTVEC);
|
||||
@ -113,120 +116,120 @@ CRegion CWLSurface::computeDamage() const {
|
||||
// go from buffer coords in the damage to hl logical
|
||||
|
||||
const auto BOX = getSurfaceBoxGlobal();
|
||||
const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.bufferSize :
|
||||
Vector2D{1.0 / m_pResource->current.scale, 1.0 / m_pResource->current.scale /* Wrong... but we can't really do better */};
|
||||
const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_resource->m_current.bufferSize :
|
||||
Vector2D{1.0 / m_resource->m_current.scale, 1.0 / m_resource->m_current.scale /* Wrong... but we can't really do better */};
|
||||
|
||||
damage.scale(SCALE);
|
||||
|
||||
if (m_pWindowOwner)
|
||||
damage.scale(m_pWindowOwner->m_fX11SurfaceScaledBy); // fix xwayland:force_zero_scaling stuff that will be fucked by the above a bit
|
||||
if (m_windowOwner)
|
||||
damage.scale(m_windowOwner->m_X11SurfaceScaledBy); // fix xwayland:force_zero_scaling stuff that will be fucked by the above a bit
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
void CWLSurface::destroy() {
|
||||
if (!m_pResource)
|
||||
if (!m_resource)
|
||||
return;
|
||||
|
||||
events.destroy.emit();
|
||||
m_events.destroy.emit();
|
||||
|
||||
m_pConstraint.reset();
|
||||
m_constraint.reset();
|
||||
|
||||
listeners.destroy.reset();
|
||||
m_pResource->hlSurface.reset();
|
||||
m_pWindowOwner.reset();
|
||||
m_pLayerOwner.reset();
|
||||
m_pPopupOwner = nullptr;
|
||||
m_pSubsurfaceOwner = nullptr;
|
||||
m_bInert = true;
|
||||
m_listeners.destroy.reset();
|
||||
m_resource->m_hlSurface.reset();
|
||||
m_windowOwner.reset();
|
||||
m_layerOwner.reset();
|
||||
m_popupOwner = nullptr;
|
||||
m_subsurfaceOwner = nullptr;
|
||||
m_inert = true;
|
||||
|
||||
if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf && g_pHyprRenderer->m_sLastCursorData.surf->get() == this)
|
||||
g_pHyprRenderer->m_sLastCursorData.surf.reset();
|
||||
if (g_pHyprRenderer && g_pHyprRenderer->m_lastCursorData.surf && g_pHyprRenderer->m_lastCursorData.surf->get() == this)
|
||||
g_pHyprRenderer->m_lastCursorData.surf.reset();
|
||||
|
||||
m_pResource.reset();
|
||||
m_resource.reset();
|
||||
|
||||
Debug::log(LOG, "CWLSurface {:x} called destroy()", (uintptr_t)this);
|
||||
}
|
||||
|
||||
void CWLSurface::init() {
|
||||
if (!m_pResource)
|
||||
if (!m_resource)
|
||||
return;
|
||||
|
||||
RASSERT(!m_pResource->hlSurface, "Attempted to duplicate CWLSurface ownership!");
|
||||
RASSERT(!m_resource->m_hlSurface, "Attempted to duplicate CWLSurface ownership!");
|
||||
|
||||
m_pResource->hlSurface = self.lock();
|
||||
m_resource->m_hlSurface = m_self.lock();
|
||||
|
||||
listeners.destroy = m_pResource->events.destroy.registerListener([this](std::any d) { destroy(); });
|
||||
m_listeners.destroy = m_resource->m_events.destroy.registerListener([this](std::any d) { destroy(); });
|
||||
|
||||
Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this);
|
||||
}
|
||||
|
||||
PHLWINDOW CWLSurface::getWindow() const {
|
||||
return m_pWindowOwner.lock();
|
||||
return m_windowOwner.lock();
|
||||
}
|
||||
|
||||
PHLLS CWLSurface::getLayer() const {
|
||||
return m_pLayerOwner.lock();
|
||||
return m_layerOwner.lock();
|
||||
}
|
||||
|
||||
CPopup* CWLSurface::getPopup() const {
|
||||
return m_pPopupOwner;
|
||||
return m_popupOwner;
|
||||
}
|
||||
|
||||
CSubsurface* CWLSurface::getSubsurface() const {
|
||||
return m_pSubsurfaceOwner;
|
||||
return m_subsurfaceOwner;
|
||||
}
|
||||
|
||||
bool CWLSurface::desktopComponent() const {
|
||||
return !m_pLayerOwner.expired() || !m_pWindowOwner.expired() || m_pSubsurfaceOwner || m_pPopupOwner;
|
||||
return !m_layerOwner.expired() || !m_windowOwner.expired() || m_subsurfaceOwner || m_popupOwner;
|
||||
}
|
||||
|
||||
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() const {
|
||||
if (!desktopComponent())
|
||||
return {};
|
||||
|
||||
if (!m_pWindowOwner.expired())
|
||||
return m_pWindowOwner->getWindowMainSurfaceBox();
|
||||
if (!m_pLayerOwner.expired())
|
||||
return m_pLayerOwner->geometry;
|
||||
if (m_pPopupOwner)
|
||||
return CBox{m_pPopupOwner->coordsGlobal(), m_pPopupOwner->size()};
|
||||
if (m_pSubsurfaceOwner)
|
||||
return CBox{m_pSubsurfaceOwner->coordsGlobal(), m_pSubsurfaceOwner->size()};
|
||||
if (!m_windowOwner.expired())
|
||||
return m_windowOwner->getWindowMainSurfaceBox();
|
||||
if (!m_layerOwner.expired())
|
||||
return m_layerOwner->m_geometry;
|
||||
if (m_popupOwner)
|
||||
return CBox{m_popupOwner->coordsGlobal(), m_popupOwner->size()};
|
||||
if (m_subsurfaceOwner)
|
||||
return CBox{m_subsurfaceOwner->coordsGlobal(), m_subsurfaceOwner->size()};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CWLSurface::appendConstraint(WP<CPointerConstraint> constraint) {
|
||||
m_pConstraint = constraint;
|
||||
m_constraint = constraint;
|
||||
}
|
||||
|
||||
SP<CPointerConstraint> CWLSurface::constraint() const {
|
||||
return m_pConstraint.lock();
|
||||
return m_constraint.lock();
|
||||
}
|
||||
|
||||
bool CWLSurface::visible() {
|
||||
if (!m_pWindowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_pWindowOwner.lock());
|
||||
if (!m_pLayerOwner.expired())
|
||||
if (!m_windowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
|
||||
if (!m_layerOwner.expired())
|
||||
return true;
|
||||
if (m_pPopupOwner)
|
||||
return m_pPopupOwner->visible();
|
||||
if (m_pSubsurfaceOwner)
|
||||
return m_pSubsurfaceOwner->visible();
|
||||
if (m_popupOwner)
|
||||
return m_popupOwner->visible();
|
||||
if (m_subsurfaceOwner)
|
||||
return m_subsurfaceOwner->visible();
|
||||
return true; // non-desktop, we don't know much.
|
||||
}
|
||||
|
||||
SP<CWLSurface> CWLSurface::fromResource(SP<CWLSurfaceResource> pSurface) {
|
||||
if (!pSurface)
|
||||
return nullptr;
|
||||
return pSurface->hlSurface.lock();
|
||||
return pSurface->m_hlSurface.lock();
|
||||
}
|
||||
|
||||
bool CWLSurface::keyboardFocusable() const {
|
||||
if (m_pWindowOwner || m_pPopupOwner || m_pSubsurfaceOwner)
|
||||
if (m_windowOwner || m_popupOwner || m_subsurfaceOwner)
|
||||
return true;
|
||||
if (m_pLayerOwner && m_pLayerOwner->layerSurface)
|
||||
return m_pLayerOwner->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
if (m_layerOwner && m_layerOwner->m_layerSurface)
|
||||
return m_layerOwner->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
return false;
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ class CWLSurfaceResource;
|
||||
class CWLSurface {
|
||||
public:
|
||||
static SP<CWLSurface> create() {
|
||||
auto p = SP<CWLSurface>(new CWLSurface);
|
||||
p->self = p;
|
||||
auto p = SP<CWLSurface>(new CWLSurface);
|
||||
p->m_self = p;
|
||||
return p;
|
||||
}
|
||||
~CWLSurface();
|
||||
@ -53,17 +53,17 @@ class CWLSurface {
|
||||
SP<CPointerConstraint> constraint() const;
|
||||
|
||||
// allow stretching. Useful for plugins.
|
||||
bool m_bFillIgnoreSmall = false;
|
||||
bool m_fillIgnoreSmall = false;
|
||||
|
||||
// track surface data and avoid dupes
|
||||
float m_fLastScale = 0;
|
||||
int m_iLastScale = 0;
|
||||
wl_output_transform m_eLastTransform = (wl_output_transform)-1;
|
||||
float m_lastScaleFloat = 0;
|
||||
int m_lastScaleInt = 0;
|
||||
wl_output_transform m_lastTransform = (wl_output_transform)-1;
|
||||
|
||||
//
|
||||
CWLSurface& operator=(SP<CWLSurfaceResource> pSurface) {
|
||||
destroy();
|
||||
m_pResource = pSurface;
|
||||
m_resource = pSurface;
|
||||
init();
|
||||
|
||||
return *this;
|
||||
@ -84,28 +84,32 @@ class CWLSurface {
|
||||
static SP<CWLSurface> fromResource(SP<CWLSurfaceResource> pSurface);
|
||||
|
||||
// used by the alpha-modifier protocol
|
||||
float m_pAlphaModifier = 1.F;
|
||||
float m_alphaModifier = 1.F;
|
||||
|
||||
// used by the hyprland-surface protocol
|
||||
float m_overallOpacity = 1.F;
|
||||
CRegion m_visibleRegion;
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
} events;
|
||||
} m_events;
|
||||
|
||||
WP<CWLSurface> self;
|
||||
WP<CWLSurface> m_self;
|
||||
|
||||
private:
|
||||
CWLSurface() = default;
|
||||
|
||||
bool m_bInert = true;
|
||||
bool m_inert = true;
|
||||
|
||||
WP<CWLSurfaceResource> m_pResource;
|
||||
WP<CWLSurfaceResource> m_resource;
|
||||
|
||||
PHLWINDOWREF m_pWindowOwner;
|
||||
PHLLSREF m_pLayerOwner;
|
||||
CPopup* m_pPopupOwner = nullptr;
|
||||
CSubsurface* m_pSubsurfaceOwner = nullptr;
|
||||
PHLWINDOWREF m_windowOwner;
|
||||
PHLLSREF m_layerOwner;
|
||||
CPopup* m_popupOwner = nullptr;
|
||||
CSubsurface* m_subsurfaceOwner = nullptr;
|
||||
|
||||
//
|
||||
WP<CPointerConstraint> m_pConstraint;
|
||||
WP<CPointerConstraint> m_constraint;
|
||||
|
||||
void destroy();
|
||||
void init();
|
||||
@ -113,7 +117,8 @@ class CWLSurface {
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroy;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
|
||||
friend class CPointerConstraint;
|
||||
};
|
||||
friend class CXxColorManagerV4;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,22 +2,23 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "../config/ConfigDataValues.hpp"
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/AnimatedVariable.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../helpers/TagKeeper.hpp"
|
||||
#include "../macros.hpp"
|
||||
#include "../managers/XWaylandManager.hpp"
|
||||
#include "../render/decorations/IHyprWindowDecoration.hpp"
|
||||
#include "../render/Transformer.hpp"
|
||||
#include "DesktopTypes.hpp"
|
||||
#include "Popup.hpp"
|
||||
#include "Subsurface.hpp"
|
||||
#include "WLSurface.hpp"
|
||||
#include "Workspace.hpp"
|
||||
#include "WindowRule.hpp"
|
||||
#include "WindowOverridableVar.hpp"
|
||||
#include "../protocols/types/ContentType.hpp"
|
||||
|
||||
class CXDGSurfaceResource;
|
||||
class CXWaylandSurface;
|
||||
@ -42,13 +43,15 @@ enum eGroupRules : uint8_t {
|
||||
};
|
||||
|
||||
enum eGetWindowProperties : uint8_t {
|
||||
WINDOW_ONLY = 0,
|
||||
RESERVED_EXTENTS = 1 << 0,
|
||||
INPUT_EXTENTS = 1 << 1,
|
||||
FULL_EXTENTS = 1 << 2,
|
||||
FLOATING_ONLY = 1 << 3,
|
||||
ALLOW_FLOATING = 1 << 4,
|
||||
USE_PROP_TILED = 1 << 5,
|
||||
WINDOW_ONLY = 0,
|
||||
RESERVED_EXTENTS = 1 << 0,
|
||||
INPUT_EXTENTS = 1 << 1,
|
||||
FULL_EXTENTS = 1 << 2,
|
||||
FLOATING_ONLY = 1 << 3,
|
||||
ALLOW_FLOATING = 1 << 4,
|
||||
USE_PROP_TILED = 1 << 5,
|
||||
SKIP_FULLSCREEN_PRIORITY = 1 << 6,
|
||||
FOCUS_PRIORITY = 1 << 7,
|
||||
};
|
||||
|
||||
enum eSuppressEvents : uint8_t {
|
||||
@ -57,108 +60,27 @@ enum eSuppressEvents : uint8_t {
|
||||
SUPPRESS_MAXIMIZE = 1 << 1,
|
||||
SUPPRESS_ACTIVATE = 1 << 2,
|
||||
SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3,
|
||||
SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4,
|
||||
};
|
||||
|
||||
class IWindowTransformer;
|
||||
|
||||
struct SAlphaValue {
|
||||
float m_fAlpha;
|
||||
bool m_bOverride;
|
||||
float alpha;
|
||||
bool overridden;
|
||||
|
||||
float applyAlpha(float alpha) const {
|
||||
if (m_bOverride)
|
||||
return m_fAlpha;
|
||||
float applyAlpha(float a) const {
|
||||
if (overridden)
|
||||
return alpha;
|
||||
else
|
||||
return m_fAlpha * alpha;
|
||||
return alpha * a;
|
||||
};
|
||||
};
|
||||
|
||||
enum eOverridePriority : uint8_t {
|
||||
PRIORITY_LAYOUT = 0,
|
||||
PRIORITY_WORKSPACE_RULE,
|
||||
PRIORITY_WINDOW_RULE,
|
||||
PRIORITY_SET_PROP,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CWindowOverridableVar {
|
||||
public:
|
||||
CWindowOverridableVar(T const& value, eOverridePriority priority) {
|
||||
values[priority] = value;
|
||||
}
|
||||
CWindowOverridableVar(T const& value) : defaultValue{value} {}
|
||||
|
||||
CWindowOverridableVar() = default;
|
||||
~CWindowOverridableVar() = default;
|
||||
|
||||
CWindowOverridableVar<T>& operator=(CWindowOverridableVar<T> const& other) {
|
||||
// Self-assignment check
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
for (auto const& value : other.values) {
|
||||
values[value.first] = value.second;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void unset(eOverridePriority priority) {
|
||||
values.erase(priority);
|
||||
}
|
||||
|
||||
bool hasValue() {
|
||||
return !values.empty();
|
||||
}
|
||||
|
||||
T value() {
|
||||
if (!values.empty())
|
||||
return std::prev(values.end())->second;
|
||||
else
|
||||
throw std::bad_optional_access();
|
||||
}
|
||||
|
||||
T valueOr(T const& other) {
|
||||
if (hasValue())
|
||||
return value();
|
||||
else
|
||||
return other;
|
||||
}
|
||||
|
||||
T valueOrDefault() {
|
||||
return valueOr(defaultValue);
|
||||
}
|
||||
|
||||
eOverridePriority getPriority() {
|
||||
if (!values.empty())
|
||||
return std::prev(values.end())->first;
|
||||
else
|
||||
throw std::bad_optional_access();
|
||||
}
|
||||
|
||||
void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
|
||||
if (optValue.has_value())
|
||||
values[priority] = optValue.value();
|
||||
else
|
||||
unset(priority);
|
||||
}
|
||||
|
||||
operator std::optional<T>() {
|
||||
if (hasValue())
|
||||
return value();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<eOverridePriority, T> values;
|
||||
T defaultValue; // used for toggling, so required for bool
|
||||
};
|
||||
|
||||
struct SWindowData {
|
||||
CWindowOverridableVar<SAlphaValue> alpha = SAlphaValue{1.f, false};
|
||||
CWindowOverridableVar<SAlphaValue> alphaInactive = SAlphaValue{1.f, false};
|
||||
CWindowOverridableVar<SAlphaValue> alphaFullscreen = SAlphaValue{1.f, false};
|
||||
CWindowOverridableVar<SAlphaValue> alpha = SAlphaValue{.alpha = 1.f, .overridden = false};
|
||||
CWindowOverridableVar<SAlphaValue> alphaInactive = SAlphaValue{.alpha = 1.f, .overridden = false};
|
||||
CWindowOverridableVar<SAlphaValue> alphaFullscreen = SAlphaValue{.alpha = 1.f, .overridden = false};
|
||||
|
||||
CWindowOverridableVar<bool> allowsInput = false;
|
||||
CWindowOverridableVar<bool> dimAround = false;
|
||||
@ -181,12 +103,14 @@ struct SWindowData {
|
||||
CWindowOverridableVar<bool> tearing = false;
|
||||
CWindowOverridableVar<bool> xray = false;
|
||||
CWindowOverridableVar<bool> renderUnfocused = false;
|
||||
CWindowOverridableVar<bool> noFollowMouse = false;
|
||||
|
||||
CWindowOverridableVar<int> rounding;
|
||||
CWindowOverridableVar<int> borderSize;
|
||||
CWindowOverridableVar<Hyprlang::INT> borderSize = {std::string("general:border_size"), Hyprlang::INT(0), std::nullopt};
|
||||
CWindowOverridableVar<Hyprlang::INT> rounding = {std::string("decoration:rounding"), Hyprlang::INT(0), std::nullopt};
|
||||
|
||||
CWindowOverridableVar<float> scrollMouse;
|
||||
CWindowOverridableVar<float> scrollTouchpad;
|
||||
CWindowOverridableVar<Hyprlang::FLOAT> roundingPower = {std::string("decoration:rounding_power")};
|
||||
CWindowOverridableVar<Hyprlang::FLOAT> scrollMouse = {std::string("input:scroll_factor")};
|
||||
CWindowOverridableVar<Hyprlang::FLOAT> scrollTouchpad = {std::string("input:touchpad:scroll_factor")};
|
||||
|
||||
CWindowOverridableVar<std::string> animationStyle;
|
||||
CWindowOverridableVar<Vector2D> maxSize;
|
||||
@ -194,6 +118,8 @@ struct SWindowData {
|
||||
|
||||
CWindowOverridableVar<CGradientValueData> activeBorderColor;
|
||||
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
|
||||
|
||||
CWindowOverridableVar<bool> persistentSize;
|
||||
};
|
||||
|
||||
struct SInitialWorkspaceToken {
|
||||
@ -218,151 +144,151 @@ class CWindow {
|
||||
public:
|
||||
~CWindow();
|
||||
|
||||
SP<CWLSurface> m_pWLSurface;
|
||||
SP<CWLSurface> m_wlSurface;
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
} events;
|
||||
} m_events;
|
||||
|
||||
WP<CXDGSurfaceResource> m_pXDGSurface;
|
||||
WP<CXWaylandSurface> m_pXWaylandSurface;
|
||||
WP<CXDGSurfaceResource> m_xdgSurface;
|
||||
WP<CXWaylandSurface> m_xwaylandSurface;
|
||||
|
||||
// this is the position and size of the "bounding box"
|
||||
Vector2D m_vPosition = Vector2D(0, 0);
|
||||
Vector2D m_vSize = Vector2D(0, 0);
|
||||
Vector2D m_position = Vector2D(0, 0);
|
||||
Vector2D m_size = Vector2D(0, 0);
|
||||
|
||||
// this is the real position and size used to draw the thing
|
||||
CAnimatedVariable<Vector2D> m_vRealPosition;
|
||||
CAnimatedVariable<Vector2D> m_vRealSize;
|
||||
PHLANIMVAR<Vector2D> m_realPosition;
|
||||
PHLANIMVAR<Vector2D> m_realSize;
|
||||
|
||||
// for not spamming the protocols
|
||||
Vector2D m_vReportedPosition;
|
||||
Vector2D m_vReportedSize;
|
||||
Vector2D m_vPendingReportedSize;
|
||||
std::optional<std::pair<uint32_t, Vector2D>> m_pPendingSizeAck;
|
||||
std::vector<std::pair<uint32_t, Vector2D>> m_vPendingSizeAcks;
|
||||
Vector2D m_reportedPosition;
|
||||
Vector2D m_reportedSize;
|
||||
Vector2D m_pendingReportedSize;
|
||||
std::optional<std::pair<uint32_t, Vector2D>> m_pendingSizeAck;
|
||||
std::vector<std::pair<uint32_t, Vector2D>> m_pendingSizeAcks;
|
||||
|
||||
// for restoring floating statuses
|
||||
Vector2D m_vLastFloatingSize;
|
||||
Vector2D m_vLastFloatingPosition;
|
||||
Vector2D m_lastFloatingSize;
|
||||
Vector2D m_lastFloatingPosition;
|
||||
|
||||
// for floating window offset in workspace animations
|
||||
Vector2D m_vFloatingOffset = Vector2D(0, 0);
|
||||
Vector2D m_floatingOffset = Vector2D(0, 0);
|
||||
|
||||
// this is used for pseudotiling
|
||||
bool m_bIsPseudotiled = false;
|
||||
Vector2D m_vPseudoSize = Vector2D(1280, 720);
|
||||
bool m_isPseudotiled = false;
|
||||
Vector2D m_pseudoSize = Vector2D(1280, 720);
|
||||
|
||||
// for recovering relative cursor position
|
||||
Vector2D m_vRelativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
|
||||
Vector2D m_relativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
|
||||
|
||||
bool m_bFirstMap = false; // for layouts
|
||||
bool m_bIsFloating = false;
|
||||
bool m_bDraggingTiled = false; // for dragging around tiled windows
|
||||
bool m_bWasMaximized = false;
|
||||
SFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
|
||||
std::string m_szTitle = "";
|
||||
std::string m_szClass = "";
|
||||
std::string m_szInitialTitle = "";
|
||||
std::string m_szInitialClass = "";
|
||||
PHLWORKSPACE m_pWorkspace;
|
||||
PHLMONITORREF m_pMonitor;
|
||||
bool m_firstMap = false; // for layouts
|
||||
bool m_isFloating = false;
|
||||
bool m_draggingTiled = false; // for dragging around tiled windows
|
||||
SFullscreenState m_fullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
|
||||
std::string m_title = "";
|
||||
std::string m_class = "";
|
||||
std::string m_initialTitle = "";
|
||||
std::string m_initialClass = "";
|
||||
PHLWORKSPACE m_workspace;
|
||||
PHLMONITORREF m_monitor;
|
||||
|
||||
bool m_bIsMapped = false;
|
||||
bool m_isMapped = false;
|
||||
|
||||
bool m_bRequestsFloat = false;
|
||||
bool m_requestsFloat = false;
|
||||
|
||||
// This is for fullscreen apps
|
||||
bool m_bCreatedOverFullscreen = false;
|
||||
bool m_createdOverFullscreen = false;
|
||||
|
||||
// XWayland stuff
|
||||
bool m_bIsX11 = false;
|
||||
PHLWINDOWREF m_pX11Parent;
|
||||
bool m_bX11DoesntWantBorders = false;
|
||||
bool m_bX11ShouldntFocus = false;
|
||||
float m_fX11SurfaceScaledBy = 1.f;
|
||||
bool m_isX11 = false;
|
||||
bool m_X11DoesntWantBorders = false;
|
||||
bool m_X11ShouldntFocus = false;
|
||||
float m_X11SurfaceScaledBy = 1.f;
|
||||
//
|
||||
|
||||
// For nofocus
|
||||
bool m_bNoInitialFocus = false;
|
||||
bool m_noInitialFocus = false;
|
||||
|
||||
// Fullscreen and Maximize
|
||||
bool m_bWantsInitialFullscreen = false;
|
||||
bool m_wantsInitialFullscreen = false;
|
||||
MONITORID m_wantsInitialFullscreenMonitor = MONITOR_INVALID;
|
||||
|
||||
// bitfield eSuppressEvents
|
||||
uint64_t m_eSuppressedEvents = SUPPRESS_NONE;
|
||||
// bitfield suppressEvents
|
||||
uint64_t m_suppressedEvents = SUPPRESS_NONE;
|
||||
|
||||
// desktop components
|
||||
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
||||
std::unique_ptr<CPopup> m_pPopupHead;
|
||||
UP<CSubsurface> m_subsurfaceHead;
|
||||
UP<CPopup> m_popupHead;
|
||||
|
||||
// Animated border
|
||||
CGradientValueData m_cRealBorderColor = {0};
|
||||
CGradientValueData m_cRealBorderColorPrevious = {0};
|
||||
CAnimatedVariable<float> m_fBorderFadeAnimationProgress;
|
||||
CAnimatedVariable<float> m_fBorderAngleAnimationProgress;
|
||||
CGradientValueData m_realBorderColor = {0};
|
||||
CGradientValueData m_realBorderColorPrevious = {0};
|
||||
PHLANIMVAR<float> m_borderFadeAnimationProgress;
|
||||
PHLANIMVAR<float> m_borderAngleAnimationProgress;
|
||||
|
||||
// Fade in-out
|
||||
CAnimatedVariable<float> m_fAlpha;
|
||||
bool m_bFadingOut = false;
|
||||
bool m_bReadyToDelete = false;
|
||||
Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in
|
||||
Vector2D m_vOriginalClosedSize; // drawing the closing animations
|
||||
SBoxExtents m_eOriginalClosedExtents;
|
||||
bool m_bAnimatingIn = false;
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
bool m_fadingOut = false;
|
||||
bool m_readyToDelete = false;
|
||||
Vector2D m_originalClosedPos; // these will be used for calculations later on in
|
||||
Vector2D m_originalClosedSize; // drawing the closing animations
|
||||
SBoxExtents m_originalClosedExtents;
|
||||
bool m_animatingIn = false;
|
||||
|
||||
// For pinned (sticky) windows
|
||||
bool m_bPinned = false;
|
||||
bool m_pinned = false;
|
||||
|
||||
// For preserving pinned state when fullscreening a pinned window
|
||||
bool m_bPinFullscreened = false;
|
||||
bool m_pinFullscreened = false;
|
||||
|
||||
// urgency hint
|
||||
bool m_bIsUrgent = false;
|
||||
bool m_isUrgent = false;
|
||||
|
||||
// for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window.
|
||||
PHLWINDOWREF m_pLastCycledWindow;
|
||||
PHLWINDOWREF m_lastCycledWindow;
|
||||
|
||||
// Window decorations
|
||||
// TODO: make this a SP.
|
||||
std::vector<std::unique_ptr<IHyprWindowDecoration>> m_dWindowDecorations;
|
||||
std::vector<IHyprWindowDecoration*> m_vDecosToRemove;
|
||||
std::vector<UP<IHyprWindowDecoration>> m_windowDecorations;
|
||||
std::vector<IHyprWindowDecoration*> m_decosToRemove;
|
||||
|
||||
// Special render data, rules, etc
|
||||
SWindowData m_sWindowData;
|
||||
SWindowData m_windowData;
|
||||
|
||||
// Transformers
|
||||
std::vector<std::unique_ptr<IWindowTransformer>> m_vTransformers;
|
||||
std::vector<UP<IWindowTransformer>> m_transformers;
|
||||
|
||||
// for alpha
|
||||
CAnimatedVariable<float> m_fActiveInactiveAlpha;
|
||||
PHLANIMVAR<float> m_activeInactiveAlpha;
|
||||
PHLANIMVAR<float> m_movingFromWorkspaceAlpha;
|
||||
|
||||
// animated shadow color
|
||||
CAnimatedVariable<CHyprColor> m_cRealShadowColor;
|
||||
PHLANIMVAR<CHyprColor> m_realShadowColor;
|
||||
|
||||
// animated tint
|
||||
CAnimatedVariable<float> m_fDimPercent;
|
||||
PHLANIMVAR<float> m_dimPercent;
|
||||
|
||||
// animate moving to an invisible workspace
|
||||
int m_iMonitorMovedFrom = -1; // -1 means not moving
|
||||
CAnimatedVariable<float> m_fMovingToWorkspaceAlpha;
|
||||
int m_monitorMovedFrom = -1; // -1 means not moving
|
||||
PHLANIMVAR<float> m_movingToWorkspaceAlpha;
|
||||
|
||||
// swallowing
|
||||
PHLWINDOWREF m_pSwallowed;
|
||||
bool m_bGroupSwallowed = false;
|
||||
PHLWINDOWREF m_swallowed;
|
||||
bool m_currentlySwallowed = false;
|
||||
bool m_groupSwallowed = false;
|
||||
|
||||
// focus stuff
|
||||
bool m_bStayFocused = false;
|
||||
bool m_stayFocused = false;
|
||||
|
||||
// for toplevel monitor events
|
||||
MONITORID m_iLastToplevelMonitorID = -1;
|
||||
MONITORID m_iLastSurfaceMonitorID = -1;
|
||||
MONITORID m_lastSurfaceMonitorID = -1;
|
||||
|
||||
// for idle inhibiting windows
|
||||
eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE;
|
||||
eIdleInhibitMode m_idleInhibitMode = IDLEINHIBIT_NONE;
|
||||
|
||||
// initial token. Will be unregistered on workspace change or timeout of 2 minutes
|
||||
std::string m_szInitialWorkspaceToken = "";
|
||||
std::string m_initialWorkspaceToken = "";
|
||||
|
||||
// for groups
|
||||
struct SGroupData {
|
||||
@ -370,102 +296,123 @@ class CWindow {
|
||||
bool head = false;
|
||||
bool locked = false; // per group lock
|
||||
bool deny = false; // deny window from enter a group or made a group
|
||||
} m_sGroupData;
|
||||
uint16_t m_eGroupRules = GROUP_NONE;
|
||||
} m_groupData;
|
||||
uint16_t m_groupRules = GROUP_NONE;
|
||||
|
||||
bool m_bTearingHint = false;
|
||||
bool m_tearingHint = false;
|
||||
|
||||
// stores the currently matched window rules
|
||||
std::vector<SP<CWindowRule>> m_vMatchedRules;
|
||||
std::vector<SP<CWindowRule>> m_matchedRules;
|
||||
|
||||
// window tags
|
||||
CTagKeeper m_tags;
|
||||
|
||||
// ANR
|
||||
PHLANIMVAR<float> m_notRespondingTint;
|
||||
|
||||
// For the noclosefor windowrule
|
||||
Time::steady_tp m_closeableSince = Time::steadyNow();
|
||||
|
||||
// For the list lookup
|
||||
bool operator==(const CWindow& rhs) const {
|
||||
return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
|
||||
m_bFadingOut == rhs.m_bFadingOut;
|
||||
return m_xdgSurface == rhs.m_xdgSurface && m_xwaylandSurface == rhs.m_xwaylandSurface && m_position == rhs.m_position && m_size == rhs.m_size &&
|
||||
m_fadingOut == rhs.m_fadingOut;
|
||||
}
|
||||
|
||||
// methods
|
||||
CBox getFullWindowBoundingBox();
|
||||
SBoxExtents getFullWindowExtents();
|
||||
CBox getWindowBoxUnified(uint64_t props);
|
||||
CBox getWindowIdealBoundingBoxIgnoreReserved();
|
||||
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
|
||||
void updateWindowDecos();
|
||||
void removeWindowDeco(IHyprWindowDecoration* deco);
|
||||
void uncacheWindowDecos();
|
||||
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
|
||||
pid_t getPID();
|
||||
IHyprWindowDecoration* getDecorationByType(eDecorationType);
|
||||
void removeDecorationByType(eDecorationType);
|
||||
void updateToplevel();
|
||||
void updateSurfaceScaleTransformDetails(bool force = false);
|
||||
void moveToWorkspace(PHLWORKSPACE);
|
||||
PHLWINDOW x11TransientFor();
|
||||
void onUnmap();
|
||||
void onMap();
|
||||
void setHidden(bool hidden);
|
||||
bool isHidden();
|
||||
void applyDynamicRule(const SP<CWindowRule>& r);
|
||||
void updateDynamicRules();
|
||||
SBoxExtents getFullWindowReservedArea();
|
||||
Vector2D middle();
|
||||
bool opaque();
|
||||
float rounding();
|
||||
bool canBeTorn();
|
||||
void setSuspended(bool suspend);
|
||||
bool visibleOnMonitor(PHLMONITOR pMonitor);
|
||||
WORKSPACEID workspaceID();
|
||||
MONITORID monitorID();
|
||||
bool onSpecialWorkspace();
|
||||
void activate(bool force = false);
|
||||
int surfacesCount();
|
||||
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
|
||||
bool isFullscreen();
|
||||
bool isEffectiveInternalFSMode(const eFullscreenMode);
|
||||
int getRealBorderSize();
|
||||
float getScrollMouse();
|
||||
float getScrollTouchpad();
|
||||
void updateWindowData();
|
||||
void updateWindowData(const struct SWorkspaceRule&);
|
||||
void onBorderAngleAnimEnd(void* ptr);
|
||||
bool isInCurvedCorner(double x, double y);
|
||||
bool hasPopupAt(const Vector2D& pos);
|
||||
int popupsCount();
|
||||
void applyGroupRules();
|
||||
void createGroup();
|
||||
void destroyGroup();
|
||||
PHLWINDOW getGroupHead();
|
||||
PHLWINDOW getGroupTail();
|
||||
PHLWINDOW getGroupCurrent();
|
||||
PHLWINDOW getGroupPrevious();
|
||||
PHLWINDOW getGroupWindowByIndex(int);
|
||||
int getGroupSize();
|
||||
bool canBeGroupedInto(PHLWINDOW pWindow);
|
||||
void setGroupCurrent(PHLWINDOW pWindow);
|
||||
void insertWindowToGroup(PHLWINDOW pWindow);
|
||||
void updateGroupOutputs();
|
||||
void switchWithWindowInGroup(PHLWINDOW pWindow);
|
||||
void setAnimationsToMove();
|
||||
void onWorkspaceAnimUpdate();
|
||||
void onUpdateState();
|
||||
void onUpdateMeta();
|
||||
void onX11Configure(CBox box);
|
||||
void onResourceChangeX11();
|
||||
std::string fetchTitle();
|
||||
std::string fetchClass();
|
||||
void warpCursor(bool force = false);
|
||||
PHLWINDOW getSwallower();
|
||||
void unsetWindowData(eOverridePriority priority);
|
||||
bool isX11OverrideRedirect();
|
||||
bool isModal();
|
||||
Vector2D requestedMinSize();
|
||||
Vector2D requestedMaxSize();
|
||||
CBox getFullWindowBoundingBox();
|
||||
SBoxExtents getFullWindowExtents();
|
||||
CBox getWindowBoxUnified(uint64_t props);
|
||||
CBox getWindowIdealBoundingBoxIgnoreReserved();
|
||||
void addWindowDeco(UP<IHyprWindowDecoration> deco);
|
||||
void updateWindowDecos();
|
||||
void removeWindowDeco(IHyprWindowDecoration* deco);
|
||||
void uncacheWindowDecos();
|
||||
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
|
||||
pid_t getPID();
|
||||
IHyprWindowDecoration* getDecorationByType(eDecorationType);
|
||||
void updateToplevel();
|
||||
void updateSurfaceScaleTransformDetails(bool force = false);
|
||||
void moveToWorkspace(PHLWORKSPACE);
|
||||
PHLWINDOW x11TransientFor();
|
||||
void onUnmap();
|
||||
void onMap();
|
||||
void setHidden(bool hidden);
|
||||
bool isHidden();
|
||||
void applyDynamicRule(const SP<CWindowRule>& r);
|
||||
void updateDynamicRules();
|
||||
SBoxExtents getFullWindowReservedArea();
|
||||
Vector2D middle();
|
||||
bool opaque();
|
||||
float rounding();
|
||||
float roundingPower();
|
||||
bool canBeTorn();
|
||||
void setSuspended(bool suspend);
|
||||
bool visibleOnMonitor(PHLMONITOR pMonitor);
|
||||
WORKSPACEID workspaceID();
|
||||
MONITORID monitorID();
|
||||
bool onSpecialWorkspace();
|
||||
void activate(bool force = false);
|
||||
int surfacesCount();
|
||||
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
|
||||
bool isFullscreen();
|
||||
bool isEffectiveInternalFSMode(const eFullscreenMode);
|
||||
int getRealBorderSize();
|
||||
float getScrollMouse();
|
||||
float getScrollTouchpad();
|
||||
void updateWindowData();
|
||||
void updateWindowData(const struct SWorkspaceRule&);
|
||||
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
|
||||
bool isInCurvedCorner(double x, double y);
|
||||
bool hasPopupAt(const Vector2D& pos);
|
||||
int popupsCount();
|
||||
void applyGroupRules();
|
||||
void createGroup();
|
||||
void destroyGroup();
|
||||
PHLWINDOW getGroupHead();
|
||||
PHLWINDOW getGroupTail();
|
||||
PHLWINDOW getGroupCurrent();
|
||||
PHLWINDOW getGroupPrevious();
|
||||
PHLWINDOW getGroupWindowByIndex(int);
|
||||
int getGroupSize();
|
||||
bool canBeGroupedInto(PHLWINDOW pWindow);
|
||||
void setGroupCurrent(PHLWINDOW pWindow);
|
||||
void insertWindowToGroup(PHLWINDOW pWindow);
|
||||
void updateGroupOutputs();
|
||||
void switchWithWindowInGroup(PHLWINDOW pWindow);
|
||||
void setAnimationsToMove();
|
||||
void onWorkspaceAnimUpdate();
|
||||
void onFocusAnimUpdate();
|
||||
void onUpdateState();
|
||||
void onUpdateMeta();
|
||||
void onX11ConfigureRequest(CBox box);
|
||||
void onResourceChangeX11();
|
||||
std::string fetchTitle();
|
||||
std::string fetchClass();
|
||||
void warpCursor(bool force = false);
|
||||
PHLWINDOW getSwallower();
|
||||
void unsetWindowData(eOverridePriority priority);
|
||||
bool isX11OverrideRedirect();
|
||||
bool isModal();
|
||||
Vector2D requestedMinSize();
|
||||
Vector2D requestedMaxSize();
|
||||
Vector2D realToReportSize();
|
||||
Vector2D realToReportPosition();
|
||||
Vector2D xwaylandSizeToReal(Vector2D size);
|
||||
Vector2D xwaylandPositionToReal(Vector2D size);
|
||||
void updateX11SurfaceScale();
|
||||
void sendWindowSize(bool force = false);
|
||||
NContentType::eContentType getContentType();
|
||||
void setContentType(NContentType::eContentType contentType);
|
||||
void deactivateGroupMembers();
|
||||
bool isNotResponding();
|
||||
std::optional<std::string> xdgTag();
|
||||
std::optional<std::string> xdgDescription();
|
||||
PHLWINDOW parent();
|
||||
bool priorityFocus();
|
||||
|
||||
CBox getWindowMainSurfaceBox() const {
|
||||
return {m_vRealPosition.value().x, m_vRealPosition.value().y, m_vRealSize.value().x, m_vRealSize.value().y};
|
||||
CBox getWindowMainSurfaceBox() const {
|
||||
return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y};
|
||||
}
|
||||
|
||||
// listeners
|
||||
@ -475,7 +422,7 @@ class CWindow {
|
||||
std::unordered_map<std::string, std::string> getEnv();
|
||||
|
||||
//
|
||||
PHLWINDOWREF m_pSelf;
|
||||
PHLWINDOWREF m_self;
|
||||
|
||||
// make private once we move listeners to inside CWindow
|
||||
struct {
|
||||
@ -485,18 +432,18 @@ class CWindow {
|
||||
CHyprSignalListener commit;
|
||||
CHyprSignalListener destroy;
|
||||
CHyprSignalListener activate;
|
||||
CHyprSignalListener configure;
|
||||
CHyprSignalListener configureRequest;
|
||||
CHyprSignalListener setGeometry;
|
||||
CHyprSignalListener updateState;
|
||||
CHyprSignalListener updateMetadata;
|
||||
CHyprSignalListener resourceChange;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
|
||||
private:
|
||||
// For hidden windows and stuff
|
||||
bool m_bHidden = false;
|
||||
bool m_bSuspended = false;
|
||||
WORKSPACEID m_iLastWorkspace = WORKSPACE_INVALID;
|
||||
bool m_hidden = false;
|
||||
bool m_suspended = false;
|
||||
WORKSPACEID m_lastWorkspace = WORKSPACE_INVALID;
|
||||
};
|
||||
|
||||
inline bool valid(PHLWINDOW w) {
|
||||
@ -510,15 +457,52 @@ inline bool valid(PHLWINDOWREF w) {
|
||||
inline bool validMapped(PHLWINDOW w) {
|
||||
if (!valid(w))
|
||||
return false;
|
||||
return w->m_bIsMapped;
|
||||
return w->m_isMapped;
|
||||
}
|
||||
|
||||
inline bool validMapped(PHLWINDOWREF w) {
|
||||
if (!valid(w))
|
||||
return false;
|
||||
return w->m_bIsMapped;
|
||||
return w->m_isMapped;
|
||||
}
|
||||
|
||||
namespace NWindowProperties {
|
||||
static const std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> boolWindowProperties = {
|
||||
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.allowsInput; }},
|
||||
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.dimAround; }},
|
||||
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.decorate; }},
|
||||
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.focusOnActivate; }},
|
||||
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.keepAspectRatio; }},
|
||||
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.nearestNeighbor; }},
|
||||
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noAnim; }},
|
||||
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBlur; }},
|
||||
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBorder; }},
|
||||
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noDim; }},
|
||||
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFocus; }},
|
||||
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noMaxSize; }},
|
||||
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noRounding; }},
|
||||
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShadow; }},
|
||||
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShortcutsInhibit; }},
|
||||
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.opaque; }},
|
||||
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.RGBX; }},
|
||||
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.syncFullscreen; }},
|
||||
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.tearing; }},
|
||||
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.xray; }},
|
||||
{"nofollowmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFollowMouse; }},
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::INT>*(const PHLWINDOW&)>> intWindowProperties = {
|
||||
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.rounding; }},
|
||||
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.borderSize; }},
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::FLOAT>*(PHLWINDOW)>> floatWindowProperties = {
|
||||
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.roundingPower; }},
|
||||
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollMouse; }},
|
||||
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollTouchpad; }},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
format specification
|
||||
- 'x', only address, equivalent of (uintpr_t)CWindow*
|
||||
@ -549,13 +533,13 @@ struct std::formatter<PHLWINDOW, CharT> : std::formatter<CharT> {
|
||||
return std::format_to(out, "[Window nullptr]");
|
||||
|
||||
std::format_to(out, "[");
|
||||
std::format_to(out, "Window {:x}: title: \"{}\"", (uintptr_t)w.get(), w->m_szTitle);
|
||||
std::format_to(out, "Window {:x}: title: \"{}\"", (uintptr_t)w.get(), w->m_title);
|
||||
if (formatWorkspace)
|
||||
std::format_to(out, ", workspace: {}", w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID);
|
||||
std::format_to(out, ", workspace: {}", w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID);
|
||||
if (formatMonitor)
|
||||
std::format_to(out, ", monitor: {}", w->monitorID());
|
||||
if (formatClass)
|
||||
std::format_to(out, ", class: {}", w->m_szClass);
|
||||
std::format_to(out, ", class: {}", w->m_class);
|
||||
return std::format_to(out, "]");
|
||||
}
|
||||
};
|
||||
|
132
src/desktop/WindowOverridableVar.hpp
Normal file
132
src/desktop/WindowOverridableVar.hpp
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <any>
|
||||
#include "../config/ConfigValue.hpp"
|
||||
|
||||
enum eOverridePriority : uint8_t {
|
||||
PRIORITY_LAYOUT = 0,
|
||||
PRIORITY_WORKSPACE_RULE,
|
||||
PRIORITY_WINDOW_RULE,
|
||||
PRIORITY_SET_PROP,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
|
||||
return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
|
||||
}
|
||||
|
||||
template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
|
||||
class CWindowOverridableVar {
|
||||
public:
|
||||
CWindowOverridableVar(T const& value, eOverridePriority priority) {
|
||||
m_values[priority] = value;
|
||||
}
|
||||
|
||||
CWindowOverridableVar(T const& value) : m_defaultValue{value} {}
|
||||
CWindowOverridableVar(T const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
|
||||
CWindowOverridableVar(std::string const& value)
|
||||
requires(Extended && !std::is_same_v<T, bool>)
|
||||
: m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
|
||||
CWindowOverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
|
||||
requires(Extended && !std::is_same_v<T, bool>)
|
||||
: m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
|
||||
|
||||
CWindowOverridableVar() = default;
|
||||
~CWindowOverridableVar() = default;
|
||||
|
||||
CWindowOverridableVar& operator=(CWindowOverridableVar<T> const& other) {
|
||||
// Self-assignment check
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
for (auto const& value : other.m_values) {
|
||||
if constexpr (Extended && !std::is_same_v<T, bool>)
|
||||
m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue);
|
||||
else
|
||||
m_values[value.first] = value.second;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void unset(eOverridePriority priority) {
|
||||
m_values.erase(priority);
|
||||
}
|
||||
|
||||
bool hasValue() {
|
||||
return !m_values.empty();
|
||||
}
|
||||
|
||||
T value() {
|
||||
if (!m_values.empty())
|
||||
return std::prev(m_values.end())->second;
|
||||
else
|
||||
throw std::bad_optional_access();
|
||||
}
|
||||
|
||||
T valueOr(T const& other) {
|
||||
if (hasValue())
|
||||
return value();
|
||||
else
|
||||
return other;
|
||||
}
|
||||
|
||||
T valueOrDefault()
|
||||
requires(Extended && !std::is_same_v<T, bool>)
|
||||
{
|
||||
if (hasValue())
|
||||
return value();
|
||||
else if (m_defaultValue.has_value())
|
||||
return m_defaultValue.value();
|
||||
else
|
||||
return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
|
||||
}
|
||||
|
||||
T valueOrDefault()
|
||||
requires(!Extended || std::is_same_v<T, bool>)
|
||||
{
|
||||
if (hasValue())
|
||||
return value();
|
||||
else if (!m_defaultValue.has_value())
|
||||
throw std::bad_optional_access();
|
||||
else
|
||||
return m_defaultValue.value();
|
||||
}
|
||||
|
||||
eOverridePriority getPriority() {
|
||||
if (!m_values.empty())
|
||||
return std::prev(m_values.end())->first;
|
||||
else
|
||||
throw std::bad_optional_access();
|
||||
}
|
||||
|
||||
void increment(T const& other, eOverridePriority priority) {
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
m_values[priority] = valueOr(false) ^ other;
|
||||
else
|
||||
m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
|
||||
}
|
||||
|
||||
void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
|
||||
if (optValue.has_value())
|
||||
m_values[priority] = optValue.value();
|
||||
else
|
||||
unset(priority);
|
||||
}
|
||||
|
||||
operator std::optional<T>() {
|
||||
if (hasValue())
|
||||
return value();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<eOverridePriority, T> m_values;
|
||||
std::optional<T> m_defaultValue; // used for toggling, so required for bool
|
||||
std::optional<T> m_minValue;
|
||||
std::optional<T> m_maxValue;
|
||||
std::any m_configValue; // only there for select variables
|
||||
};
|
@ -1,100 +1,97 @@
|
||||
#include "WindowRule.hpp"
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include <re2/re2.h>
|
||||
#include "../config/ConfigManager.hpp"
|
||||
|
||||
static const auto RULES = std::unordered_set<std::string>{
|
||||
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused",
|
||||
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused", "persistentsize",
|
||||
};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{
|
||||
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", "opacity",
|
||||
"plugin:", "prop", "pseudo", "rounding", "scrollmouse", "scrolltouchpad", "size", "suppressevent", "tag", "workspace", "xray",
|
||||
"animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
|
||||
"move", "noclosefor", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size",
|
||||
"suppressevent", "tag", "workspace", "xray",
|
||||
};
|
||||
|
||||
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
|
||||
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : m_value(value), m_rule(rule), m_v2(isV2), m_execRule(isExecRule) {
|
||||
const auto VALS = CVarList(rule, 2, ' ');
|
||||
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
|
||||
(g_pConfigManager->mbWindowProperties.find(VALS[0]) != g_pConfigManager->mbWindowProperties.end()) ||
|
||||
(g_pConfigManager->miWindowProperties.find(VALS[0]) != g_pConfigManager->miWindowProperties.end()) ||
|
||||
(g_pConfigManager->mfWindowProperties.find(VALS[0]) != g_pConfigManager->mfWindowProperties.end());
|
||||
const bool VALID = RULES.contains(rule) || std::ranges::any_of(RULES_PREFIX, [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
|
||||
(NWindowProperties::boolWindowProperties.contains(VALS[0])) || (NWindowProperties::intWindowProperties.contains(VALS[0])) ||
|
||||
(NWindowProperties::floatWindowProperties.contains(VALS[0]));
|
||||
|
||||
if (!VALID)
|
||||
return;
|
||||
|
||||
if (rule == "float")
|
||||
ruleType = RULE_FLOAT;
|
||||
m_ruleType = RULE_FLOAT;
|
||||
else if (rule == "fullscreen")
|
||||
ruleType = RULE_FULLSCREEN;
|
||||
m_ruleType = RULE_FULLSCREEN;
|
||||
else if (rule == "maximize")
|
||||
ruleType = RULE_MAXIMIZE;
|
||||
m_ruleType = RULE_MAXIMIZE;
|
||||
else if (rule == "noinitialfocus")
|
||||
ruleType = RULE_NOINITIALFOCUS;
|
||||
m_ruleType = RULE_NOINITIALFOCUS;
|
||||
else if (rule == "pin")
|
||||
ruleType = RULE_PIN;
|
||||
m_ruleType = RULE_PIN;
|
||||
else if (rule == "stayfocused")
|
||||
ruleType = RULE_STAYFOCUSED;
|
||||
m_ruleType = RULE_STAYFOCUSED;
|
||||
else if (rule == "tile")
|
||||
ruleType = RULE_TILE;
|
||||
m_ruleType = RULE_TILE;
|
||||
else if (rule == "renderunfocused")
|
||||
ruleType = RULE_RENDERUNFOCUSED;
|
||||
m_ruleType = RULE_RENDERUNFOCUSED;
|
||||
else if (rule == "persistentsize")
|
||||
m_ruleType = RULE_PERSISTENTSIZE;
|
||||
else if (rule.starts_with("animation"))
|
||||
ruleType = RULE_ANIMATION;
|
||||
m_ruleType = RULE_ANIMATION;
|
||||
else if (rule.starts_with("bordercolor"))
|
||||
ruleType = RULE_BORDERCOLOR;
|
||||
else if (rule.starts_with("bordersize"))
|
||||
ruleType = RULE_BORDERSIZE;
|
||||
m_ruleType = RULE_BORDERCOLOR;
|
||||
else if (rule.starts_with("center"))
|
||||
ruleType = RULE_CENTER;
|
||||
m_ruleType = RULE_CENTER;
|
||||
else if (rule.starts_with("fullscreenstate"))
|
||||
ruleType = RULE_FULLSCREENSTATE;
|
||||
m_ruleType = RULE_FULLSCREENSTATE;
|
||||
else if (rule.starts_with("group"))
|
||||
ruleType = RULE_GROUP;
|
||||
m_ruleType = RULE_GROUP;
|
||||
else if (rule.starts_with("idleinhibit"))
|
||||
ruleType = RULE_IDLEINHIBIT;
|
||||
m_ruleType = RULE_IDLEINHIBIT;
|
||||
else if (rule.starts_with("maxsize"))
|
||||
ruleType = RULE_MAXSIZE;
|
||||
m_ruleType = RULE_MAXSIZE;
|
||||
else if (rule.starts_with("minsize"))
|
||||
ruleType = RULE_MINSIZE;
|
||||
m_ruleType = RULE_MINSIZE;
|
||||
else if (rule.starts_with("monitor"))
|
||||
ruleType = RULE_MONITOR;
|
||||
m_ruleType = RULE_MONITOR;
|
||||
else if (rule.starts_with("move"))
|
||||
ruleType = RULE_MOVE;
|
||||
m_ruleType = RULE_MOVE;
|
||||
else if (rule.starts_with("opacity"))
|
||||
ruleType = RULE_OPACITY;
|
||||
m_ruleType = RULE_OPACITY;
|
||||
else if (rule.starts_with("plugin:"))
|
||||
ruleType = RULE_PLUGIN;
|
||||
m_ruleType = RULE_PLUGIN;
|
||||
else if (rule.starts_with("pseudo"))
|
||||
ruleType = RULE_PSEUDO;
|
||||
else if (rule.starts_with("rounding"))
|
||||
ruleType = RULE_ROUNDING;
|
||||
else if (rule.starts_with("scrollmouse"))
|
||||
ruleType = RULE_SCROLLMOUSE;
|
||||
else if (rule.starts_with("scrolltouchpad"))
|
||||
ruleType = RULE_SCROLLTOUCHPAD;
|
||||
m_ruleType = RULE_PSEUDO;
|
||||
else if (rule.starts_with("size"))
|
||||
ruleType = RULE_SIZE;
|
||||
m_ruleType = RULE_SIZE;
|
||||
else if (rule.starts_with("suppressevent"))
|
||||
ruleType = RULE_SUPPRESSEVENT;
|
||||
m_ruleType = RULE_SUPPRESSEVENT;
|
||||
else if (rule.starts_with("tag"))
|
||||
ruleType = RULE_TAG;
|
||||
m_ruleType = RULE_TAG;
|
||||
else if (rule.starts_with("workspace"))
|
||||
ruleType = RULE_WORKSPACE;
|
||||
else if (rule.starts_with("xray"))
|
||||
ruleType = RULE_XRAY;
|
||||
m_ruleType = RULE_WORKSPACE;
|
||||
else if (rule.starts_with("prop"))
|
||||
ruleType = RULE_PROP;
|
||||
m_ruleType = RULE_PROP;
|
||||
else if (rule.starts_with("content"))
|
||||
m_ruleType = RULE_CONTENT;
|
||||
else if (rule.starts_with("noclosefor"))
|
||||
m_ruleType = RULE_NOCLOSEFOR;
|
||||
else {
|
||||
// check if this is a prop.
|
||||
const CVarList VARS(rule, 0, 's', true);
|
||||
if (g_pConfigManager->miWindowProperties.find(VARS[0]) != g_pConfigManager->miWindowProperties.end() ||
|
||||
g_pConfigManager->mbWindowProperties.find(VARS[0]) != g_pConfigManager->mbWindowProperties.end() ||
|
||||
g_pConfigManager->mfWindowProperties.find(VARS[0]) != g_pConfigManager->mfWindowProperties.end()) {
|
||||
*const_cast<std::string*>(&szRule) = "prop " + rule;
|
||||
ruleType = RULE_PROP;
|
||||
Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, szRule);
|
||||
const bool ISPROP = NWindowProperties::intWindowProperties.contains(VARS[0]) || NWindowProperties::boolWindowProperties.contains(VARS[0]) ||
|
||||
NWindowProperties::floatWindowProperties.contains(VARS[0]);
|
||||
if (ISPROP) {
|
||||
*const_cast<std::string*>(&m_rule) = "prop " + rule;
|
||||
m_ruleType = RULE_PROP;
|
||||
Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, m_rule);
|
||||
} else {
|
||||
Debug::log(ERR, "CWindowRule: didn't match a rule that was found valid?!");
|
||||
ruleType = RULE_INVALID;
|
||||
m_ruleType = RULE_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include "Rule.hpp"
|
||||
|
||||
class CWindowRule {
|
||||
public:
|
||||
@ -19,7 +20,6 @@ class CWindowRule {
|
||||
RULE_RENDERUNFOCUSED,
|
||||
RULE_ANIMATION,
|
||||
RULE_BORDERCOLOR,
|
||||
RULE_BORDERSIZE,
|
||||
RULE_CENTER,
|
||||
RULE_FULLSCREENSTATE,
|
||||
RULE_GROUP,
|
||||
@ -31,35 +31,43 @@ class CWindowRule {
|
||||
RULE_OPACITY,
|
||||
RULE_PLUGIN,
|
||||
RULE_PSEUDO,
|
||||
RULE_ROUNDING,
|
||||
RULE_SCROLLMOUSE,
|
||||
RULE_SCROLLTOUCHPAD,
|
||||
RULE_SIZE,
|
||||
RULE_SUPPRESSEVENT,
|
||||
RULE_TAG,
|
||||
RULE_WORKSPACE,
|
||||
RULE_XRAY,
|
||||
RULE_PROP,
|
||||
RULE_CONTENT,
|
||||
RULE_PERSISTENTSIZE,
|
||||
RULE_NOCLOSEFOR,
|
||||
};
|
||||
|
||||
eRuleType ruleType = RULE_INVALID;
|
||||
eRuleType m_ruleType = RULE_INVALID;
|
||||
|
||||
const std::string szValue;
|
||||
const std::string szRule;
|
||||
const bool v2 = false;
|
||||
const bool execRule = false;
|
||||
const std::string m_value;
|
||||
const std::string m_rule;
|
||||
const bool m_v2 = false;
|
||||
const bool m_execRule = false;
|
||||
|
||||
std::string szTitle;
|
||||
std::string szClass;
|
||||
std::string szInitialTitle;
|
||||
std::string szInitialClass;
|
||||
std::string szTag;
|
||||
int bX11 = -1; // -1 means "ANY"
|
||||
int bFloating = -1;
|
||||
int bFullscreen = -1;
|
||||
int bPinned = -1;
|
||||
int bFocus = -1;
|
||||
std::string szFullscreenState = ""; // empty means any
|
||||
std::string szOnWorkspace = ""; // empty means any
|
||||
std::string szWorkspace = ""; // empty means any
|
||||
std::string m_title;
|
||||
std::string m_class;
|
||||
std::string m_initialTitle;
|
||||
std::string m_initialClass;
|
||||
std::string m_tag;
|
||||
int m_X11 = -1; // -1 means "ANY"
|
||||
int m_floating = -1;
|
||||
int m_fullscreen = -1;
|
||||
int m_pinned = -1;
|
||||
int m_focus = -1;
|
||||
std::string m_fullscreenState = ""; // empty means any
|
||||
std::string m_onWorkspace = ""; // empty means any
|
||||
std::string m_workspace = ""; // empty means any
|
||||
std::string m_contentType = ""; // empty means any
|
||||
std::string m_xdgTag = ""; // empty means any
|
||||
|
||||
// precompiled regexes
|
||||
CRuleRegexContainer m_titleRegex;
|
||||
CRuleRegexContainer m_classRegex;
|
||||
CRuleRegexContainer m_initialTitleRegex;
|
||||
CRuleRegexContainer m_initialClassRegex;
|
||||
CRuleRegexContainer m_v1Regex;
|
||||
};
|
@ -1,7 +1,12 @@
|
||||
#include "Workspace.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "managers/AnimationManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
|
||||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
@ -12,87 +17,76 @@ PHLWORKSPACE CWorkspace::create(WORKSPACEID id, PHLMONITOR monitor, std::string
|
||||
}
|
||||
|
||||
CWorkspace::CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) :
|
||||
m_iID(id), m_szName(name), m_pMonitor(monitor), m_bIsSpecialWorkspace(special), m_bWasCreatedEmpty(isEmpty) {
|
||||
m_id(id), m_name(name), m_monitor(monitor), m_isSpecialWorkspace(special), m_wasCreatedEmpty(isEmpty) {
|
||||
;
|
||||
}
|
||||
|
||||
void CWorkspace::init(PHLWORKSPACE self) {
|
||||
m_pSelf = self;
|
||||
m_self = self;
|
||||
|
||||
m_vRenderOffset.create(m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") :
|
||||
g_pConfigManager->getAnimationPropertyConfig("workspacesIn"),
|
||||
self, AVARDAMAGE_ENTIRE);
|
||||
m_fAlpha.create(AVARTYPE_FLOAT,
|
||||
m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") : g_pConfigManager->getAnimationPropertyConfig("workspacesIn"), self,
|
||||
AVARDAMAGE_ENTIRE);
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
|
||||
m_vRenderOffset.registerVar();
|
||||
m_fAlpha.registerVar();
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), m_renderOffset, g_pConfigManager->getAnimationPropertyConfig(m_isSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"),
|
||||
self, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(1.f, m_alpha, g_pConfigManager->getAnimationPropertyConfig(m_isSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self,
|
||||
AVARDAMAGE_ENTIRE);
|
||||
|
||||
const auto RULEFORTHIS = g_pConfigManager->getWorkspaceRuleFor(self);
|
||||
if (RULEFORTHIS.defaultName.has_value())
|
||||
m_szName = RULEFORTHIS.defaultName.value();
|
||||
m_name = RULEFORTHIS.defaultName.value();
|
||||
|
||||
m_pFocusedWindowHook = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
m_focusedWindowHook = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
const auto PWINDOW = std::any_cast<PHLWINDOW>(param);
|
||||
|
||||
if (PWINDOW == m_pLastFocusedWindow.lock())
|
||||
m_pLastFocusedWindow.reset();
|
||||
if (PWINDOW == m_lastFocusedWindow.lock())
|
||||
m_lastFocusedWindow.reset();
|
||||
});
|
||||
|
||||
m_bInert = false;
|
||||
m_inert = false;
|
||||
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
|
||||
m_bPersistent = WORKSPACERULE.isPersistent;
|
||||
m_persistent = WORKSPACERULE.isPersistent;
|
||||
|
||||
if (self->m_bWasCreatedEmpty)
|
||||
if (self->m_wasCreatedEmpty)
|
||||
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
|
||||
g_pKeybindManager->spawnWithRules(*cmd, self);
|
||||
CKeybindManager::spawnWithRules(*cmd, self);
|
||||
|
||||
g_pEventManager->postEvent({"createworkspace", m_szName});
|
||||
g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)});
|
||||
g_pEventManager->postEvent({.event = "createworkspace", .data = m_name});
|
||||
g_pEventManager->postEvent({.event = "createworkspacev2", .data = std::format("{},{}", m_id, m_name)});
|
||||
EMIT_HOOK_EVENT("createWorkspace", this);
|
||||
}
|
||||
|
||||
SWorkspaceIDName CWorkspace::getPrevWorkspaceIDName(bool perMonitor) const {
|
||||
if (perMonitor)
|
||||
return m_sPrevWorkspacePerMonitor;
|
||||
|
||||
return m_sPrevWorkspace;
|
||||
SWorkspaceIDName CWorkspace::getPrevWorkspaceIDName() const {
|
||||
return m_prevWorkspace;
|
||||
}
|
||||
|
||||
CWorkspace::~CWorkspace() {
|
||||
m_vRenderOffset.unregister();
|
||||
|
||||
Debug::log(LOG, "Destroying workspace ID {}", m_iID);
|
||||
Debug::log(LOG, "Destroying workspace ID {}", m_id);
|
||||
|
||||
// check if g_pHookSystem and g_pEventManager exist, they might be destroyed as in when the compositor is closing.
|
||||
if (g_pHookSystem)
|
||||
g_pHookSystem->unhook(m_pFocusedWindowHook);
|
||||
g_pHookSystem->unhook(m_focusedWindowHook);
|
||||
|
||||
if (g_pEventManager) {
|
||||
g_pEventManager->postEvent({"destroyworkspace", m_szName});
|
||||
g_pEventManager->postEvent({"destroyworkspacev2", std::format("{},{}", m_iID, m_szName)});
|
||||
g_pEventManager->postEvent({.event = "destroyworkspace", .data = m_name});
|
||||
g_pEventManager->postEvent({.event = "destroyworkspacev2", .data = std::format("{},{}", m_id, m_name)});
|
||||
EMIT_HOOK_EVENT("destroyWorkspace", this);
|
||||
}
|
||||
}
|
||||
|
||||
void CWorkspace::startAnim(bool in, bool left, bool instant) {
|
||||
if (!instant) {
|
||||
const std::string ANIMNAME = std::format("{}{}", m_bIsSpecialWorkspace ? "specialWorkspace" : "workspaces", in ? "In" : "Out");
|
||||
const std::string ANIMNAME = std::format("{}{}", m_isSpecialWorkspace ? "specialWorkspace" : "workspaces", in ? "In" : "Out");
|
||||
|
||||
m_fAlpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
|
||||
m_vRenderOffset.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
|
||||
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
|
||||
m_renderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
|
||||
}
|
||||
|
||||
const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle;
|
||||
const auto ANIMSTYLE = m_alpha->getStyle();
|
||||
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
|
||||
|
||||
// set floating windows offset callbacks
|
||||
m_vRenderOffset.setUpdateCallback([&](void*) {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (!validMapped(w) || w->workspaceID() != m_iID)
|
||||
m_renderOffset->setUpdateCallback([&](auto) {
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (!validMapped(w) || w->workspaceID() != m_id)
|
||||
continue;
|
||||
|
||||
w->onWorkspaceAnimUpdate();
|
||||
@ -100,7 +94,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
|
||||
});
|
||||
|
||||
if (ANIMSTYLE.starts_with("slidefade")) {
|
||||
const auto PMONITOR = m_pMonitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
float movePerc = 100.f;
|
||||
|
||||
if (ANIMSTYLE.find('%') != std::string::npos) {
|
||||
@ -110,84 +104,84 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
|
||||
} catch (std::exception& e) { Debug::log(ERR, "Error in startAnim: invalid percentage"); }
|
||||
}
|
||||
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
|
||||
m_alpha->setValueAndWarp(1.f);
|
||||
m_renderOffset->setValueAndWarp(Vector2D(0, 0));
|
||||
|
||||
if (ANIMSTYLE.starts_with("slidefadevert")) {
|
||||
if (in) {
|
||||
m_fAlpha.setValueAndWarp(0.f);
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f)));
|
||||
m_fAlpha = 1.f;
|
||||
m_vRenderOffset = Vector2D(0, 0);
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
m_renderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->m_size.y : -PMONITOR->m_size.y) * (movePerc / 100.f)));
|
||||
*m_alpha = 1.f;
|
||||
*m_renderOffset = Vector2D(0, 0);
|
||||
} else {
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
m_fAlpha = 0.f;
|
||||
m_vRenderOffset = Vector2D(0.0, (left ? -PMONITOR->vecSize.y : PMONITOR->vecSize.y) * (movePerc / 100.f));
|
||||
m_alpha->setValueAndWarp(1.f);
|
||||
*m_alpha = 0.f;
|
||||
*m_renderOffset = Vector2D(0.0, (left ? -PMONITOR->m_size.y : PMONITOR->m_size.y) * (movePerc / 100.f));
|
||||
}
|
||||
} else {
|
||||
if (in) {
|
||||
m_fAlpha.setValueAndWarp(0.f);
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D((left ? PMONITOR->vecSize.x : -PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0));
|
||||
m_fAlpha = 1.f;
|
||||
m_vRenderOffset = Vector2D(0, 0);
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
m_renderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->m_size.x : -PMONITOR->m_size.x) * (movePerc / 100.f), 0.0));
|
||||
*m_alpha = 1.f;
|
||||
*m_renderOffset = Vector2D(0, 0);
|
||||
} else {
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
m_fAlpha = 0.f;
|
||||
m_vRenderOffset = Vector2D((left ? -PMONITOR->vecSize.x : PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0);
|
||||
m_alpha->setValueAndWarp(1.f);
|
||||
*m_alpha = 0.f;
|
||||
*m_renderOffset = Vector2D((left ? -PMONITOR->m_size.x : PMONITOR->m_size.x) * (movePerc / 100.f), 0.0);
|
||||
}
|
||||
}
|
||||
} else if (ANIMSTYLE == "fade") {
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade.
|
||||
m_renderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade.
|
||||
|
||||
if (in) {
|
||||
m_fAlpha.setValueAndWarp(0.f);
|
||||
m_fAlpha = 1.f;
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
*m_alpha = 1.f;
|
||||
} else {
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
m_fAlpha = 0.f;
|
||||
m_alpha->setValueAndWarp(1.f);
|
||||
*m_alpha = 0.f;
|
||||
}
|
||||
} else if (ANIMSTYLE == "slidevert") {
|
||||
// fallback is slide
|
||||
const auto PMONITOR = m_pMonitor.lock();
|
||||
const auto YDISTANCE = PMONITOR->vecSize.y + *PWORKSPACEGAP;
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto YDISTANCE = PMONITOR->m_size.y + *PWORKSPACEGAP;
|
||||
|
||||
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
|
||||
m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
|
||||
|
||||
if (in) {
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE));
|
||||
m_vRenderOffset = Vector2D(0, 0);
|
||||
m_renderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE));
|
||||
*m_renderOffset = Vector2D(0, 0);
|
||||
} else {
|
||||
m_vRenderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE);
|
||||
*m_renderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE);
|
||||
}
|
||||
} else {
|
||||
// fallback is slide
|
||||
const auto PMONITOR = m_pMonitor.lock();
|
||||
const auto XDISTANCE = PMONITOR->vecSize.x + *PWORKSPACEGAP;
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto XDISTANCE = PMONITOR->m_size.x + *PWORKSPACEGAP;
|
||||
|
||||
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
|
||||
m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
|
||||
|
||||
if (in) {
|
||||
m_vRenderOffset.setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0));
|
||||
m_vRenderOffset = Vector2D(0, 0);
|
||||
m_renderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0));
|
||||
*m_renderOffset = Vector2D(0, 0);
|
||||
} else {
|
||||
m_vRenderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0);
|
||||
*m_renderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bIsSpecialWorkspace) {
|
||||
if (m_isSpecialWorkspace) {
|
||||
// required for open/close animations
|
||||
if (in) {
|
||||
m_fAlpha.setValueAndWarp(0.f);
|
||||
m_fAlpha = 1.f;
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
*m_alpha = 1.f;
|
||||
} else {
|
||||
m_fAlpha.setValueAndWarp(1.f);
|
||||
m_fAlpha = 0.f;
|
||||
m_alpha->setValueAndWarp(1.f);
|
||||
*m_alpha = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
if (instant) {
|
||||
m_vRenderOffset.warp();
|
||||
m_fAlpha.warp();
|
||||
m_renderOffset->warp();
|
||||
m_alpha->warp();
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,42 +194,39 @@ void CWorkspace::moveToMonitor(const MONITORID& id) {
|
||||
}
|
||||
|
||||
PHLWINDOW CWorkspace::getLastFocusedWindow() {
|
||||
if (!validMapped(m_pLastFocusedWindow) || m_pLastFocusedWindow->workspaceID() != m_iID)
|
||||
if (!validMapped(m_lastFocusedWindow) || m_lastFocusedWindow->workspaceID() != m_id)
|
||||
return nullptr;
|
||||
|
||||
return m_pLastFocusedWindow.lock();
|
||||
return m_lastFocusedWindow.lock();
|
||||
}
|
||||
|
||||
void CWorkspace::rememberPrevWorkspace(const PHLWORKSPACE& prev) {
|
||||
if (!prev) {
|
||||
m_sPrevWorkspace.id = -1;
|
||||
m_sPrevWorkspace.name = "";
|
||||
m_prevWorkspace.id = -1;
|
||||
m_prevWorkspace.name = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev->m_iID == m_iID) {
|
||||
if (prev->m_id == m_id) {
|
||||
Debug::log(LOG, "Tried to set prev workspace to the same as current one");
|
||||
return;
|
||||
}
|
||||
|
||||
m_sPrevWorkspace.id = prev->m_iID;
|
||||
m_sPrevWorkspace.name = prev->m_szName;
|
||||
m_prevWorkspace.id = prev->m_id;
|
||||
m_prevWorkspace.name = prev->m_name;
|
||||
|
||||
if (prev->m_pMonitor == m_pMonitor) {
|
||||
m_sPrevWorkspacePerMonitor.id = prev->m_iID;
|
||||
m_sPrevWorkspacePerMonitor.name = prev->m_szName;
|
||||
}
|
||||
prev->m_monitor->addPrevWorkspaceID(prev->m_id);
|
||||
}
|
||||
|
||||
std::string CWorkspace::getConfigName() {
|
||||
if (m_bIsSpecialWorkspace) {
|
||||
return m_szName;
|
||||
if (m_isSpecialWorkspace) {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
if (m_iID > 0)
|
||||
return std::to_string(m_iID);
|
||||
if (m_id > 0)
|
||||
return std::to_string(m_id);
|
||||
|
||||
return "name:" + m_szName;
|
||||
return "name:" + m_name;
|
||||
}
|
||||
|
||||
bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
@ -250,12 +241,12 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
if (wsid == WORKSPACE_INVALID)
|
||||
return false;
|
||||
|
||||
return wsid == m_iID;
|
||||
return wsid == m_id;
|
||||
|
||||
} else if (selector.starts_with("name:")) {
|
||||
return m_szName == selector.substr(5);
|
||||
return m_name == selector.substr(5);
|
||||
} else if (selector.starts_with("special")) {
|
||||
return m_szName == selector;
|
||||
return m_name == selector;
|
||||
} else {
|
||||
// parse selector
|
||||
|
||||
@ -270,6 +261,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
// n - named: n[true] or n[s:string] or n[e:string]
|
||||
// m - monitor: m[monitor_selector]
|
||||
// w - windowCount: w[1-4] or w[1], optional flag t or f for tiled or floating and
|
||||
// flag p to count only pinned windows, e.g. w[p1-2], w[pg4]
|
||||
// flag g to count groups instead of windows, e.g. w[t1-2], w[fg4]
|
||||
// flag v will count only visible windows
|
||||
// f - fullscreen state : f[-1], f[0], f[1], or f[2] for different fullscreen states
|
||||
@ -314,7 +306,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::clamp(m_iID, from, to) != m_iID)
|
||||
if (std::clamp(m_id, from, to) != m_id)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
@ -329,7 +321,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
|
||||
const auto SHOULDBESPECIAL = configStringToInt(prop);
|
||||
|
||||
if (SHOULDBESPECIAL && (bool)*SHOULDBESPECIAL != m_bIsSpecialWorkspace)
|
||||
if (SHOULDBESPECIAL && (bool)*SHOULDBESPECIAL != m_isSpecialWorkspace)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
@ -344,7 +336,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromString(prop);
|
||||
|
||||
if (!(PMONITOR ? PMONITOR == m_pMonitor : false))
|
||||
if (!(PMONITOR ? PMONITOR == m_monitor : false))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
@ -357,14 +349,14 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
|
||||
prop = prop.substr(2, prop.length() - 3);
|
||||
|
||||
if (prop.starts_with("s:") && !m_szName.starts_with(prop.substr(2)))
|
||||
if (prop.starts_with("s:") && !m_name.starts_with(prop.substr(2)))
|
||||
return false;
|
||||
if (prop.starts_with("e:") && !m_szName.ends_with(prop.substr(2)))
|
||||
if (prop.starts_with("e:") && !m_name.ends_with(prop.substr(2)))
|
||||
return false;
|
||||
|
||||
const auto WANTSNAMED = configStringToInt(prop);
|
||||
|
||||
if (WANTSNAMED && *WANTSNAMED != (m_iID <= -1337))
|
||||
if (WANTSNAMED && *WANTSNAMED != (m_id <= -1337))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
@ -379,6 +371,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
prop = prop.substr(2, prop.length() - 3);
|
||||
|
||||
int wantsOnlyTiled = -1;
|
||||
int wantsOnlyPinned = false;
|
||||
bool wantsCountGroup = false;
|
||||
bool wantsCountVisible = false;
|
||||
|
||||
@ -390,6 +383,9 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
} else if (flag == 'f' && wantsOnlyTiled == -1) {
|
||||
wantsOnlyTiled = 0;
|
||||
flagCount++;
|
||||
} else if (flag == 'p' && !wantsOnlyPinned) {
|
||||
wantsOnlyPinned = true;
|
||||
flagCount++;
|
||||
} else if (flag == 'g' && !wantsCountGroup) {
|
||||
wantsCountGroup = true;
|
||||
flagCount++;
|
||||
@ -420,9 +416,11 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
int count;
|
||||
if (wantsCountGroup)
|
||||
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
|
||||
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
|
||||
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
|
||||
else
|
||||
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
|
||||
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
|
||||
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
|
||||
|
||||
if (count != from)
|
||||
@ -453,10 +451,12 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
|
||||
WORKSPACEID count;
|
||||
if (wantsCountGroup)
|
||||
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
|
||||
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
|
||||
count =
|
||||
getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
|
||||
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
|
||||
else
|
||||
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
|
||||
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
|
||||
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
|
||||
|
||||
if (std::clamp(count, from, to) != count)
|
||||
@ -481,15 +481,15 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
|
||||
switch (FSSTATE) {
|
||||
case -1: // no fullscreen
|
||||
if (m_bHasFullscreenWindow)
|
||||
if (m_hasFullscreenWindow)
|
||||
return false;
|
||||
break;
|
||||
case 0: // fullscreen full
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_FULLSCREEN)
|
||||
if (!m_hasFullscreenWindow || m_fullscreenMode != FSMODE_FULLSCREEN)
|
||||
return false;
|
||||
break;
|
||||
case 1: // maximized
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_MAXIMIZED)
|
||||
if (!m_hasFullscreenWindow || m_fullscreenMode != FSMODE_MAXIMIZED)
|
||||
return false;
|
||||
break;
|
||||
default: break;
|
||||
@ -509,23 +509,23 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
||||
}
|
||||
|
||||
void CWorkspace::markInert() {
|
||||
m_bInert = true;
|
||||
m_iID = WORKSPACE_INVALID;
|
||||
m_bVisible = false;
|
||||
m_pMonitor.reset();
|
||||
m_inert = true;
|
||||
m_id = WORKSPACE_INVALID;
|
||||
m_visible = false;
|
||||
m_monitor.reset();
|
||||
}
|
||||
|
||||
bool CWorkspace::inert() {
|
||||
return m_bInert;
|
||||
return m_inert;
|
||||
}
|
||||
|
||||
MONITORID CWorkspace::monitorID() {
|
||||
return m_pMonitor ? m_pMonitor->ID : MONITOR_INVALID;
|
||||
return m_monitor ? m_monitor->m_id : MONITOR_INVALID;
|
||||
}
|
||||
|
||||
PHLWINDOW CWorkspace::getFullscreenWindow() {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace == m_pSelf && w->isFullscreen())
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace == m_self && w->isFullscreen())
|
||||
return w;
|
||||
}
|
||||
|
||||
@ -533,23 +533,25 @@ PHLWINDOW CWorkspace::getFullscreenWindow() {
|
||||
}
|
||||
|
||||
bool CWorkspace::isVisible() {
|
||||
return m_bVisible;
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
bool CWorkspace::isVisibleNotCovered() {
|
||||
const auto PMONITOR = m_pMonitor.lock();
|
||||
if (PMONITOR->activeSpecialWorkspace)
|
||||
return PMONITOR->activeSpecialWorkspace->m_iID == m_iID;
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
if (PMONITOR->m_activeSpecialWorkspace)
|
||||
return PMONITOR->m_activeSpecialWorkspace->m_id == m_id;
|
||||
|
||||
return PMONITOR->activeWorkspace->m_iID == m_iID;
|
||||
return PMONITOR->m_activeWorkspace->m_id == m_id;
|
||||
}
|
||||
|
||||
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
|
||||
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
|
||||
int no = 0;
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->workspaceID() != m_id || !w->m_isMapped)
|
||||
continue;
|
||||
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
|
||||
if (onlyTiled.has_value() && w->m_isFloating == onlyTiled.value())
|
||||
continue;
|
||||
if (onlyPinned.has_value() && w->m_pinned != onlyPinned.value())
|
||||
continue;
|
||||
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
|
||||
continue;
|
||||
@ -559,14 +561,16 @@ int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> on
|
||||
return no;
|
||||
}
|
||||
|
||||
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
|
||||
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
|
||||
int no = 0;
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->workspaceID() != m_id || !w->m_isMapped)
|
||||
continue;
|
||||
if (!w->m_sGroupData.head)
|
||||
if (!w->m_groupData.head)
|
||||
continue;
|
||||
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
|
||||
if (onlyTiled.has_value() && w->m_isFloating == onlyTiled.value())
|
||||
continue;
|
||||
if (onlyPinned.has_value() && w->m_pinned != onlyPinned.value())
|
||||
continue;
|
||||
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
|
||||
continue;
|
||||
@ -576,8 +580,8 @@ int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onl
|
||||
}
|
||||
|
||||
PHLWINDOW CWorkspace::getFirstWindow() {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && !w->isHidden())
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace == m_self && w->m_isMapped && !w->isHidden())
|
||||
return w;
|
||||
}
|
||||
|
||||
@ -585,32 +589,27 @@ PHLWINDOW CWorkspace::getFirstWindow() {
|
||||
}
|
||||
|
||||
PHLWINDOW CWorkspace::getTopLeftWindow() {
|
||||
const auto PMONITOR = m_pMonitor.lock();
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden())
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace != m_self || !w->m_isMapped || w->isHidden())
|
||||
continue;
|
||||
|
||||
const auto WINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
|
||||
if (WINDOWIDEALBB.x <= PMONITOR->vecPosition.x + 1 && WINDOWIDEALBB.y <= PMONITOR->vecPosition.y + 1)
|
||||
if (WINDOWIDEALBB.x <= PMONITOR->m_position.x + 1 && WINDOWIDEALBB.y <= PMONITOR->m_position.y + 1)
|
||||
return w;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CWorkspace::hasUrgentWindow() {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && w->m_bIsUrgent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::ranges::any_of(g_pCompositor->m_windows, [this](const auto& w) { return w->m_workspace == m_self && w->m_isMapped && w->m_isUrgent; });
|
||||
}
|
||||
|
||||
void CWorkspace::updateWindowDecos() {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace != m_pSelf)
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace != m_self)
|
||||
continue;
|
||||
|
||||
w->updateWindowDecos();
|
||||
@ -618,10 +617,10 @@ void CWorkspace::updateWindowDecos() {
|
||||
}
|
||||
|
||||
void CWorkspace::updateWindowData() {
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_pSelf.lock());
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_self.lock());
|
||||
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace != m_pSelf)
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace != m_self)
|
||||
continue;
|
||||
|
||||
w->updateWindowData(WORKSPACERULE);
|
||||
@ -629,29 +628,35 @@ void CWorkspace::updateWindowData() {
|
||||
}
|
||||
|
||||
void CWorkspace::forceReportSizesToWindows() {
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden())
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace != m_self || !w->m_isMapped || w->isHidden())
|
||||
continue;
|
||||
|
||||
g_pXWaylandManager->setWindowSize(w, w->m_vRealSize.value(), true);
|
||||
w->sendWindowSize(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CWorkspace::rename(const std::string& name) {
|
||||
if (g_pCompositor->isWorkspaceSpecial(m_iID))
|
||||
if (g_pCompositor->isWorkspaceSpecial(m_id))
|
||||
return;
|
||||
|
||||
Debug::log(LOG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_iID, name);
|
||||
m_szName = name;
|
||||
Debug::log(LOG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_id, name);
|
||||
m_name = name;
|
||||
|
||||
g_pEventManager->postEvent({"renameworkspace", std::to_string(m_iID) + "," + m_szName});
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_self.lock());
|
||||
m_persistent = WORKSPACERULE.isPersistent;
|
||||
|
||||
if (WORKSPACERULE.isPersistent)
|
||||
g_pCompositor->ensurePersistentWorkspacesPresent(std::vector<SWorkspaceRule>{WORKSPACERULE}, m_self.lock());
|
||||
|
||||
g_pEventManager->postEvent({.event = "renameworkspace", .data = std::to_string(m_id) + "," + m_name});
|
||||
}
|
||||
|
||||
void CWorkspace::updateWindows() {
|
||||
m_bHasFullscreenWindow = std::ranges::any_of(g_pCompositor->m_vWindows, [this](const auto& w) { return w->m_bIsMapped && w->m_pWorkspace == m_pSelf && w->isFullscreen(); });
|
||||
m_hasFullscreenWindow = std::ranges::any_of(g_pCompositor->m_windows, [this](const auto& w) { return w->m_isMapped && w->m_workspace == m_self && w->isFullscreen(); });
|
||||
|
||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||
if (!w->m_bIsMapped || w->m_pWorkspace != m_pSelf)
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (!w->m_isMapped || w->m_workspace != m_self)
|
||||
continue;
|
||||
|
||||
w->updateDynamicRules();
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "../helpers/AnimatedVariable.hpp"
|
||||
#include <string>
|
||||
#include "../defines.hpp"
|
||||
#include "DesktopTypes.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
@ -24,42 +23,39 @@ class CWorkspace {
|
||||
|
||||
// Workspaces ID-based have IDs > 0
|
||||
// and workspaces name-based have IDs starting with -1337
|
||||
WORKSPACEID m_iID = WORKSPACE_INVALID;
|
||||
std::string m_szName = "";
|
||||
PHLMONITORREF m_pMonitor;
|
||||
// Previous workspace ID and name is stored during a workspace change, allowing travel
|
||||
// to the previous workspace.
|
||||
SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor;
|
||||
WORKSPACEID m_id = WORKSPACE_INVALID;
|
||||
std::string m_name = "";
|
||||
PHLMONITORREF m_monitor;
|
||||
|
||||
bool m_bHasFullscreenWindow = false;
|
||||
eFullscreenMode m_efFullscreenMode = FSMODE_NONE;
|
||||
bool m_hasFullscreenWindow = false;
|
||||
eFullscreenMode m_fullscreenMode = FSMODE_NONE;
|
||||
|
||||
wl_array m_wlrCoordinateArr;
|
||||
wl_array m_wlrCoordinateArr;
|
||||
|
||||
// for animations
|
||||
CAnimatedVariable<Vector2D> m_vRenderOffset;
|
||||
CAnimatedVariable<float> m_fAlpha;
|
||||
bool m_bForceRendering = false;
|
||||
PHLANIMVAR<Vector2D> m_renderOffset;
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
bool m_forceRendering = false;
|
||||
|
||||
// allows damage to propagate.
|
||||
bool m_bVisible = false;
|
||||
bool m_visible = false;
|
||||
|
||||
// "scratchpad"
|
||||
bool m_bIsSpecialWorkspace = false;
|
||||
bool m_isSpecialWorkspace = false;
|
||||
|
||||
// last window
|
||||
PHLWINDOWREF m_pLastFocusedWindow;
|
||||
PHLWINDOWREF m_lastFocusedWindow;
|
||||
|
||||
// user-set
|
||||
bool m_bDefaultFloating = false;
|
||||
bool m_bDefaultPseudo = false;
|
||||
bool m_defaultFloating = false;
|
||||
bool m_defaultPseudo = false;
|
||||
|
||||
// last monitor (used on reconnect)
|
||||
std::string m_szLastMonitor = "";
|
||||
std::string m_lastMonitor = "";
|
||||
|
||||
bool m_bWasCreatedEmpty = true;
|
||||
bool m_wasCreatedEmpty = true;
|
||||
|
||||
bool m_bPersistent = false;
|
||||
bool m_persistent = false;
|
||||
|
||||
// Inert: destroyed and invalid. If this is true, release the ptr you have.
|
||||
bool inert();
|
||||
@ -72,11 +68,11 @@ class CWorkspace {
|
||||
std::string getConfigName();
|
||||
bool matchesStaticSelector(const std::string& selector);
|
||||
void markInert();
|
||||
SWorkspaceIDName getPrevWorkspaceIDName(bool perMonitor) const;
|
||||
SWorkspaceIDName getPrevWorkspaceIDName() const;
|
||||
void updateWindowDecos();
|
||||
void updateWindowData();
|
||||
int getWindows(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
|
||||
int getGroups(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
|
||||
int getWindows(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyPinned = {}, std::optional<bool> onlyVisible = {});
|
||||
int getGroups(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyPinned = {}, std::optional<bool> onlyVisible = {});
|
||||
bool hasUrgentWindow();
|
||||
PHLWINDOW getFirstWindow();
|
||||
PHLWINDOW getTopLeftWindow();
|
||||
@ -88,11 +84,14 @@ class CWorkspace {
|
||||
void updateWindows();
|
||||
|
||||
private:
|
||||
void init(PHLWORKSPACE self);
|
||||
void init(PHLWORKSPACE self);
|
||||
// Previous workspace ID and name is stored during a workspace change, allowing travel
|
||||
// to the previous workspace.
|
||||
SWorkspaceIDName m_prevWorkspace;
|
||||
|
||||
SP<HOOK_CALLBACK_FN> m_pFocusedWindowHook;
|
||||
bool m_bInert = true;
|
||||
WP<CWorkspace> m_pSelf;
|
||||
SP<HOOK_CALLBACK_FN> m_focusedWindowHook;
|
||||
bool m_inert = true;
|
||||
WP<CWorkspace> m_self;
|
||||
};
|
||||
|
||||
inline bool valid(const PHLWORKSPACE& ref) {
|
||||
|
@ -34,7 +34,8 @@ class IHID {
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
} events;
|
||||
} m_events;
|
||||
|
||||
std::string deviceName, hlName;
|
||||
std::string m_deviceName;
|
||||
std::string m_hlName;
|
||||
};
|
@ -8,6 +8,8 @@
|
||||
#include <aquamarine/input/Input.hpp>
|
||||
#include <cstring>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
#define LED_COUNT 3
|
||||
|
||||
constexpr static std::array<const char*, 8> MODNAMES = {
|
||||
@ -26,37 +28,38 @@ eHIDType IKeyboard::getType() {
|
||||
}
|
||||
|
||||
IKeyboard::~IKeyboard() {
|
||||
events.destroy.emit();
|
||||
m_events.destroy.emit();
|
||||
|
||||
clearManuallyAllocd();
|
||||
}
|
||||
|
||||
void IKeyboard::clearManuallyAllocd() {
|
||||
if (xkbStaticState)
|
||||
xkb_state_unref(xkbStaticState);
|
||||
if (m_xkbStaticState)
|
||||
xkb_state_unref(m_xkbStaticState);
|
||||
|
||||
if (xkbState)
|
||||
xkb_state_unref(xkbState);
|
||||
if (m_xkbState)
|
||||
xkb_state_unref(m_xkbState);
|
||||
|
||||
if (xkbKeymap)
|
||||
xkb_keymap_unref(xkbKeymap);
|
||||
if (m_xkbKeymap)
|
||||
xkb_keymap_unref(m_xkbKeymap);
|
||||
|
||||
if (xkbKeymapFD >= 0)
|
||||
close(xkbKeymapFD);
|
||||
if (m_xkbSymState)
|
||||
xkb_state_unref(m_xkbSymState);
|
||||
|
||||
xkbKeymap = nullptr;
|
||||
xkbState = nullptr;
|
||||
xkbStaticState = nullptr;
|
||||
xkbKeymapFD = -1;
|
||||
m_xkbSymState = nullptr;
|
||||
m_xkbKeymap = nullptr;
|
||||
m_xkbState = nullptr;
|
||||
m_xkbStaticState = nullptr;
|
||||
m_xkbKeymapFD.reset();
|
||||
}
|
||||
|
||||
void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
||||
if (keymapOverridden) {
|
||||
if (m_keymapOverridden) {
|
||||
Debug::log(LOG, "Ignoring setKeymap: keymap is overridden");
|
||||
return;
|
||||
}
|
||||
|
||||
currentRules = rules;
|
||||
m_currentRules = rules;
|
||||
xkb_rule_names XKBRULES = {
|
||||
.rules = rules.rules.c_str(),
|
||||
.model = rules.model.c_str(),
|
||||
@ -77,21 +80,21 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
||||
Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model,
|
||||
rules.options);
|
||||
|
||||
if (!xkbFilePath.empty()) {
|
||||
auto path = absolutePath(xkbFilePath, g_pConfigManager->configCurrentPath);
|
||||
if (!m_xkbFilePath.empty()) {
|
||||
auto path = absolutePath(m_xkbFilePath, g_pConfigManager->m_configCurrentPath);
|
||||
|
||||
if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE)
|
||||
Debug::log(ERR, "Cannot open input:kb_file= file for reading");
|
||||
else {
|
||||
xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
m_xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
fclose(KEYMAPFILE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!xkbKeymap)
|
||||
xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!m_xkbKeymap)
|
||||
m_xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
if (!xkbKeymap) {
|
||||
if (!m_xkbKeymap) {
|
||||
g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + rules.rules + ", model: " + rules.model + ", variant: " + rules.variant +
|
||||
", options: " + rules.options + ", layout: " + rules.layout + " )");
|
||||
|
||||
@ -99,38 +102,38 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
||||
rules.options);
|
||||
memset(&XKBRULES, 0, sizeof(XKBRULES));
|
||||
|
||||
currentRules.rules = "";
|
||||
currentRules.model = "";
|
||||
currentRules.variant = "";
|
||||
currentRules.options = "";
|
||||
currentRules.layout = "us";
|
||||
m_currentRules.rules = "";
|
||||
m_currentRules.model = "";
|
||||
m_currentRules.variant = "";
|
||||
m_currentRules.options = "";
|
||||
m_currentRules.layout = "us";
|
||||
|
||||
xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
m_xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
updateXKBTranslationState(xkbKeymap);
|
||||
updateXKBTranslationState(m_xkbKeymap);
|
||||
|
||||
const auto NUMLOCKON = g_pConfigManager->getDeviceInt(hlName, "numlock_by_default", "input:numlock_by_default");
|
||||
const auto NUMLOCKON = g_pConfigManager->getDeviceInt(m_hlName, "numlock_by_default", "input:numlock_by_default");
|
||||
|
||||
if (NUMLOCKON == 1) {
|
||||
// lock numlock
|
||||
const auto IDX = xkb_map_mod_get_index(xkbKeymap, XKB_MOD_NAME_NUM);
|
||||
const auto IDX = xkb_map_mod_get_index(m_xkbKeymap, XKB_MOD_NAME_NUM);
|
||||
|
||||
if (IDX != XKB_MOD_INVALID)
|
||||
modifiersState.locked |= (uint32_t)1 << IDX;
|
||||
m_modifiersState.locked |= (uint32_t)1 << IDX;
|
||||
|
||||
// 0 to avoid mods getting stuck if depressed during reload
|
||||
updateModifiers(0, 0, modifiersState.locked, modifiersState.group);
|
||||
updateModifiers(0, 0, m_modifiersState.locked, m_modifiersState.group);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < LEDNAMES.size(); ++i) {
|
||||
ledIndexes.at(i) = xkb_map_led_get_index(xkbKeymap, LEDNAMES.at(i));
|
||||
Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES.at(i), ledIndexes.at(i));
|
||||
for (size_t i = 0; i < std::min(LEDNAMES.size(), m_ledIndexes.size()); ++i) {
|
||||
m_ledIndexes[i] = xkb_map_led_get_index(m_xkbKeymap, LEDNAMES[i]);
|
||||
Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES[i], m_ledIndexes[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MODNAMES.size(); ++i) {
|
||||
modIndexes.at(i) = xkb_map_mod_get_index(xkbKeymap, MODNAMES.at(i));
|
||||
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES.at(i), modIndexes.at(i));
|
||||
for (size_t i = 0; i < std::min(MODNAMES.size(), m_modIndexes.size()); ++i) {
|
||||
m_modIndexes[i] = xkb_map_mod_get_index(m_xkbKeymap, MODNAMES[i]);
|
||||
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES[i], m_modIndexes[i]);
|
||||
}
|
||||
|
||||
updateKeymapFD();
|
||||
@ -141,60 +144,59 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
||||
}
|
||||
|
||||
void IKeyboard::updateKeymapFD() {
|
||||
Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName);
|
||||
Debug::log(LOG, "Updating keymap fd for keyboard {}", m_deviceName);
|
||||
|
||||
if (xkbKeymapFD >= 0)
|
||||
close(xkbKeymapFD);
|
||||
xkbKeymapFD = -1;
|
||||
if (m_xkbKeymapFD.isValid())
|
||||
m_xkbKeymapFD.reset();
|
||||
|
||||
auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
|
||||
xkbKeymapString = cKeymapStr;
|
||||
auto cKeymapStr = xkb_keymap_get_as_string(m_xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
|
||||
m_xkbKeymapString = cKeymapStr;
|
||||
free(cKeymapStr);
|
||||
|
||||
int rw, ro;
|
||||
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, &rw, &ro))
|
||||
CFileDescriptor rw, ro;
|
||||
if (!allocateSHMFilePair(m_xkbKeymapString.length() + 1, rw, ro))
|
||||
Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap");
|
||||
else {
|
||||
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw, 0);
|
||||
close(rw);
|
||||
auto keymapFDDest = mmap(nullptr, m_xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw.get(), 0);
|
||||
rw.reset();
|
||||
if (keymapFDDest == MAP_FAILED) {
|
||||
Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap");
|
||||
close(ro);
|
||||
ro.reset();
|
||||
} else {
|
||||
memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length());
|
||||
munmap(keymapFDDest, xkbKeymapString.length() + 1);
|
||||
xkbKeymapFD = ro;
|
||||
memcpy(keymapFDDest, m_xkbKeymapString.c_str(), m_xkbKeymapString.length());
|
||||
munmap(keymapFDDest, m_xkbKeymapString.length() + 1);
|
||||
m_xkbKeymapFD = std::move(ro);
|
||||
}
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD);
|
||||
Debug::log(LOG, "Updated keymap fd to {}", m_xkbKeymapFD.get());
|
||||
}
|
||||
|
||||
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
||||
|
||||
if (xkbStaticState)
|
||||
xkb_state_unref(xkbStaticState);
|
||||
if (m_xkbStaticState)
|
||||
xkb_state_unref(m_xkbStaticState);
|
||||
|
||||
if (xkbState)
|
||||
xkb_state_unref(xkbState);
|
||||
if (m_xkbState)
|
||||
xkb_state_unref(m_xkbState);
|
||||
|
||||
if (xkbSymState)
|
||||
xkb_state_unref(xkbSymState);
|
||||
if (m_xkbSymState)
|
||||
xkb_state_unref(m_xkbSymState);
|
||||
|
||||
xkbState = nullptr;
|
||||
xkbStaticState = nullptr;
|
||||
xkbSymState = nullptr;
|
||||
m_xkbState = nullptr;
|
||||
m_xkbStaticState = nullptr;
|
||||
m_xkbSymState = nullptr;
|
||||
|
||||
if (keymap) {
|
||||
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
|
||||
xkbStaticState = xkb_state_new(keymap);
|
||||
xkbState = xkb_state_new(keymap);
|
||||
xkbSymState = xkb_state_new(keymap);
|
||||
m_xkbStaticState = xkb_state_new(keymap);
|
||||
m_xkbState = xkb_state_new(keymap);
|
||||
m_xkbSymState = xkb_state_new(keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto KEYMAP = xkbKeymap;
|
||||
const auto STATE = xkbState;
|
||||
const auto KEYMAP = m_xkbKeymap;
|
||||
const auto STATE = m_xkbState;
|
||||
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
|
||||
|
||||
const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
@ -203,9 +205,9 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
||||
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) {
|
||||
Debug::log(LOG, "Updating keyboard {:x}'s translation state from an active index {}", (uintptr_t)this, i);
|
||||
|
||||
CVarList keyboardLayouts(currentRules.layout, 0, ',');
|
||||
CVarList keyboardModels(currentRules.model, 0, ',');
|
||||
CVarList keyboardVariants(currentRules.variant, 0, ',');
|
||||
CVarList keyboardLayouts(m_currentRules.layout, 0, ',');
|
||||
CVarList keyboardModels(m_currentRules.model, 0, ',');
|
||||
CVarList keyboardVariants(m_currentRules.variant, 0, ',');
|
||||
|
||||
xkb_rule_names rules = {.rules = "", .model = "", .layout = "", .variant = "", .options = ""};
|
||||
|
||||
@ -233,9 +235,9 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
||||
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
xkbState = xkb_state_new(KEYMAP);
|
||||
xkbStaticState = xkb_state_new(KEYMAP);
|
||||
xkbSymState = xkb_state_new(KEYMAP);
|
||||
m_xkbState = xkb_state_new(KEYMAP);
|
||||
m_xkbStaticState = xkb_state_new(KEYMAP);
|
||||
m_xkbSymState = xkb_state_new(KEYMAP);
|
||||
|
||||
xkb_keymap_unref(KEYMAP);
|
||||
xkb_context_unref(PCONTEXT);
|
||||
@ -247,26 +249,26 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
||||
Debug::log(LOG, "Updating keyboard {:x}'s translation state from an unknown index", (uintptr_t)this);
|
||||
|
||||
xkb_rule_names rules = {
|
||||
.rules = currentRules.rules.c_str(),
|
||||
.model = currentRules.model.c_str(),
|
||||
.layout = currentRules.layout.c_str(),
|
||||
.variant = currentRules.variant.c_str(),
|
||||
.options = currentRules.options.c_str(),
|
||||
.rules = m_currentRules.rules.c_str(),
|
||||
.model = m_currentRules.model.c_str(),
|
||||
.layout = m_currentRules.layout.c_str(),
|
||||
.variant = m_currentRules.variant.c_str(),
|
||||
.options = m_currentRules.options.c_str(),
|
||||
};
|
||||
|
||||
const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
xkbState = xkb_state_new(NEWKEYMAP);
|
||||
xkbStaticState = xkb_state_new(NEWKEYMAP);
|
||||
xkbSymState = xkb_state_new(NEWKEYMAP);
|
||||
m_xkbState = xkb_state_new(NEWKEYMAP);
|
||||
m_xkbStaticState = xkb_state_new(NEWKEYMAP);
|
||||
m_xkbSymState = xkb_state_new(NEWKEYMAP);
|
||||
|
||||
xkb_keymap_unref(NEWKEYMAP);
|
||||
xkb_context_unref(PCONTEXT);
|
||||
}
|
||||
|
||||
std::string IKeyboard::getActiveLayout() {
|
||||
const auto KEYMAP = xkbKeymap;
|
||||
const auto STATE = xkbState;
|
||||
const auto KEYMAP = m_xkbKeymap;
|
||||
const auto STATE = m_xkbState;
|
||||
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
|
||||
|
||||
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
|
||||
@ -283,12 +285,12 @@ std::string IKeyboard::getActiveLayout() {
|
||||
}
|
||||
|
||||
std::optional<uint32_t> IKeyboard::getLEDs() {
|
||||
if (xkbState == nullptr)
|
||||
if (m_xkbState == nullptr)
|
||||
return {};
|
||||
|
||||
uint32_t leds = 0;
|
||||
for (uint32_t i = 0; i < LED_COUNT; ++i) {
|
||||
if (xkb_state_led_index_is_active(xkbState, ledIndexes.at(i)))
|
||||
for (uint32_t i = 0; i < std::min((size_t)LED_COUNT, m_ledIndexes.size()); ++i) {
|
||||
if (xkb_state_led_index_is_active(m_xkbState, m_ledIndexes[i]))
|
||||
leds |= (1 << i);
|
||||
}
|
||||
|
||||
@ -305,10 +307,10 @@ void IKeyboard::updateLEDs() {
|
||||
}
|
||||
|
||||
void IKeyboard::updateLEDs(uint32_t leds) {
|
||||
if (!xkbState)
|
||||
if (!m_xkbState)
|
||||
return;
|
||||
|
||||
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
|
||||
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(m_self.lock()))
|
||||
return;
|
||||
|
||||
if (!aq())
|
||||
@ -318,13 +320,13 @@ void IKeyboard::updateLEDs(uint32_t leds) {
|
||||
}
|
||||
|
||||
uint32_t IKeyboard::getModifiers() {
|
||||
uint32_t modMask = modifiersState.depressed | modifiersState.latched;
|
||||
uint32_t modMask = m_modifiersState.depressed | m_modifiersState.latched;
|
||||
uint32_t mods = 0;
|
||||
for (size_t i = 0; i < modIndexes.size(); ++i) {
|
||||
if (modIndexes.at(i) == XKB_MOD_INVALID)
|
||||
for (size_t i = 0; i < m_modIndexes.size(); ++i) {
|
||||
if (m_modIndexes[i] == XKB_MOD_INVALID)
|
||||
continue;
|
||||
|
||||
if (!(modMask & (1 << modIndexes.at(i))))
|
||||
if (!(modMask & (1 << m_modIndexes[i])))
|
||||
continue;
|
||||
|
||||
mods |= (1 << i);
|
||||
@ -334,50 +336,50 @@ uint32_t IKeyboard::getModifiers() {
|
||||
}
|
||||
|
||||
void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
|
||||
if (!xkbState)
|
||||
if (!m_xkbState)
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group);
|
||||
xkb_state_update_mask(m_xkbState, depressed, latched, locked, 0, 0, group);
|
||||
|
||||
if (xkbSymState)
|
||||
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, group);
|
||||
if (m_xkbSymState)
|
||||
xkb_state_update_mask(m_xkbSymState, 0, 0, 0, 0, 0, group);
|
||||
|
||||
if (!updateModifiersState())
|
||||
return;
|
||||
|
||||
keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = modifiersState.depressed,
|
||||
.latched = modifiersState.latched,
|
||||
.locked = modifiersState.locked,
|
||||
.group = modifiersState.group,
|
||||
m_keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = m_modifiersState.depressed,
|
||||
.latched = m_modifiersState.latched,
|
||||
.locked = m_modifiersState.locked,
|
||||
.group = m_modifiersState.group,
|
||||
});
|
||||
|
||||
updateLEDs();
|
||||
}
|
||||
|
||||
bool IKeyboard::updateModifiersState() {
|
||||
if (!xkbState)
|
||||
if (!m_xkbState)
|
||||
return false;
|
||||
|
||||
auto depressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED);
|
||||
auto latched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
|
||||
auto locked = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
|
||||
auto group = xkb_state_serialize_layout(xkbState, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
auto depressed = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_DEPRESSED);
|
||||
auto latched = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LATCHED);
|
||||
auto locked = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED);
|
||||
auto group = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
|
||||
if (depressed == modifiersState.depressed && latched == modifiersState.latched && locked == modifiersState.locked && group == modifiersState.group)
|
||||
if (depressed == m_modifiersState.depressed && latched == m_modifiersState.latched && locked == m_modifiersState.locked && group == m_modifiersState.group)
|
||||
return false;
|
||||
|
||||
modifiersState.depressed = depressed;
|
||||
modifiersState.latched = latched;
|
||||
modifiersState.locked = locked;
|
||||
modifiersState.group = group;
|
||||
m_modifiersState.depressed = depressed;
|
||||
m_modifiersState.latched = latched;
|
||||
m_modifiersState.locked = locked;
|
||||
m_modifiersState.group = group;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) {
|
||||
|
||||
const auto contains = std::find(pressedXKB.begin(), pressedXKB.end(), xkbKey) != pressedXKB.end();
|
||||
const auto contains = std::find(m_pressedXKB.begin(), m_pressedXKB.end(), xkbKey) != m_pressedXKB.end();
|
||||
|
||||
if (contains && pressed)
|
||||
return;
|
||||
@ -385,21 +387,21 @@ void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) {
|
||||
return;
|
||||
|
||||
if (contains)
|
||||
std::erase(pressedXKB, xkbKey);
|
||||
std::erase(m_pressedXKB, xkbKey);
|
||||
else
|
||||
pressedXKB.emplace_back(xkbKey);
|
||||
m_pressedXKB.emplace_back(xkbKey);
|
||||
|
||||
xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
xkb_state_update_key(m_xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
|
||||
if (updateModifiersState()) {
|
||||
if (xkbSymState)
|
||||
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, modifiersState.group);
|
||||
if (m_xkbSymState)
|
||||
xkb_state_update_mask(m_xkbSymState, 0, 0, 0, 0, 0, m_modifiersState.group);
|
||||
|
||||
keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = modifiersState.depressed,
|
||||
.latched = modifiersState.latched,
|
||||
.locked = modifiersState.locked,
|
||||
.group = modifiersState.group,
|
||||
m_keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = m_modifiersState.depressed,
|
||||
.latched = m_modifiersState.latched,
|
||||
.locked = m_modifiersState.locked,
|
||||
.group = m_modifiersState.group,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
AQUAMARINE_FORWARD(IKeyboard);
|
||||
|
||||
@ -51,7 +52,7 @@ class IKeyboard : public IHID {
|
||||
CSignal modifiers;
|
||||
CSignal keymap;
|
||||
CSignal repeatInfo;
|
||||
} keyboardEvents;
|
||||
} m_keyboardEvents;
|
||||
|
||||
struct SStringRuleNames {
|
||||
std::string layout = "";
|
||||
@ -73,41 +74,49 @@ class IKeyboard : public IHID {
|
||||
void updateXkbStateWithKey(uint32_t xkbKey, bool pressed);
|
||||
void updateKeymapFD();
|
||||
|
||||
bool active = false;
|
||||
bool enabled = true;
|
||||
bool m_active = false;
|
||||
bool m_enabled = true;
|
||||
bool m_allowBinds = true;
|
||||
|
||||
// permission flag: whether this keyboard is allowed to be processed
|
||||
bool m_allowed = true;
|
||||
|
||||
// if the keymap is overridden by the implementation,
|
||||
// don't try to set keyboard rules anymore, to avoid overwriting the requested one.
|
||||
// e.g. Virtual keyboards with custom maps.
|
||||
bool keymapOverridden = false;
|
||||
bool m_keymapOverridden = false;
|
||||
|
||||
xkb_layout_index_t activeLayout = 0;
|
||||
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr,
|
||||
*xkbSymState = nullptr /* Same as static but gets layouts */;
|
||||
xkb_keymap* xkbKeymap = nullptr;
|
||||
xkb_layout_index_t m_activeLayout = 0;
|
||||
xkb_state* m_xkbState = nullptr;
|
||||
|
||||
// never gets modifiers or layout changes sent, used for keybinds
|
||||
xkb_state* m_xkbStaticState = nullptr;
|
||||
xkb_state* m_xkbSymState = nullptr; // same as static but gets layouts
|
||||
|
||||
xkb_keymap* m_xkbKeymap = nullptr;
|
||||
|
||||
struct {
|
||||
uint32_t depressed = 0, latched = 0, locked = 0, group = 0;
|
||||
} modifiersState;
|
||||
} m_modifiersState;
|
||||
|
||||
std::array<xkb_led_index_t, 3> ledIndexes = {XKB_MOD_INVALID};
|
||||
std::array<xkb_mod_index_t, 8> modIndexes = {XKB_MOD_INVALID};
|
||||
uint32_t leds = 0;
|
||||
std::array<xkb_led_index_t, 3> m_ledIndexes = {XKB_MOD_INVALID};
|
||||
std::array<xkb_mod_index_t, 8> m_modIndexes = {XKB_MOD_INVALID};
|
||||
uint32_t m_leds = 0;
|
||||
|
||||
std::string xkbFilePath = "";
|
||||
std::string xkbKeymapString = "";
|
||||
int xkbKeymapFD = -1;
|
||||
std::string m_xkbFilePath = "";
|
||||
std::string m_xkbKeymapString = "";
|
||||
Hyprutils::OS::CFileDescriptor m_xkbKeymapFD;
|
||||
|
||||
SStringRuleNames currentRules;
|
||||
int repeatRate = 0;
|
||||
int repeatDelay = 0;
|
||||
int numlockOn = -1;
|
||||
bool resolveBindsBySym = false;
|
||||
SStringRuleNames m_currentRules;
|
||||
int m_repeatRate = 0;
|
||||
int m_repeatDelay = 0;
|
||||
int m_numlockOn = -1;
|
||||
bool m_resolveBindsBySym = false;
|
||||
|
||||
WP<IKeyboard> self;
|
||||
WP<IKeyboard> m_self;
|
||||
|
||||
private:
|
||||
void clearManuallyAllocd();
|
||||
|
||||
std::vector<uint32_t> pressedXKB;
|
||||
std::vector<uint32_t> m_pressedXKB;
|
||||
};
|
||||
|
@ -17,9 +17,10 @@ class IPointer : public IHID {
|
||||
virtual SP<Aquamarine::IPointer> aq() = 0;
|
||||
|
||||
struct SMotionEvent {
|
||||
uint32_t timeMs = 0;
|
||||
Vector2D delta, unaccel;
|
||||
bool mouse = false;
|
||||
uint32_t timeMs = 0;
|
||||
Vector2D delta, unaccel;
|
||||
bool mouse = false;
|
||||
SP<IPointer> device;
|
||||
};
|
||||
|
||||
struct SMotionAbsoluteEvent {
|
||||
@ -105,10 +106,13 @@ class IPointer : public IHID {
|
||||
|
||||
CSignal holdBegin;
|
||||
CSignal holdEnd;
|
||||
} pointerEvents;
|
||||
} m_pointerEvents;
|
||||
|
||||
bool connected = false; // means connected to the cursor
|
||||
std::string boundOutput = "";
|
||||
bool m_connected = false; // means connected to the cursor
|
||||
std::string m_boundOutput = "";
|
||||
bool m_flipX = false; // decide to invert horizontal movement
|
||||
bool m_flipY = false; // decide to invert vertical movement
|
||||
bool m_isTouchpad = false;
|
||||
|
||||
WP<IPointer> self;
|
||||
WP<IPointer> m_self;
|
||||
};
|
||||
|
@ -42,9 +42,9 @@ class ITouch : public IHID {
|
||||
CSignal motion;
|
||||
CSignal cancel;
|
||||
CSignal frame;
|
||||
} touchEvents;
|
||||
} m_touchEvents;
|
||||
|
||||
std::string boundOutput = "";
|
||||
std::string m_boundOutput = "";
|
||||
|
||||
WP<ITouch> self;
|
||||
WP<ITouch> m_self;
|
||||
};
|
@ -1,13 +1,12 @@
|
||||
#include "Keyboard.hpp"
|
||||
#include "../defines.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
|
||||
#include <aquamarine/input/Input.hpp>
|
||||
|
||||
SP<CKeyboard> CKeyboard::create(SP<Aquamarine::IKeyboard> keeb) {
|
||||
SP<CKeyboard> pKeeb = SP<CKeyboard>(new CKeyboard(keeb));
|
||||
|
||||
pKeeb->self = pKeeb;
|
||||
pKeeb->m_self = pKeeb;
|
||||
|
||||
return pKeeb;
|
||||
}
|
||||
@ -17,22 +16,22 @@ bool CKeyboard::isVirtual() {
|
||||
}
|
||||
|
||||
SP<Aquamarine::IKeyboard> CKeyboard::aq() {
|
||||
return keyboard.lock();
|
||||
return m_keyboard.lock();
|
||||
}
|
||||
|
||||
CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : keyboard(keeb) {
|
||||
CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : m_keyboard(keeb) {
|
||||
if (!keeb)
|
||||
return;
|
||||
|
||||
listeners.destroy = keeb->events.destroy.registerListener([this](std::any d) {
|
||||
keyboard.reset();
|
||||
events.destroy.emit();
|
||||
m_listeners.destroy = keeb->events.destroy.registerListener([this](std::any d) {
|
||||
m_keyboard.reset();
|
||||
m_events.destroy.emit();
|
||||
});
|
||||
|
||||
listeners.key = keeb->events.key.registerListener([this](std::any d) {
|
||||
m_listeners.key = keeb->events.key.registerListener([this](std::any d) {
|
||||
auto E = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(d);
|
||||
|
||||
keyboardEvents.key.emit(SKeyEvent{
|
||||
m_keyboardEvents.key.emit(SKeyEvent{
|
||||
.timeMs = E.timeMs,
|
||||
.keycode = E.key,
|
||||
.state = E.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
@ -41,16 +40,16 @@ CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : keyboard(keeb) {
|
||||
updateXkbStateWithKey(E.key + 8, E.pressed);
|
||||
});
|
||||
|
||||
listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) {
|
||||
m_listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) {
|
||||
updateModifiersState();
|
||||
|
||||
keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = modifiersState.depressed,
|
||||
.latched = modifiersState.latched,
|
||||
.locked = modifiersState.locked,
|
||||
.group = modifiersState.group,
|
||||
m_keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = m_modifiersState.depressed,
|
||||
.latched = m_modifiersState.latched,
|
||||
.locked = m_modifiersState.locked,
|
||||
.group = m_modifiersState.group,
|
||||
});
|
||||
});
|
||||
|
||||
deviceName = keeb->getName();
|
||||
m_deviceName = keeb->getName();
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ class CKeyboard : public IKeyboard {
|
||||
private:
|
||||
CKeyboard(SP<Aquamarine::IKeyboard> keeb);
|
||||
|
||||
WP<Aquamarine::IKeyboard> keyboard;
|
||||
WP<Aquamarine::IKeyboard> m_keyboard;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroy;
|
||||
CHyprSignalListener key;
|
||||
CHyprSignalListener modifiers;
|
||||
} listeners;
|
||||
} m_listeners;
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user