Compare commits

..

578 Commits

Author SHA1 Message Date
Vaxry
84ab8d11e8 props: bump ver to 0.35.0 2024-02-05 01:59:02 +00:00
Vaxry
60bda7ee3d pluginapi: allow registering hyprctl commands
closes #4616
2024-02-05 01:57:29 +00:00
Vaxry
939696f97e hyprctl: move to a class and unify commands 2024-02-05 01:56:49 +00:00
MightyPlaza
cbadf3e3f3 input: focus window on mouse down on decoration (#4514)
Also unifies vectorToWindow funcs
2024-02-04 15:40:20 +00:00
JManch
1ed4f1cb25 screenshader: rename output uniform to wl_output (#4606)
* screenshader: rename output uniform to monitor

* rename to wl_output
2024-02-04 02:30:00 +00:00
Vaxry
5d4ff60f53 hyprpm: fix invalid pkg-config path env in build
ref #4573
2024-02-03 18:32:59 +00:00
Vaxry
504ebe1b37 box: add missing include 2024-02-03 01:31:52 +00:00
Vaxry
cf1886ca44 renderer: avoid unnecessary gpu resource deletions
fixes #4594
2024-02-02 15:36:13 +00:00
Vaxry
341e04a36c dwindle: avoid sending negative sizes to wlr
fixes #4591
2024-02-02 15:04:04 +00:00
Vaxry
d7514412d8 renderer: reset fb pointers after render pass
fixes #4590
2024-02-02 14:56:04 +00:00
Vaxry
7447be8220 hyprpm: fix crash on add plugin
ref #4563
2024-02-02 01:51:14 +00:00
Vaxry
4644de2269 keybinds: fix ignoremods with release 2024-02-02 01:09:57 +00:00
Vaxry
3656045ad8 hyprpm: install headers locally (#4585)
* hyprpm: install headers locally

* oopsie
2024-02-01 19:38:43 +00:00
Abílio Costa
15316aaa31 subsurfaceTree: Fix nullptr crash when disconnecting a monitor (#4577)
I was able to reproduce this frequently by having a kitty terminal on an
monitor running the following command and then unplugging that monitor:

`while true; do echo "" && sleep 0.02; done`
2024-02-01 03:09:31 +00:00
Vaxry
cfd68af5b6 tearing-control: handle unmapped surfaces for hints
fixes #4570
2024-02-01 00:55:29 +00:00
Roger Roger
4f804d5f96 Makefile: remove hyprland symlink on uninstall 2024-01-31 19:34:16 +02:00
Mihai Fufezan
e6f7724ab0 subprojects: remove wlroots.wrap 2024-01-31 14:04:30 +02:00
Mihai Fufezan
e65f52bf2d Makefile: pass PREFIX to CMake 2024-01-30 22:22:06 +02:00
Vaxry
c51b3fb06f events: ignore sending mouse enter to focused if a constraint is active
fixes #4186
2024-01-30 16:24:41 +00:00
Vaxry
3ff59e7e1d hyprpm: update global state on plugin recompile not header update
ref #4547
2024-01-29 23:37:05 +00:00
Mihai Fufezan
3d0d3b6343 Meson: fix wallpaper installation 2024-01-30 01:28:07 +02:00
Vaxry
2e3f0d5991 renderer: Add new background infrastructure
Adds new backgrounds from the winners of the contest
Rewrites how it works
Allows high color precision PNGs (RGB32F precisely)
Fixes a small bug in renderTextureInternalWithDamage
Nukes misc:force_hypr_chan
2024-01-29 23:11:00 +00:00
Vaxry
91e8c42843 hyprpm: don't update headers if they are up-to-date, only recompile
will not update headers if plugins are compiled for different ones, and instead only compile.

ref #4284
2024-01-29 10:30:31 +00:00
Vaxry
4b4bd90b14 renderer: fixup misaligned fsv1 surfaces with uv
fixes #4548
2024-01-28 23:42:49 +00:00
GrizzlT
7009dc9184 nix: fix overlay composition 2024-01-29 00:27:51 +02:00
Vaxry
b7840c6461 xwayland: remove delta from pos sets in configureX11
ref #4536
2024-01-28 20:13:44 +00:00
Vaxry
5a90911b70 hyprpm: verify headersHashCompiled as well in headersValid()
ref #4547
2024-01-28 20:01:46 +00:00
Epilepsy Gatherings
0e5f14d8d2 xwayland: remove reportedsize set in unmanagedSetGeometry (#4539) 2024-01-28 19:22:02 +00:00
Vaxry
df990c80e2 hyprpm: log verbose return of cmake and meson in update 2024-01-28 03:00:05 +00:00
Vaxry
352574d862 hyprpm: add --force for update
closes #4547
2024-01-28 02:04:35 +00:00
Vaxry
bfcc2adbda monitor: wrap usage of wlr_output_state
for better control and convenience in usage.

fixes #4546
2024-01-28 01:57:13 +00:00
Vaxry
9002657bcc monitor: don't call output_state_finish on buffer-less state clears
ref #4546
2024-01-28 00:41:54 +00:00
Vaxry
3e93fdf779 opengl: use texBox for rendering background texture
fixes #4543
2024-01-28 00:32:54 +00:00
Vaxry
bc7e488a4c monitor: clear output state after usage
fixes massive lag
2024-01-27 19:11:14 +00:00
Mihai Fufezan
8b1069b330 flake.lock: update 2024-01-27 17:31:39 +02:00
vaxerski
61fd75b55e [gha] Nix: update wlroots 2024-01-27 13:59:16 +00:00
Vaxry
7b3d039388 deps: update wlroots
drops requirement for WLR_DRM_NO_ATOMIC provided kernel >= 6.8
2024-01-27 13:58:28 +00:00
rszyma
12d79d6342 dwindle: fix windows being created at incorrect position when cursor is over reserved area (#4520)
* fix: smart_split not working correctly when creating a window with cursor over reserved area

* use getClosestNodeOnWorkspace instead of getFirstNodeOnWorkspace when hovering over reserved area

* optimize `getClosestNodeOnWorkspace`

* remove unused methods
2024-01-26 22:30:36 +00:00
bvr-yr
08e3519747 layout: save float props before setting fs state (#4537)
fixes #4388
2024-01-26 17:24:00 +00:00
Epilepsy Gatherings
5cd7e4587e compositor: don't close special on focus on pinned (#4533) 2024-01-26 12:24:52 +00:00
Vaxry
72987dee88 opengl: rassert false on lost context
we do not have infra to deal with this. It will cause hyprland to freeze rendering, we might as well die.
2024-01-26 02:26:10 +00:00
Vaxry
754eaf5b8b pluginapi: fix hooks with negative rip offsets
fixes #4484
2024-01-24 13:53:18 +00:00
Zach DeCook
df17991b1c input: Allow disabling touchscreen input (#4517)
* enable/disable touch device

* ConfigManager: update documentation of 'enabled'
2024-01-24 00:15:01 +00:00
Vaxry
791e1b96b3 internal: minor header cleanup 2024-01-23 01:32:34 +00:00
vaxerski
02b4a9bded compositor: clarify common errors at launch 2024-01-22 09:46:47 +01:00
Sean McGovern
4d403dac32 build: protocols: require wayland-protocols >= 1.32
The cursor-shape-v1 protocol was not available until this release.
2024-01-20 22:33:12 +02:00
vaxerski
f40e382fc6 crashreporter: skip first possibly cut off line in log tail 2024-01-20 09:16:27 +01:00
vaxerski
b86ed02d8a keybinds: avoid duplicated held keys, only use last, remove all
ref #4471
2024-01-19 19:09:32 +01:00
vaxerski
17339e0ae9 input: track exclusive LSes
ref #4465
2024-01-19 16:45:34 +01:00
vaxerski
5eeec8860e core: improve cleanup logic 2024-01-19 16:20:30 +01:00
Epilepsy Gatherings
9f20a15955 input: remove animate checks on resize limiter (#4480) 2024-01-19 15:45:51 +01:00
vaxerski
c4365f20ed damage: use buffer_damage instead of effective_damage 2024-01-17 16:01:20 +01:00
vaxerski
307dd8f511 input: partially revert #4401
ref #4465
2024-01-17 14:43:38 +01:00
Huy Nguyen
8342bac697 Nix: disable fortify for devshell (#4463)
This disables '_FORTIFY_SOURCE' Werrors trying to compile wlroots.
Long standing issue in https://github.com/NixOS/nixpkgs/issues/60919
afaik.

After this change you should be able to:
```
nix develop
mmeson setup build -Dbuildtype=debug
ninja -C build
```
2024-01-17 14:27:35 +02:00
virchau13
3c964a9fdc keybinds: Add dispatcher for xmonad/qtile-style workspace switching (#4439)
* feat: implement xmonad/qtile-style workspace switching

Implements the focusWorkspaceOnCurrentMonitor dispatcher and function,
which implements XMonad/Qtile-style workspace switching.

When called, focusWorkspaceOnCurrentMonitor will:
1. Send the requested workspace to the current monitor,
2. If the workspace was previously active on a different monitor,
   replace it with the workspace that was previously active on the
   current monitor,
3. Focus the workspace on the current monitor.

* fix: address PR comments
2024-01-15 16:30:46 +01:00
MightyPlaza
f14c5ea5c5 groupbar: separate gradients from title (#4444)
* separate gradients from title logic
modified:   src/config/ConfigManager.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* fix disabled extents
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* fix disabled height
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2024-01-15 16:17:42 +01:00
fufexan
dcd7a92b01 [gha] Nix: update inputs 2024-01-15 00:03:29 +00:00
Isaac Myhal
b6516bad02 config: Add border gradients to windowrulev2 (#4335)
* Add border gradients to windowrulev2

* windowrule border gradient: Use CVarList to parse

* windowrule border gradient: No {} around short ifs
2024-01-14 18:27:32 +01:00
dranull
13d9a637d6 hyprctl: screen_shader config fixes (#4102)
* Allow "/" in values of requests

* Don't tick on empty value
2024-01-14 18:12:52 +01:00
1over137
4cee94b91c fractional: Set preferred scale on monitor config reload (#4406) 2024-01-14 14:56:35 +01:00
Zach DeCook
c4da4b026d layershell: Fix greedy mouse grab from keyboard_interactive layer (#4401)
* Layer: Don't allow a keyboard-layer to steal focus from other layers

* Input: Don't change keyboard focus on click if focus is locked
2024-01-12 15:43:16 +01:00
scorpion-26
babb9c07b0 swipe: Prevent hiding current workspace when swiping (#4417)
When workspace_swipe_use_r is enabled, swiping from WS 1 to a non-empty WS 2 would
hide WS 1 (Similar effect to issue #4076). This is caused by a faulty
check which doesn't consider, that workspaceIDLeft could be the current
workspace.
This bug is only a problem for r, because m wraps around on WS 1 m-1, whereas r stays on WS 1.
2024-01-11 19:22:40 +01:00
vaxerski
6b92144f15 surface: avoid spam of window surfaces with scale and transform events
fixes #4408
2024-01-11 14:07:28 +01:00
Clyybber
8d31c84483 layout: Round window pos and size on togglefloating (#4407)
Also restore the behaviour introduced in bc4a51dbbb
2024-01-10 18:08:58 +01:00
vaxerski
d484506600 keybinds: fix tracking of sent key states 2024-01-10 18:06:38 +01:00
vaxerski
b240704bee renderer: allow rendering multiple fullscreen windows in third fs pass
something might be fading out, sliding out, etc. We handle it before, why not use it?

fixes #4076
2024-01-09 20:42:07 +01:00
vaxerski
71166ef40b subsurfaceTree: update surface tree protocol feedback on map 2024-01-09 18:14:08 +01:00
vaxerski
252aaaecfa input: add special_fallthrough
fixes #4323
2024-01-09 13:17:55 +01:00
vaxerski
f92a86af53 renderer: ignore box offsets for fullscreen windows 2024-01-08 19:58:15 +01:00
MightyPlaza
2ba2c8aeee groupbar: improve gradient handling (#4390)
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2024-01-08 19:38:22 +01:00
Piroro-hs
955009655d cmake: Propagate NO_XWAYLAND to wlroots building setup (#4385) 2024-01-08 19:24:52 +01:00
vaxerski
d7d333d162 opengl: apply box rot to projections 2024-01-07 18:51:08 +01:00
vaxerski
f5b2fd2bc3 opengl: add renderdata.forceIntrospection 2024-01-07 18:37:02 +01:00
vaxerski
44ee9915e3 renderer: overhaul renderModifData 2024-01-07 18:35:44 +01:00
vaxerski
9f2bde925b hyprpm: handle failed compilations gracefully 2024-01-07 18:15:51 +01:00
vaxerski
7904188de9 input: allow focusSurface when locked if surfase is sessionLock 2024-01-07 14:04:32 +01:00
Epilepsy Gatherings
666ee61c13 input: leave special on focus (#4358) 2024-01-07 12:06:33 +01:00
Jan Beich
7e033e48ac make: unbreak with non-GNU ln(1) after 78f9ba9fdd
ln -s -r -f /usr/local/bin/Hyprland /usr/local/bin/hyprland
ln: illegal option -- r
usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]
       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir
       link source_file target_file
*** Error code 1
2024-01-05 19:22:43 +02:00
Naksu
d8dbdc4a01 main: Fix typo in std::cerr (#4359) 2024-01-05 12:45:49 +01:00
vaxerski
d3acf8da3b CI: don't close stale issues 2024-01-05 12:40:18 +01:00
Mihai Fufezan
aeeeace102 flake.lock: update 2024-01-04 22:52:03 +02:00
thejch
880996b053 master: Add more null checks for rollnext (#4343)
* add more null check for rollnext

* remove pwindow check
2024-01-04 16:17:17 +01:00
rszyma
4d6d662c67 Makefile: force ln command to overwrite symlink (#4347)
When running `make install` twice ln shows an error:
```
ln -s -r /usr/local/bin/Hyprland /usr/local/bin/hyprland
ln: failed to create symbolic link '/usr/local/bin/hyprland': File exists
```
2024-01-04 10:20:17 +01:00
vaxerski
1512b81126 master: guard PNODE in roll*
fixes #4331
2024-01-02 22:18:38 +01:00
flicko
4f26c4e1eb config: variables update their value when set again (#4263)
* variables update their value when set again

* only sort if new variable is found

* clang-format
2024-01-02 16:38:30 +01:00
dranull
3c33d4b9dd keybinds: Refocus only if the silently moved window had the focus (#4328) 2024-01-02 14:50:30 +01:00
vaxerski
bd3ea8dcb5 examples: remove example plugin
closes #4329
2024-01-02 14:25:18 +01:00
vaxerski
813af393f1 layout: update rules before applying fullscreen nodes in layouts 2024-01-02 14:21:36 +01:00
dranull
583b05a8c6 groupbar: Drag single window instead of destroying group (#4327) 2024-01-02 13:37:03 +01:00
Zach DeCook
1607e96704 HookSystem: rename PAGESIZE_VAR from PAGESIZE to avoid conflict (#4321) 2024-01-01 23:05:26 +01:00
q234rty
1a4f23eb2f renderer: Only force nearest neighbor when the sizes are off by one or two (#4325)
Fixes rendering issues in arch's extra/telegram-desktop
2024-01-01 20:20:27 +01:00
bvr-yr
42ab06e7c8 meson: fix wlroots patch (#4324) 2024-01-01 19:58:01 +01:00
vaxerski
46753b1f22 CI: limit stalebot ops per run 2024-01-01 18:37:49 +01:00
Vaxry
d4e68ab602 CI: allow manual stale execution 2024-01-01 18:34:15 +01:00
rszyma
37b76cd1ca keybinds: fix keys getting stuck + minor refactor & optimizations to keybind handling (#4304) 2024-01-01 18:29:51 +01:00
vaxerski
0be36cd02d cmakelists: fix wlroots patch sed 2024-01-01 18:29:10 +01:00
Vaxry
4e0e8d933e CI: add stalebot 2024-01-01 18:26:48 +01:00
vaxerski
c7ba460687 wlroots: update version patches 2024-01-01 18:19:22 +01:00
vaxerski
3a189c265d issue templates: make versions spoiler'd 2024-01-01 18:05:49 +01:00
vaxerski
069880e374 hyprctl: add systeminfo 2024-01-01 17:53:03 +01:00
vaxerski
fa5e812304 [gha] Nix: update wlroots 2024-01-01 15:48:37 +00:00
vaxerski
33444e1e5e deps: update wlroots 2024-01-01 16:47:54 +01:00
vaxerski
03ebbe18ed props: bump ver to 0.34.0 2024-01-01 13:03:15 +01:00
dranull
7c1ac58a4b input: Ignore some input events when focus is on a layer surface (#4306)
* No motion events with focus on LS on workspace change

* Don't check scroll events on decorations with focus on LS
2024-01-01 13:02:16 +01:00
vaxerski
46997a7643 renderer: fix auto scale detection with fractional
ref #4225
2023-12-31 13:54:24 +01:00
vaxerski
b5b025a1ed renderer: use nearest_neighbor for misaligned fractional-scale surfaces
ref #4225
2023-12-31 13:11:26 +01:00
MightyPlaza
94d6b2d2c1 deco: fix missing border on togglefloating (#4305)
modified:   src/managers/KeybindManager.cpp
2023-12-30 16:59:01 +01:00
zakk4223
33fe3a2e7f hyprpm: Make sure we're in git repo before getting new hash (#4303) 2023-12-30 15:19:53 +01:00
MightyPlaza
2ad2e1d5f5 groupbar: add enabling groupbar and setting priority (#4299) 2023-12-30 15:18:53 +01:00
dranull
5f8e4068e5 groupbar: Middle click on groupbar to close tab (#4297)
* Prevent window swapping when the head is removed

* Bring floating windows to top when selected

* Allow clicks on gropubar in fullscreen 1

* Close window on groupbar with middle click
2023-12-30 00:38:12 +01:00
vaxerski
78f9ba9fdd makefile: add symbolic link for lowercase binary name
ref #4272
2023-12-29 10:37:58 +01:00
vaxerski
9242b03317 internal: minor include fixes and missed format 2023-12-29 10:27:09 +01:00
vaxerski
f9c13b614c xdg-shell: fix sending of suspended state
fixes #4287
2023-12-29 00:26:23 +01:00
vaxerski
ddf8e01c1e config: don't emit reloaded event before eventManager is created 2023-12-29 00:17:58 +01:00
vaxerski
f771c10d1a renderer: ignore windowRequestedCursorHide
ref #4197, thanks @dtop129

co-authored-by: dtop129
2023-12-29 00:04:01 +01:00
dranull
8321d6be46 internal: Unify input handling on decorations (#4280)
* Unify input handling on decorations

* Make input methods private

* Optional data
2023-12-28 23:54:41 +01:00
Mihai Fufezan
cedf5f1fca CI/Nix: fix build 2023-12-29 00:07:23 +02:00
vaxerski
9fba887cc9 socket2: emit configreloaded event
fixes #4285
2023-12-28 22:34:12 +01:00
vaxerski
4f3ee4c645 renderer: add decoration:blur:popups_ignorealpha
fixes #4282
2023-12-28 22:29:04 +01:00
MightyPlaza
5f65946c84 hyprctl: add decorations (#4275)
* add hyprctl decorations

modified:   hyprctl/main.cpp
modified:   src/debug/HyprCtl.cpp
modified:   src/render/decorations/CHyprBorderDecoration.cpp
modified:   src/render/decorations/CHyprBorderDecoration.hpp
modified:   src/render/decorations/CHyprDropShadowDecoration.cpp
modified:   src/render/decorations/CHyprDropShadowDecoration.hpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.hpp
modified:   src/render/decorations/IHyprWindowDecoration.cpp
modified:   src/render/decorations/IHyprWindowDecoration.hpp

* fixes
modified:   hyprctl/main.cpp
modified:   src/render/decorations/IHyprWindowDecoration.cpp
2023-12-28 16:38:16 +01:00
vaxerski
6a93cee74e pluginapi: manually detect endbr64 opcodes in function hooks
ref #4277
2023-12-28 13:36:09 +01:00
vaxerski
07132741bc renderer: use 120 as the denominator in scale checks 2023-12-27 23:47:01 +01:00
Mihai Fufezan
e5eb11ad04 Nix: wrap with gcc
This ensures the function hook can assemble.
2023-12-27 20:27:55 +02:00
vaxerski
e75dafd8b2 pluginapi: better wording for plugin function hook errors 2023-12-27 19:27:15 +01:00
vaxerski
b7e8110a30 pluginapi: log assembler return 2023-12-27 19:23:06 +01:00
vaxerski
2702814a3f opengl: add toggleable nvidia_anti_flicker
ref #4252
2023-12-27 19:17:07 +01:00
André Silva
f86cdcf8d5 nix: fix wlroots build 2023-12-27 14:29:15 +02:00
André Silva
85d375e8ab flake.lock: update nixpkgs 2023-12-27 14:29:15 +02:00
MightyPlaza
6cd82d948f input: don't steal mouseDown from LS (#4260)
modified:   src/managers/input/InputManager.cpp
2023-12-27 11:44:13 +01:00
MightyPlaza
1ecd173c7a groupbar: remove extra border size from groupbars (#4262)
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2023-12-27 11:43:55 +01:00
Vaxry
7474c81958 pluginapi: Trampoline hooks %rip patching improvements (#4256)
---------

Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
2023-12-27 11:43:04 +01:00
Tuur Vanhoutte
191fa587f4 windowrules: add initialTitle and initialClass (#4259) 2023-12-26 23:47:46 +01:00
vaxerski
9fb50252d3 special: move floating windows along with the workspace 2023-12-26 19:44:38 +01:00
André Silva
bfb4d66c81 compositor: spawn environment setup with keybind manager (#3722) 2023-12-26 18:16:59 +01:00
dranull
34b0ce66b3 dwindle: Use window->middle() when cursor is on reserved area (#4253) 2023-12-26 17:24:31 +01:00
André Silva
2c2ff4b61b hyprctl: check only ISDEBUG in version (#3702) 2023-12-26 14:14:12 +01:00
Mihai Fufezan
4f99e805b9 flake.lock: update 2023-12-25 19:27:25 +02:00
vaxerski
e2d04ae503 renderer: add option to blur popups
closes #2134
2023-12-25 18:07:07 +01:00
MightyPlaza
b25b06430b groupbar: add egl context to refreshGroupBarGradients() (#4238)
* add egl context to refreshGroupBarGradients()

modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* don't unsetEGL
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2023-12-24 19:29:04 +01:00
vaxerski
5aab4a96e3 dispatchers: add tiled/floating to cyclenext 2023-12-24 15:08:48 +01:00
vaxerski
ff75f991a5 compositor: don't block focus if there is no keyboard 2023-12-24 13:20:31 +01:00
vaxerski
f013acc6ee renderer: add occlusion for special workspaces 2023-12-23 22:41:42 +01:00
vaxerski
d5811283d2 xdgshell: bump to 6, send suspended states 2023-12-23 22:30:56 +01:00
dranull
5c7e23f86b shadow: Avoid rounded shadows when rounding is 0 (#4230) 2023-12-23 20:40:07 +01:00
vaxerski
b9c1414f25 border: fix missed translate by offset 2023-12-23 16:11:26 +01:00
vaxerski
cc0516a9ae windowrules: minor fixes to onworkspace
fixes #4227
2023-12-23 15:49:42 +01:00
vaxerski
6c8e0f9863 config: add debug:disable_scale_checks
ref #4225
2023-12-23 00:21:02 +01:00
vaxerski
6b6f3396cf renderer: deny invalid scales and suggest a replacement
ref #4225 #3511
2023-12-23 00:09:55 +01:00
vaxerski
79ef29d6e0 renderer: apply scale to wlr after checks
ref #4225
2023-12-22 23:00:36 +01:00
vaxerski
c416880cf9 shadow: correctly scale boxes 2023-12-22 19:54:31 +01:00
vaxerski
37d2840246 renderer: reject non-clean scales, find nearest clean 2023-12-22 19:54:31 +01:00
thejch
7cec618fe4 master: add dispatchers rollnext and rollprev (#4209) 2023-12-22 12:37:38 +01:00
vaxerski
bd952dcef2 systemd: add HYPRLAND_NO_SD_NOTIFY
fixes #4217
2023-12-21 22:27:12 +01:00
vaxerski
bc51a91043 crashreporter: don't explicitly set 777 on crash report directory
fixes #4218
2023-12-21 22:18:07 +01:00
vaxerski
698f3b6576 hyprpm: trim paths in PATH
fixes #4210
2023-12-21 22:01:55 +01:00
Ahmed Yasser
79f3888b4b signal: fix invalid pointer access (#4207) 2023-12-20 23:54:52 +01:00
vaxerski
4eb42fab7b windowrules: add onworkspace
cool
2023-12-20 23:52:18 +01:00
vaxerski
48ecb13b14 renderer: improve cursor hiding infra
ref #4197
2023-12-20 21:40:44 +01:00
vaxerski
a197fe3c11 renderer: don't set surfaces on cursor timeout 2023-12-20 17:59:11 +01:00
Junxuan Liao
53c78ab906 idle: notify idle on tablet inputs (#4201)
Fixes #4028.
2023-12-20 17:53:54 +01:00
vaxerski
b4f4bd38e8 configmanager: set a limit to config variable substitutions
fixes #4198
2023-12-20 13:07:12 +01:00
vaxerski
d1b8a63a8e input: allow setting cursor even if it's hidden
ref #4197
2023-12-20 13:00:35 +01:00
Husam Harazi
3771c49a94 filesystem: Set the sticky bit on /tmp/hypr (#4199)
To prevent unprivileged users from deleting other users' files.
2023-12-20 11:56:15 +00:00
vaxerski
d9b74ff96b renderer: staticize local functions 2023-12-19 11:55:56 +00:00
Vaxry
ef445093f9 internal: convert uname fields to strings before logging
fixes #4188, thanks @jbeich
2023-12-18 23:33:10 +00:00
Vaxry
11fd37418c renderer: fix null cursor surface sets being ignored
oopsie from #4184
2023-12-18 22:06:50 +00:00
dusanx
0c74df4f9e renderer: cursor hiding logic improvements (#4184)
Co-authored-by: Dusan Popovic <dpx@binaryapparatus.com>
2023-12-18 16:06:06 +00:00
Vaxry
460a326c90 Revert "xdgshell: set predicted tiled windows to monitor res size pre-map"
This reverts commit 763d5fa05f.

Some issues and fixes #4185
2023-12-18 14:45:20 +00:00
Vaxry
403fd7d9f6 xwayland: move commit handler connect to associate
fixes #4179
2023-12-18 00:15:30 +00:00
Vaxry
763d5fa05f xdgshell: set predicted tiled windows to monitor res size pre-map
Should improve #4022 although not exactly fix. Fixing would require more witchcraft
2023-12-17 20:09:34 +00:00
Vaxry
9fd928e114 internal: nuke CWindow::m_bMappedX11
it's useless. m_bIsMapped is the same.
2023-12-17 20:00:18 +00:00
Vaxry
bf7374011b xwaylandmgr: allow resizes without a monitor 2023-12-17 19:58:11 +00:00
Vaxry
8c9f38e405 events: improve wl_surface::commit event tracking 2023-12-17 19:58:01 +00:00
Vaxry
c0d9dcc586 xwayland: set reported and pending size/pos on geometry sets 2023-12-17 14:53:59 +00:00
MightyPlaza
2a777cb71b hyprctl: add commit date to "hyprctl version" (#4171)
* add commit date to "hyprctl version"
modified:   scripts/generateVersion.sh
modified:   src/debug/HyprCtl.cpp
modified:   src/version.h.in

* Nix: add date to hyprctl

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-12-17 14:29:58 +00:00
dranull
9ca0c7d814 input: Activate resize_on_border only when key is pressed (#4170) 2023-12-16 20:47:58 +00:00
Vaxry
b1b8d732e6 toplevelexport: fix missed pmonitor set for opengl
ref #4165
2023-12-15 21:20:13 +00:00
Vaxry
4e5d9b90c0 toplevelexport: fix missing GL_READ_FRAMEBUFFER set in shm copy
ref #4165
2023-12-15 21:04:34 +00:00
Vaxry
e1ed8e11ca internal: import qt env to dbus 2023-12-15 20:59:13 +00:00
Vaxry
79d8d14fe0 config: add qt env to default config 2023-12-15 18:27:04 +00:00
Vaxry
395ab3ba37 xdg: use better min/max size checks for floating resizes 2023-12-15 15:38:20 +00:00
dranull
36fa33f7ca input: Prevent crash with invalid keyboard layout (#4157) 2023-12-15 00:39:03 +00:00
Mihai Fufezan
f7cde9c92c Nix: remove duplicate options from modules (#4012) 2023-12-14 20:02:20 +02:00
Vaxry
01e5c59d75 Revert "xwayland: push invisible windows outside layout"
This reverts commit accb3d8d0b.

Bad idea
2023-12-13 17:25:19 +00:00
Junxuan Liao
b2e5a80e2f hyprpm: Link against tomlplusplus when using cmake (#4145)
This reduces the compilation time and is consistent with the meson version.
2023-12-13 14:15:03 +00:00
Mihai Fufezan
55cb565e6d Nix: expose legacyRenderer package 2023-12-13 13:31:36 +02:00
Vaxry
4190b96718 hyprpm: add duplicate header error and log more verbose in install fails 2023-12-13 02:33:07 +00:00
Mihai Fufezan
d9bc210285 Nix: remove libdrm override
Nixpkgs update has the new version.
2023-12-12 23:34:22 +02:00
dranull
4de986072c layout: Focus a floating window after closing the last tiled (#4137) 2023-12-12 16:44:31 +00:00
dranull
934112af5b config: Use canonical instead of read_symlink (#4136) 2023-12-12 16:43:38 +00:00
Vaxry
ba2af6f86d focus: prefer sendMotionEventsToFocused for ensuring cursor image 2023-12-12 14:58:43 +00:00
Vaxry
1950c3fc9c input: unset resize cursor on empty focus
fixes #4133
2023-12-12 14:55:48 +00:00
Vaxry
8f38487884 xwayland: don't change workspace on configure for invisible windows
fixes #3578
2023-12-12 01:15:17 +00:00
Vaxry
accb3d8d0b xwayland: push invisible windows outside layout
fixes some xwl focus issues with special especially.
2023-12-12 01:14:36 +00:00
Vaxry
ea7569d7e0 config: improve layoutopt handling for workspacerules 2023-12-11 22:58:51 +00:00
François Conzelmann
e53134ca90 internal: fix interactions with fakefullscreen (#4113)
- In a maximized window, unstuck fakefullscreen from on state
- In a fakefullscreen window, going in and out of fullscreen state
  keep the fakefullscreen state rendered
2023-12-11 16:51:10 +00:00
Vaxry
8191e635a3 deco-positioner: include sticky in sent geometry 2023-12-11 16:46:20 +00:00
Vaxry
0fdf909b19 renderer: don't render decorations on renderdata.decorate false
fixes #4117
2023-12-11 16:32:11 +00:00
Jan Beich
50648e6bae hyprpm: cast std::clamp args to be of the same type (#4116)
hyprpm/src/progress/CProgressBar.cpp:50:27: error: no matching function for call to 'clamp'
    const auto BARWIDTH = std::clamp(w.ws_col - m_szCurrentMessage.length() - 2, 0UL, 50UL);
                          ^~~~~~~~~~
/usr/include/c++/v1/__algorithm/clamp.h:38:1: note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('size_type' (aka 'unsigned int') vs. 'unsigned long')
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi)
^
/usr/include/c++/v1/__algorithm/clamp.h:27:1: note: candidate function template not viable: requires 4 arguments, but 3 were provided
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi, _Compare __comp)
^
2023-12-11 16:29:04 +00:00
nmzik
53ce7992be renderer: double lookup fix, input: double conversion fix (#4124) 2023-12-11 16:28:22 +00:00
Vaxry
167f2ed3b2 border: fix failed assert on small windows
fixes #4115
2023-12-10 19:32:03 +00:00
Vaxry
d02ba422da hyprpm: guard empty command
fixes #4114
2023-12-10 19:30:26 +00:00
François Conzelmann
359baa0214 ci: use composite action to minimize code duplication (#4112) 2023-12-10 19:26:25 +00:00
nmzik
efdf07e295 renderer: Allocate background texture only if required (#4111) 2023-12-10 16:53:08 +00:00
Mihai Fufezan
0c10b8ab2d input: Add scroll_points option for device (#4101) 2023-12-10 16:30:08 +00:00
Vaxry
9f5b9053c6 internal: Remake borders as window decorations (#4104) 2023-12-10 16:28:12 +00:00
Vaxry
b3dc58e104 hyprpm: clone plugins recursively 2023-12-10 16:18:30 +00:00
Vaxry
af6aae4e12 renderer: don't use clipBox for pinned window occlusion during animations
ref #4094
2023-12-09 18:41:32 +00:00
Vaxry
0ebbf371ff renderer: improve fullscreen workspace client rendering
fixes #4076
2023-12-09 18:38:32 +00:00
dranull
89d8f665b5 config: Avoid regenerating config if --config is used (#4103) 2023-12-09 18:27:30 +00:00
Vaxry
9132660768 functionHooks: fix incorrect protlen calcs 2023-12-09 16:16:46 +00:00
Vaxry
dd0714c22a input: add relative_input for tablets
closes #2639
2023-12-09 04:07:28 +00:00
Vaxry
11d1c50420 windowrules: add focus param 2023-12-08 16:02:16 +00:00
Vaxry
288f1863f0 hyprctl: allow instances without HIS 2023-12-08 15:40:01 +00:00
thejch
6fb1b89b98 makefile: add rm hyprpm for uninstall (#4086) 2023-12-07 22:36:18 +00:00
dranull
004bf94a23 keybinds: Keep focus on special when switching workspaces (#4084) 2023-12-07 22:12:08 +00:00
Vaxry
aa020a2a1a toplevel-export: commence render pass before reading
fixes #4070
2023-12-07 17:58:13 +00:00
Vaxry
d9175a0181 hyprpm: fix with system headers
fixes #4082
2023-12-07 17:22:02 +00:00
Vaxry
a794eecd6a README: add note about hyprpm + clang-format 2023-12-07 10:46:12 +00:00
Vaxry
d360550546 hyprpm: Add hyprpm, a Hyprland Plugin Manager (#4072) 2023-12-07 10:41:09 +00:00
ddmetz
62a8d0be5c keybinds: check for null last monitor in changeworkspace (#4077) 2023-12-07 00:09:25 +00:00
François Conzelmann
4a42344e97 style/ci: apply clang-format and verify it in ci (#4039)
* style: apply clang-format

* ci: add new clang-format job to CI
2023-12-06 22:54:56 +00:00
Vaxry
5489f9f07a renderer: use xray for background blur on small() surfaces
ref #4050
2023-12-06 20:17:40 +00:00
Vaxry
d74607e414 props: bump ver to 0.33.1 2023-12-06 16:47:26 +00:00
Vaxry
c4bd91ec8a makefile: only require version.h before installheaders 2023-12-06 15:31:23 +00:00
Vaxry
03c6f4506a internal: various improvements to avoid crashes on exit 2023-12-06 14:46:29 +00:00
swwind
13b4c6de86 input: don't send mouse events on touch (#4071) 2023-12-06 14:30:40 +00:00
Tungsten842
8bd86cf37e hyprctl: order commands alphabetically (#4061) 2023-12-05 23:39:57 +00:00
dranull
cfd94c5b30 input: Stop propagating axis events after valid binds (#4059) 2023-12-05 21:16:26 +00:00
Vaxry
ab66fa430e screencopy: fix glReadPixels offset
fixes #4042
2023-12-05 20:04:53 +00:00
Vaxry
37d7a8c64d framebuffer: ignore addStencil on legacyRenderer
ref #4044
2023-12-05 15:41:17 +00:00
Vaxry
da863459c4 screencopy: fix legacyrenderer builds
fixes #4044
2023-12-05 14:59:12 +00:00
Vaxry
83248b6936 toplevelexport: fix getPreferredReadFormat param in captureToplevel
fixes #4043
2023-12-05 14:43:54 +00:00
Vaxry
3bb9c7c5cf props: bump ver to 0.33.0 2023-12-05 00:52:16 +00:00
Vaxry
2d04cb1cc6 input: make fallback layout us 2023-12-05 00:48:39 +00:00
Vaxry
c6804ccaab opengl: fixup blur dirty repaint conditions with solitary
fixes #4025
2023-12-05 00:43:09 +00:00
Glizda
aa46aaed04 config: Add variables to default config (#4032)
* update default config

* Fix inconsistency in variable naming

* continuation of last commit

* edited example/hyprland.conf for parity

* fix  issue

* deleted unwanted newline
2023-12-04 18:47:58 +00:00
Vaxry
68783d904d screencopy: use buffer format for glReadPixels
fixes #4029
2023-12-04 03:52:54 +00:00
Vaxry
5d100bdcbb opengl: clear layer fade fbs in ~dtor 2023-12-04 02:08:34 +00:00
Vaxry
45d3fbb8d8 opengl: free window framebuffers in ~dtor
ref #4036
2023-12-04 01:44:16 +00:00
dranull
9a9528d093 config: Minor --config improvements, fixes (#4034)
* Follow symlink, only file, absolute path for -c

* Create config file only for default paths

* Skip non-file source= glob results

* Check for absolute path on XDG_CONFIG_HOME

As per spec, all non-absolute paths should be ignored.
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
2023-12-04 01:35:24 +00:00
Vaxry
e496b0f250 screencopy: fix detecting gl shm formats
ref #4029
2023-12-03 22:04:07 +00:00
Vaxry
dc2082b00a screencopy: fix transformed on shm 2023-12-03 19:06:51 +00:00
dranull
59cb0e20de input: Handle fullscreen windows in vectorToWindowIdeal (#4021) 2023-12-03 12:53:12 +00:00
Vaxry
80b9b21f9f opengl: fix nvidia read formats
fixes #4023
2023-12-02 14:51:45 +00:00
thejch
758cf90ea1 workspacerules: Add workspace rule for master layout orientation (#3964)
* add workspace rule for master layout orientation

* change rule format

* edit rule name

* use map for layoutopts

* use std::any instead of string
2023-12-02 14:42:49 +00:00
Vaxry
6e8b9ef7d8 opengl: fix swapped rgb drm formats 2023-12-01 17:23:50 +00:00
Vaxry
9c09f2a847 screencopy: fix shm exports with 10-bit
fixes #4019
2023-12-01 17:20:56 +00:00
Wren Baxter
8440a30231 input: fix overzealous mouse capture on resize_on_border (#4010)
fixes #2456
2023-12-01 01:12:08 +00:00
Vaxry
ab40f240c3 screencopy: use drmFormat instead of wlr funcs
ref #4014
2023-12-01 00:23:48 +00:00
vaxerski
b394c1695c [gha] Nix: update wlroots 2023-11-30 18:53:34 +00:00
Vaxry
0a4c4da5f0 deps: update wlroots 2023-11-30 18:52:49 +00:00
Vaxry
b2f3623131 events: add keyPress and mouseAxis
fixes #4011 fixes #4008
2023-11-30 18:45:12 +00:00
François Conzelmann
5513eed64d managers: fix debug log using printf format (#4007)
Some debug messages where using printf format style to print variable
content instead of std::format format.
2023-11-30 15:20:08 +00:00
André Silva
29970228c5 nix: override libdrm to use newer version (#4003) 2023-11-30 11:40:14 +00:00
vaxerski
12ec549a18 screencopy: fix shm sharing if introspection required 2023-11-30 11:07:17 +00:00
Vaxry
9f2027be4b opengl: don't make a mirror buffer on fakeFrame 2023-11-30 10:15:02 +00:00
Vaxry
b9937484f4 screencopy: fix broken shm copying
fixes #4001
2023-11-30 10:14:35 +00:00
Vaxry
776f944619 opengl: fix missed makeEGLCurrent
fixes #3998
2023-11-30 02:19:27 +00:00
François Conzelmann
1fc1e4e9cb monitor: remove comma from monitor description (#3996)
this allows for monitor specific rules to work on monitor with comma on
their description

fixes #2457
2023-11-30 01:48:10 +00:00
vaxerski
e1258707ad [gha] Nix: update wlroots 2023-11-30 01:19:51 +00:00
Vaxry
d2c3b23ace deps: update wlroots 2023-11-30 01:18:55 +00:00
Vaxry
b80c72c7dd groupbar: fix crash in renderGradientTo
fixes #3985
2023-11-29 13:36:37 +00:00
Vaxry
3caaa483d4 configmgr: fix parsing of touchdevice groups
fixes #3992
2023-11-29 03:39:45 +00:00
Vaxry
e2f18f8c7f groupbar: more safety around gradient textures 2023-11-28 19:03:02 +00:00
Vaxry
99ca26d4eb hooksystem: fix missed log include 2023-11-26 18:33:53 +00:00
Vaxry
e416ab740d config: log info about logs before loading vars 2023-11-26 18:02:33 +00:00
MightyPlaza
7a0a5666d5 groupbar: allow reload and fix locked groupbar gradient (#3546)
modified:   src/config/ConfigManager.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.hpp
2023-11-26 17:59:49 +00:00
Vaxry
1778fb77e2 functionhooks: throw an exception on unsupported %rip usage
ref #2479, now will actually tell you what's wrong instead of crashing
2023-11-26 17:53:51 +00:00
Vaxry
adeb20ea11 opengl: tiled special require introspection 2023-11-26 16:42:04 +00:00
Vaxry
68e57b7ee3 renderer: proper full occlusion checks for back layer 2023-11-26 15:24:24 +00:00
Vaxry
408d96668d renderer: use occlusion checks for buffer clear 2023-11-26 15:06:42 +00:00
Vaxry
75e5799310 layer-shell: simulate mouse movement on unmap 2023-11-26 14:54:34 +00:00
Vaxry
9e2b939024 surface: avoid infinite pointer image resets
fixes #3729, should also #3968
2023-11-26 14:53:22 +00:00
Vaxry
cd96ceecc5 build: remove nv patches (#3957) 2023-11-26 02:58:57 +00:00
Vaxry
ad3f688648 opengl: check for introspection on special_blur 2023-11-25 19:44:34 +00:00
vaxerski
98c7ba4782 [gha] Nix: update wlroots 2023-11-25 19:25:44 +00:00
Vaxry
a5f64b48ca deps: downgrade wlroots to fix crashes 2023-11-25 19:24:59 +00:00
Vaxry
b281d8647a screencopy: use new isNvidia() for format 2023-11-25 17:56:38 +00:00
Vaxry
15b282ee0c opengl: fix window introspection check 2023-11-25 17:46:50 +00:00
Vaxry
6f733292bf renderer: nvidia checks and use glFinish on nvidia
fixes #3952 #3946
2023-11-25 17:45:08 +00:00
Vaxry
3fe6162af1 opengl: fix xray modes in introspection checks for ls
fixes #3953
2023-11-25 14:52:52 +00:00
Jibin George
2ce4b94a22 input: Fix custom acceleration profile config (#3948) 2023-11-25 14:39:21 +00:00
coldified
de95e956a0 meson: Update wlroots-meson-build.patch (#3950) 2023-11-25 14:32:01 +00:00
Junxuan Liao
929c44e361 input: pass mouse input to IME popups (#3922) 2023-11-25 14:27:57 +00:00
Vaxry
512a59731b config: default special_scale_factor to 1 2023-11-25 01:45:04 +00:00
Vaxry
a6eba91935 opengl: require introspection on mirroring
fixes #3939
2023-11-25 00:48:02 +00:00
Xavier
745b998587 renderer: Adding an option to disable first launch animation (#3933) 2023-11-24 21:45:59 +00:00
Vaxry
1a2a2da6aa renderer: fixup cursor scaling
fixes #3935
2023-11-24 21:30:28 +00:00
Vaxry
822775aa8c renderer: Fixup double rendering cases with special (#3928)
* fixup

* better fullscreen
2023-11-24 21:18:50 +00:00
Vaxry
d79cf0afe2 renderer: fix software cursors on nvidia
fixes #3926
2023-11-24 13:47:36 +00:00
Vaxry
334d0ae31b monitor: fix transform matrix calculations for transformed
fixes #3929
2023-11-24 13:45:10 +00:00
Vaxry
be3d635265 makefile: update wlroots sover 2023-11-24 13:08:58 +00:00
André Silva
f9ba5a0551 flake.lock: update nixpkgs and xdph 2023-11-24 15:06:35 +02:00
thejch
258c83f3bb exec: remove redundant environment variables from spawn (#3923) 2023-11-24 12:42:20 +00:00
vaxerski
aedcade68d opengl: better checking for required introspection
performance woo
2023-11-24 12:37:10 +00:00
vaxerski
802ab58f8a renderer: fix inverseOpaque calcs in renderWithBlur 2023-11-24 12:32:35 +00:00
Vaxry
af5d06593f cmakelists: fix old wlroots sover 2023-11-24 10:59:02 +00:00
Vaxry
2ebfd0c745 renderer: Move to a full Hyprland GL rendering pipeline (#3920)
Also updates wlroots
2023-11-24 10:54:21 +00:00
vaxerski
e40e486f61 renderer: better checks for special rendering in renderWorkspaceWindows
ref #3916 #3888
2023-11-23 11:31:52 +00:00
Vaxry
e55c5a916a renderer: make sure lastWindow has correct ws in renderWorkspaceWindows
fixes #3916 fixes #3888
2023-11-22 23:43:46 +00:00
Vaxry
45ebe0df8f config: fix red warn in default config
fixes #3917
2023-11-22 23:38:14 +00:00
Vaxry
812a3f6d78 renderer: fix double render of tiled on workspace switch
fixes #3889
2023-11-22 20:05:50 +00:00
Vaxry
44accacff9 config: add nomaximizerequest all to default cfg 2023-11-22 19:50:37 +00:00
Ngô Huy
d417370bb7 makefile: Add CXXFLAGS to hyprlctl's Makefile (#3913) 2023-11-22 11:02:36 +00:00
zakk4223
4729265284 hyprctl: Add 'layouts' command (#3895)
* Add hyprctl 'layouts' command

formatting

* Add getAllLayoutNames(), move m_vLayouts back to private

Formatting

* clang-format
2023-11-21 18:43:38 +00:00
thejch
572fd554b8 renderer: Fix floating clipbox (#3907)
* fix floating decoration clipbox scale

* use vecTransformedSize

* use workspace offset
2023-11-21 00:34:34 +00:00
thejch
7d1c8d827a shadow: add workspace offset to floating window shadow (#3906) 2023-11-20 22:34:28 +00:00
thejch
6d26199e1c renderer: fix floating window rendering when scale > 1 (#3901) 2023-11-20 12:13:09 +00:00
end-4
646f4bc638 general: add workspace gaps (#3877)
* anims: workspace gap

* anims: ws gaps: on swipe end

* anims: ws gaps: add missing parentheses

* format

* refractor

* Update Swipe.cpp

* format

* fix swipe to right

* ws gaps: move animations:workspace_gap to general:gaps_workspace

* ws gaps: general:gaps_workspace -> general:gaps_workspaces
2023-11-19 12:33:26 +00:00
MightyPlaza
7e0c90b92c groupbar: fix text pos with verical offset (#3893)
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2023-11-19 12:29:26 +00:00
MightyPlaza
add23a9ba2 group: fix dragging into floating groups (#3719)
* allow dragging into floating groups

modified:   src/Compositor.cpp
modified:   src/Compositor.hpp
modified:   src/layout/IHyprLayout.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* floating-only
modified:   src/layout/IHyprLayout.cpp
2023-11-19 12:29:01 +00:00
Dickby
3d89654254 vector: New operator overloads and small fix in Vector2D. (#3891) 2023-11-18 21:37:16 +00:00
Dickby
6ad5f26cfe layout: Don't update pseudoSize after window moved by mouse. (#3873) 2023-11-18 19:59:12 +00:00
Vaxry
89f6457a99 renderer: avoid rendering floating windows twice with special
fixes #3887
2023-11-18 19:53:45 +00:00
Vaxry
8b57a1973e internal: Allow floating windows on special (#3872)
* allow floating on special

* fix mistake

* fix clipbox
2023-11-18 17:00:24 +00:00
Vaxry
483302a2cd env: add HYPRLAND_NO_RT 2023-11-17 23:29:30 +00:00
Vaxry
a903dba858 cmake: include tracy cpp if set 2023-11-17 23:26:54 +00:00
Vaxry
395985f815 pluginmgr: fix double use of dlerror() 2023-11-17 22:24:52 +00:00
Vaxry
51282f964f plugins: make logging on error more verbose
ref #3874
2023-11-17 22:22:31 +00:00
TheAngusMcFire
db8f13291a hyprctl: add monitors all to report all connected monitors (#3730)
---------

Co-authored-by: Christian Rieger <christian.rieger@student.tugraz.at>
2023-11-17 16:01:21 +00:00
André Silva
30ad71ff36 nix: add libGL to build inputs 2023-11-17 17:36:18 +02:00
Vaxry
84bc0a73f6 compositor: drop unused vectorToWindow func 2023-11-17 15:30:04 +00:00
Dickby
1d9bfa60a1 opengl: Don't use wrong shader just because it's GLES (#3867) 2023-11-16 21:03:17 +00:00
Vaxry
a34e192433 renderer: clip floating boxes on slide anim
fixes #3514
2023-11-16 20:20:41 +00:00
Vaxry
4868d4dfd3 shadow: avoid fatal mutation of the windowBox for calcs
fixes #3865
2023-11-16 17:31:52 +00:00
vaxerski
859841f4d1 renderer: don't make snapshots of invisible windows on close 2023-11-16 12:24:07 +00:00
vaxerski
28ef18a921 shadow: avoid using glClear and don't draw behind window if ignore_window
fixes #3860
2023-11-16 11:42:53 +00:00
Dickby
91d6be1f09 groupbar: Fix position of groupbar titles on monitor scales != 1.0 (#3856) 2023-11-15 20:32:44 +00:00
thejch
9e3dccca76 keybinds: Close special workspace after moving windows out of it (#3649)
* use old monitor

* use pMonitor for for special workspace
2023-11-15 12:32:02 +00:00
Vaxry
81598b3dbd README: update showcase 2023-11-14 20:08:00 +00:00
Vaxry
e195e51c1b logging: move to an internal rolling log buffer
disables logging to the logfile by default
2023-11-14 20:06:04 +00:00
Vaxry
e8469f8b1b renderer: drop unnecessary spammy logs 2023-11-14 19:51:47 +00:00
Vaxry
49597688e9 windowrules: make idleinhibit dynamic 2023-11-14 15:46:57 +00:00
Vaxry
5edb4e4a30 decorations: recalc layout and positioner on add/remove 2023-11-13 16:42:58 +00:00
Ching Pei Yang
4d6fa6ed0c pluginapi: add touch event hooks (#3836) 2023-11-13 16:32:12 +00:00
Vaxry
016a7a9c9b hyprctl: use a rolling buffer for reading requests
fixes #3846
2023-11-13 16:30:37 +00:00
Vaxry
2e26542e3b renderer: use viewporter corrected size for uv calcs 2023-11-12 23:57:53 +00:00
Vaxry
68935ba9dc renderer: separate oversize uv calcs in dimensions 2023-11-12 23:27:52 +00:00
Vaxry
ba5bc5871f subsurfaces: damage window on subsurface size change 2023-11-12 23:06:31 +00:00
Vaxry
824ccd957b renderer: pass proper arg to main param of uv calcs
was fucking up some non-fitting subsurfaces
2023-11-12 22:59:19 +00:00
Vaxry
45e86d4fdf groupbar: translate box by workspace offset 2023-11-12 22:40:21 +00:00
Vaxry
0ba2e68704 deco-positioner: don't remove hidden windows' data 2023-11-12 17:02:42 +00:00
Dickby
e974d1fe98 shaders: Some more changes in rgb2hsl. (#3834) 2023-11-12 16:20:23 +00:00
Vaxry
47d46aa56c inputmgr: clean lists in ~dtor
ref #3558
2023-11-12 14:03:46 +00:00
Vaxry
65efde32c9 internal: make getPlusMinusKeywordResult return optional 2023-11-12 13:40:02 +00:00
Vaxry
69e314207d internal: replace INT_MAX with WORKSPACE_INVALID 2023-11-12 13:34:54 +00:00
Alessio Molinari
1bfd4a2bff output-layout: fix wlroots display handling (#3718) 2023-11-12 13:14:05 +00:00
Vaxry
91cbe93cf8 decoration: add NON_SOLID flag for shadow
fixes #3841
2023-11-12 13:01:23 +00:00
Vaxry
9afdd61ade props: bump ver to 0.32.3 2023-11-11 18:14:46 +00:00
Vaxry
f39a6ca17c decoration-positioner: improve stability 2023-11-11 18:07:56 +00:00
Vaxry
eab2799842 props: bump ver to 0.32.2 2023-11-11 17:24:04 +00:00
Dickby
6eb2abcb20 shaders: Small optimization in rgb2hsl. (#3831) 2023-11-11 17:20:18 +00:00
Vaxry
ae46fbafe5 xdg: set state maximized for all tiled windows
forces them to not draw stupid decorations of their own. Wlroots stopped doing it for us. Fixes #3830
2023-11-11 17:13:20 +00:00
Vaxry
52cf122a0a shadow: move workspace offset calcs to draw
fixes #3829
2023-11-11 16:15:23 +00:00
Dickby
844da8db56 shaders: Avoid calculating unused values in hsl2rgb. (#3827) 2023-11-11 15:43:31 +00:00
Vaxry
db82fc5b09 animationmgr: push shadow avs to ended on disabled 2023-11-11 15:23:33 +00:00
Vaxry
8180ca65a5 props: bump ver to 0.32.1 2023-11-11 15:20:21 +00:00
Vaxry
bea828ea45 decoration-positioner: improve extent handling 2023-11-11 15:18:17 +00:00
vaxerski
c5d1faf72d [gha] Nix: update wlroots 2023-11-11 14:53:18 +00:00
Vaxry
cc04b52ce1 deco-positioner: recalc after uncache 2023-11-11 14:52:27 +00:00
Vaxry
9b5e2e71e0 deps: update wlroots 2023-11-11 14:47:25 +00:00
Vaxry
9be6fbf5ea decorations: Decoration Positioner (#3800) 2023-11-11 14:37:17 +00:00
Dickby
7345b1a1ea shaders: Use clamp in doubleCircleSigmoid. (#3824) 2023-11-11 13:15:37 +00:00
Vaxry
e44d6de555 shadow: alpha treatment improvements 2023-11-11 00:52:40 +00:00
Vaxry
427153e86a xwayland: add half of delta to configure request sizes 2023-11-10 23:49:35 +00:00
Greatly Pleased
1e6e9b66a5 hyprctl: Fix build warnings (#3821) 2023-11-10 21:45:20 +00:00
Vaxry
92cb44ddb2 input: don't schedule repaint on cursor move if hardware cursors are in use 2023-11-10 18:49:15 +00:00
Vaxry
b8a615ffb8 xdg-shell: improve ack-configure handling
fixes #3807
2023-11-10 00:13:22 +00:00
Vaxry
8dd02eb5f3 xdg-shell: update reported size on ack_configure 2023-11-09 22:43:52 +00:00
Vaxry
14195835ef opengl: switch to black-and-white for alpha mattes
also fixes shadows on 10b
2023-11-09 22:11:54 +00:00
Vaxry
11432f69b9 opengl: remove unused alpha matte from shadow 2023-11-09 22:11:54 +00:00
Tobias Pisani
da6fa9cbd2 hyprctl: return group list in correct order (#3683) 2023-11-09 16:05:05 +00:00
Vaxry
c619e6976f screencopy: round boxes
fixes #3795
2023-11-08 12:43:07 +00:00
Vaxry
751d2851cc props: bump ver to 0.32.0 2023-11-07 21:06:38 +00:00
Vaxry
a0fcda301d layout: round box in mouseMove
fixes #3792
2023-11-07 20:47:09 +00:00
Vaxry
47654a84c2 main: set isShuttingDown after display dispatch reaches the end
relates to #3558
2023-11-07 14:53:56 +00:00
Visual-Dawg
29e0a7112e renderer: added new customization options to the blur shaders (#3650)
Adds to `decoration:blur:` `vibrancy` and `vibrancy_darkness`
2023-11-06 18:49:03 +00:00
Vaxry
a1b7a5a53d layout: fix various rounding errors
maybe finally will end #3761
2023-11-06 17:00:37 +00:00
Vaxry
ecf98069f6 layout: round window boxes after special scale factor
fixes #3761 again
2023-11-05 19:57:23 +00:00
David Leal
0476e1b498 groups: add option to configure font name (#3751) 2023-11-05 19:25:50 +00:00
Brett Alcox
a122271f09 includes: fix box headers (#3771) 2023-11-05 17:32:27 +00:00
Vaxry
600a128f83 compositor: allow windowfromregex tiled param 2023-11-05 16:22:43 +00:00
Vaxry
55825c301e compositor: allow windowfromregex floating param
fixes #3766
2023-11-05 16:21:47 +00:00
Vaxry
d8b7ded18c compositor: find windows in direction on floating 2023-11-05 16:18:41 +00:00
Vaxry
c4e1a9b13b box: use std::round instead of std::floor in ::round()
fixes #3761, possibly also #3511
2023-11-05 14:47:24 +00:00
Vaxry
9404972732 events: set reported size on floating map
fixes #3767
2023-11-05 14:12:55 +00:00
Vaxry
3b786419d8 subprojects: update tracy 2023-11-05 00:32:22 +00:00
Vaxry
92e535025e region: include box in the header 2023-11-04 23:12:08 +00:00
Vaxry
d3e5796ee1 layout: fix missed setWindowSize in no_gaps_when_only node apply
fixes #3758
2023-11-04 23:00:20 +00:00
Vaxry
56dec1c6a2 renderer: properly set currentFB on snapshot renders 2023-11-04 22:25:09 +00:00
Vaxry
931927de29 dwindle: move to CBox for expressing nodes 2023-11-04 21:45:34 +00:00
Vaxry
74cf2281dd binds: add movefocus_cycles_fullscreen
fixes #3738
2023-11-04 21:03:08 +00:00
Vaxry
2b07d54bc7 shadow: fix missed fullBox.scale 2023-11-04 20:11:22 +00:00
Vaxry
66a3719b86 renderer: allow transform enabling from outside opengl 2023-11-04 19:35:49 +00:00
Vaxry
64a084477e shadows: fix on transformed 2023-11-04 19:32:50 +00:00
Vaxry
7a09d24065 shadow: fix ignore_window false 2023-11-04 17:45:31 +00:00
Vaxry
a3e20d2d5f wlsurface: fix small detection 2023-11-04 17:39:56 +00:00
Vaxry
32b3d2b456 includes: include vector2d for sharedDefs 2023-11-04 17:27:21 +00:00
Vaxry
447c173cad includes: include sharedDefs in includes.hpp 2023-11-04 17:04:10 +00:00
Vaxry
55b4f84fea Internal: Hyprland box implementation (#3755)
* box impl

* remove unused operators

* missed applyfromwlr
2023-11-04 17:03:05 +00:00
Vaxry
73e78f05ad Decos: Window decoration flags, shadow improvements (#3739) 2023-11-04 13:10:52 +00:00
Vaxry
54e51b7acf events: better adjust to unreported sizes for xwayland apps
fixes #3745
2023-11-04 13:09:33 +00:00
Vaxry
200cccdd3b events: set reported size on the end of mapping
fixes #3743
2023-11-03 22:47:12 +00:00
Vaxry
15b25d5850 renderer: render oversized blur with respect to fade alpha 2023-11-03 21:58:02 +00:00
Vaxry
21ba8b363e fractional-scale: fix addon existence check 2023-11-03 21:02:12 +00:00
Vaxry
9d2a5fb417 renderer: improvements to wayland surface small() detection 2023-11-03 19:12:09 +00:00
thejch
ed3d5053b2 Master: fix drop_at_cursor when there are only two windows (#3734)
* make drop_at_cursor work when dragging into a one-window workspace

* fix drop_at_cursor when new_is_master is enabled
2023-11-03 17:02:59 +00:00
Vaxry
93a2ac9de4 fractional-scale: post error on taken fs objects
fixes #3735
2023-11-03 15:51:43 +00:00
pastalian
49fdffacea renderer: fix legacy_renderer build (#3732)
In GLES2, `GL_RGB10_A2` and `GL_UNSIGNED_INT_2_10_10_10_REV` are defined as
`GL_RGB10_A2_EXT` and `GL_UNSIGNED_INT_2_10_10_10_REV_EXT` respectively.
2023-11-03 15:47:20 +00:00
vaxerski
0f6e530798 layout: better storage for no fullscreen checks var
fixes #3731
2023-11-03 12:19:23 +00:00
Vaxry
88b47dfa83 xdg: use std::ceil for sending scale to surfaces
fixes #3724
2023-11-02 23:29:47 +00:00
Vaxry
ba9e7814b0 input: simulate movement only if mouse is over in open/close window
fixes #3726
2023-11-02 21:36:16 +00:00
thejch
f10996b575 master: fix moving window between monitors (#3721) 2023-11-02 17:20:32 +00:00
q234rty
ef90d1eaaf Map cmake None to meson's plain
empty is not a build type that exists in meson.

I have no idea why I typed "empty" in #3614 ...

Fixes: ba5f1d8783
2023-11-02 19:20:09 +02:00
Dashie
062f749450 xdg: send unconstrain events after a popup reposition (#3716)
* fix: Use unconstrain_from_box after reposition request

* chore: Remove unused include

* chore: remove variable specifier
2023-11-01 20:31:52 +00:00
Vaxry
a4db48b46b input: simulate mouse movement to fix focus on open/close window
fixes #3679
2023-11-01 19:50:32 +00:00
MightyPlaza
c44e255194 group: fixes for dragging groups onto groupbars (#3708)
* fixes for dragging groups onto groupbars

modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* use onWindowRemoved()
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2023-11-01 19:13:39 +00:00
Vaxry
21e9313c10 Core: various unsafe state improvements (#3713)
Fixes #3637
2023-11-01 18:53:36 +00:00
Vaxry
7b32b4214d layout: avoid redundant size sets on new fullscreen 2023-11-01 01:28:43 +00:00
Vaxry
6914103289 cmake: set asan for only hyprland not others 2023-10-31 21:45:27 +00:00
Vaxry
ab5497a0c9 input: properly track xdg surfaces' geometry in vectorToSurfaceLocal
fixes #3703
2023-10-30 19:36:45 +00:00
André Silva
f48b3774a2 nix: use mesonAutoFeatures attribute 2023-10-30 21:00:04 +02:00
Vaxry
1c9d6b94d1 renderer: fix small surface's blur region being offset by monitor coords 2023-10-30 16:20:30 +00:00
Vaxry
4b592d0819 renderer: properly pass 10-bit formats to opengl 2023-10-30 15:56:16 +00:00
MightyPlaza
a1924ae435 internal: create canBeGroupedInto() (#3693)
modified:   src/Window.cpp
modified:   src/Window.hpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2023-10-30 14:54:12 +00:00
Vaxry
cb6cfde6e8 window: update window reported size on damage events 2023-10-30 14:49:49 +00:00
Vaxry
8e91c038db renderer: use optional for cursor surface storing
because nullptr is a valid surface.

fixes #3692
2023-10-30 00:18:40 +00:00
Vaxry
86318ce04f input: let input-grabbing decos have prio over resize on border 2023-10-29 23:37:12 +00:00
Vaxry
59d6a12a7e config: fixup usage of plugin in handles
would filter calls to keyword plugin: and stuff
2023-10-29 22:35:26 +00:00
Vaxry
935c90915a pluginapi: fixup get_hash functions 2023-10-29 21:21:54 +00:00
Vaxry
b95c0c318e renderer: fixup blend disable conditions in renderSurface
Fixes #3680
2023-10-29 20:54:14 +00:00
MightyPlaza
9abfa9efc6 input: handle mouse on decorations (#3560) 2023-10-29 20:14:47 +00:00
Vaxry
7a5234a0cc input: better cursor image infrastructure
Improves handling of cursor images/surfaces
Fixes an issue with decos and cursors
Fixes #3471
2023-10-29 18:09:05 +00:00
Vaxry
af9440152e pluginapi: add a config keyword adding method 2023-10-29 16:59:50 +00:00
Vaxry
7f4b0aaadc hyprctl: add focusHistoryID to hyprctl clients
fixes #3661
2023-10-28 17:22:42 +01:00
vaxerski
4a4e13f8ac events: ignore takes_over_fullscreen for floating windows
fixes #3622. Floating windows should not respect the config option, they should be shown on top. That option is meant for tiled only
2023-10-27 12:45:17 +01:00
Antoine Dutot
1d47e2c408 input: fix force_zero_scaling for tablet and touch (#3644)
* Attempt to fix force_zero_scaling for tablet and touch for XWayland apps.

* Formated using clang-format.

* Simpler way to compute touch origin.
2023-10-27 10:15:29 +01:00
Vaxry
47256a6ed8 input: remove incorrect check in re-enter
fixes #3659
2023-10-26 22:21:13 +01:00
Vaxry
732b058489 input: send motion events on focusWindow if follows_mouse is 0 2023-10-26 22:17:49 +01:00
Vaxry
92cf1c2337 input: fix mouse down handling on unset last surface
fixes #3659
2023-10-26 18:54:19 +01:00
Tyler Schneider
07714dd5bd input: Apply scaling to cursorPosOnActivate position (#3664) 2023-10-26 02:05:10 +01:00
fufexan
5cc33b4e8c [gha] Nix: update inputs 2023-10-26 00:03:23 +00:00
Vaxry
b0b88a63b6 renderer: fix missed box scale 2023-10-25 22:38:14 +01:00
Vaxry
5b0dc779ed renderer: blur properly behind small surfaces 2023-10-25 22:20:58 +01:00
Vaxry
8991be671f renderer: respect viewporter dest on base surfaces 2023-10-25 22:05:04 +01:00
Vaxry
6650e4ba85 compositor: remove old comment 2023-10-25 18:32:04 +01:00
vaxerski
a1b138a625 [gha] Nix: update wlroots 2023-10-24 23:28:24 +00:00
Vaxry
df00727310 deps: update wlroots 2023-10-25 00:27:16 +01:00
Vaxry
03771d3aa9 windowrules: add nearestneighbor 2023-10-24 21:29:03 +01:00
André Silva
50a80efad5 flake.lock: update nixpkgs and xdph 2023-10-24 21:06:00 +03:00
Vaxry
14a3c939ce hyprctl: log monitor id in workspaces request
fixes #3640
2023-10-24 01:03:40 +01:00
Vaxry
aeb8c8fc70 internal: honor close_special_on_empty on moveToWorkspace
fixes #3602
2023-10-24 00:58:44 +01:00
Vaxry
616ff343b7 input: fix warn 2023-10-24 00:53:59 +01:00
Vaxry
2f6729f557 example: add special workspace to default config 2023-10-24 00:50:45 +01:00
Vaxry
015664eb4c compositor: do not process fullscreen events in unsafe 2023-10-23 00:53:33 +01:00
Vaxry
98059b52d7 layershell: support ON_DEMAND keyboard mode
fixes #2264
2023-10-22 23:32:55 +01:00
Brenno Lemos
b135bd6cd4 workspacerules: Add new optional "default command" for auto-launching apps on new workspaces (#3559) 2023-10-22 23:11:03 +01:00
Vaxry
59f27e7f57 compositor: send preferred scale and transform events to surfaces
fixes #3635
2023-10-22 16:58:06 +01:00
Vaxry
edb26e0306 cmake: remove old definitions from cmakelists 2023-10-22 12:25:26 +01:00
Vaxry
d0367d8560 renderer: use preOffset pos instead of offset
offset is not passed to transformers, so if they change pos, decos will be in the wrong place
2023-10-21 19:51:14 +01:00
Vaxry
95db9108e5 transformers: allow modifying renderdata pre-pass 2023-10-21 19:25:44 +01:00
Vaxry
a61eb7694d hooksystem: add callbackinfo struct and cancellable events 2023-10-21 14:52:43 +01:00
Vaxry
c6233a790f renderer: avoid drawing invisibly small surfaces in renderSurface 2023-10-21 14:20:06 +01:00
Vaxry
92311d260a renderer: add transformers 2023-10-21 14:15:56 +01:00
Roberto Previdi
af72404259 hyprctl: add a query for workspace rules (#3630) 2023-10-21 02:28:34 +01:00
Vaxry
4a79718fe8 renderer: fix shimmers when manual resizing 2023-10-20 20:32:47 +01:00
Vaxry
7f35f33b4c surface/wayland: conform to small surface requirements
do not stretch small surfaces, instead center them if they fit.
2023-10-20 20:15:57 +01:00
thejch
bab2f6a664 master: fix movewindow across monitors (#3628) 2023-10-20 18:01:04 +01:00
thejch
bb9d0aed5b compositor: Fix a lack of a check for workspace equality on same monitors in getInDirection (#3625)
* prevent movefocus and movewindow from going into main workspace when in special workspace

* compare workspace IDs instead

* change comparison and make code more readable
2023-10-20 10:53:37 +01:00
Vaxry
386708563c workspacerules: search for special properly
fixes #3537
2023-10-19 16:04:59 +01:00
q234rty
ba5f1d8783 cmake: Map cmake None to meson empty (#3614)
While not explicitly mentioned by the cmake documentation, cmake
upstream seems to think of None as a vaild CMAKE_BUILD_TYPE. [1]
Handle it properly by mapping it to meson's empty.

[1]: ce1cadd35a
2023-10-19 15:59:24 +01:00
Campbell Barton
d994e6aea6 cmake: Fix error building without CMAKE_BUILD_TYPE being set (#3590)
* Fix error building without CMAKE_BUILD_TYPE being set

This resolves the error building without a CMAKE_BUILD_TYPE.
CMake Error at CMakeLists.txt:36 (string):                                                                                                                                                          
  string no output variable specified

* CMake: convert CMake's build type to meson build type

Fix error when the CMAKE_BUILD_TYPE variable isn't set & properly convert the build type to mesons build type.
2023-10-19 15:00:58 +01:00
Vaxry
6e15590e98 shaders: support changing the outer radius of borders independently 2023-10-19 14:05:02 +01:00
memchr
d70cc88dab meson: generate version.h before install_headers (#3612)
Otherwise, meson install would not install version.h as a header in a
clean build.
2023-10-19 00:09:06 +01:00
Vaxry
a0b675ec9e binds: add ignoreMods flag 2023-10-17 20:10:07 +01:00
André Silva
784f8a88fb input: don't reset cursor movement timer on simulateMouseMovement (#3595)
Fixes #2570
2023-10-17 17:00:39 +01:00
thejch
20e7ccd480 master: fix resizing wrong window behind in special workspace (#3591) 2023-10-17 12:06:16 +01:00
outfoxxed
210be10c92 meson/nix: Fix meson.build nix patch (#3594)
`meson.build` was modified in #3547 but `nix/patches/meson-build.patch`
was not updated to reflect the changes.
2023-10-17 12:05:38 +01:00
eriedaberrie
421f5fb221 events: make new windows taking over fullscreen keep the existing mode (#3588) 2023-10-17 11:41:27 +01:00
Jan Beich
93676f91a0 renderer: cast std::clamp args to be of the same type (#3589)
src/render/OpenGL.cpp:1769:41: error: no matching function for call to 'clamp'
    const auto         FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, -1L, 2L);
                                        ^~~~~~~~~~
/usr/include/c++/v1/__algorithm/clamp.h:38:1: note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('int64_t' (aka 'long long') vs. 'long')
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi)
^
/usr/include/c++/v1/__algorithm/clamp.h:27:1: note: candidate function template not viable: requires 4 arguments, but 3 were provided
clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi, _Compare __comp)
^
2023-10-17 01:29:06 +01:00
memchr
54e1c2ccbd fix(build): do not include wlr headers directly, use includes.hpp instead. (#3587) 2023-10-16 17:39:12 +01:00
Vaxry
5b8cfdf2ef props: bump ver to v0.31.0 2023-10-16 17:17:00 +01:00
Vaxry
8af3e7beeb cmake: search for systemd headers first 2023-10-15 22:35:56 +01:00
Alexander Seleznev
1f582457cf Debug: respect logging settings for wlr (#3584) 2023-10-15 19:19:07 +01:00
Philip Damianik
442209942f cleanup: Replace find() with C++20 starts_with(), ends_with() and contains() (#3572)
* Replace find() with C++20 starts_with() and ends_with()

* Replace find() with C++20 contains()
2023-10-15 19:07:23 +01:00
memchr
43b39e0bc6 build: include version.h in PluginAPI.hpp (#3571) 2023-10-15 12:22:51 +01:00
Ali Rizvi
261c594458 makefile: fix using -d test for a binary file AND re-add missing portals conf (#3570)
* fix: Makefile test
modified: src/Makefile

* add: hyprland-portals.conf cp to Makefile
modified: src/Makefile
2023-10-14 22:30:17 +01:00
memchr
962a0de01a meson: use ln with force flag (#3568) 2023-10-14 20:59:11 +01:00
Vaxry
21b5cf402a makefile: warn users when doing sudo make install 2023-10-14 18:58:23 +01:00
Vaxry
d4e4931008 build: CMake pipeline improvements (#3564) 2023-10-14 18:48:05 +01:00
Vaxry
d5a572bd39 Plugin API: Add version query (#3545) 2023-10-14 18:47:43 +01:00
Vaxry
424c9a7e70 render: stop locking SWC on tearing 2023-10-13 22:11:21 +01:00
Michał Lewandowski
3a61350286 notifications: Schedule a frame on notification creation (#3556) 2023-10-13 00:05:05 +01:00
vaxerski
e4bcd2e2da monitor: ensure vrr on workspace change 2023-10-12 17:26:35 +01:00
Vaxry
06cc42441c build: guard execinfo.h via build systems (#3547)
* guard execinfo via cmake

* libexecinfo -> execinfo macro name

* meson: add execinfo check

* move check in cmake up

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-10-11 22:27:53 +01:00
Bernhard M. Wiedemann
5dc7161b1d meson: Sort input file list (#3550)
so that hyprland builds in a reproducible way
in spite of non-deterministic filesystem readdir order.

See https://reproducible-builds.org/ for why this is good.
This patch was done while working on reproducible builds for openSUSE.

Co-authored-by: Bernhard M. Wiedemann <bernhard+gitcommit lsmod.de>
2023-10-11 19:49:42 +01:00
Jan Beich
0cf3d5b39a build: Unbreak Meson build on FreeBSD (#3549)
* generateVersion.sh: move cp(1) flags before arguments

POSIX and BSD cp(1) don't support flags after arguments e.g.,

cp: -fr is not a directory

* generateVersion.sh: switch to sed(1) -i for better compatibility

On BSDs awk is usually The One True Awk thus doesn't support `-i inplace`.

awk: unknown option -i ignored
awk: can't open file {sub(/@HASH@/,"1234567890abcdef")}1
 source line number 1
2023-10-11 19:38:34 +01:00
memchr
34455844e9 groups: ensure consistency in dispatcher behavior with global group lock (#3531) 2023-10-11 11:13:12 +01:00
vaxerski
d83357f497 internal: use pragma once in version.h 2023-10-10 21:03:07 +01:00
Mihai Fufezan
a0038fa161 CI/release: update version generation 2023-10-10 22:53:23 +03:00
Vaxry
914851b91a internal: better versioning (#3543)
* better versioning

* meson: better versioning

* nix: better versioning

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-10-10 20:21:57 +01:00
vaxerski
3219c84433 makefile: set perms for built subprojects in pluginenv 2023-10-10 20:03:02 +01:00
vaxerski
1a0909aa20 makefile: set 777 for /build perms 2023-10-10 20:01:02 +01:00
vaxerski
bf94df7b00 makefile: set correct perms on includes 2023-10-10 19:59:42 +01:00
MightyPlaza
d537815d43 groups: create group and groupbar config sections (#3522)
* config: create group section

modified:   src/Compositor.cpp
modified:   src/config/ConfigManager.cpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/managers/KeybindManager.cpp
modified:   src/managers/input/InputManager.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* separate group border and groupbar colors

modified:   src/Compositor.cpp
modified:   src/config/ConfigManager.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* add new line
modified:   config/ConfigManager.cpp
2023-10-10 17:42:45 +01:00
Philip Damianik
ac1bd47653 dispatchers: Add option to center the cursor on the focused window when switching workspaces (#3528)
* Add option for centering on workspace when switching workspace

* Add option for centering on workspace when switching workspace

---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-10-10 17:34:28 +01:00
vaxerski
8abb6e1cee screencopy: enhance error logging 2023-10-09 23:51:59 +01:00
Roberto Previdi
df0c8e0f7a workspacerules: Add a persistent workspace rule (#3530)
* add persistent flag to workspace rules

* get rid of unused ws->indestructible flag
2023-10-09 20:28:22 +01:00
Michał Lewandowski
7f8e0a1318 dwindle: Avoid rounding errors in window position (#3524)
* Avoid rounding errors in window position

* Fix naming scheme

* Fix unwanted window movement
2023-10-09 12:28:58 +01:00
MightyPlaza
499df49f7b dispatchers: fix extra monitor offset when snapping floating (#3520)
modified:   src/managers/KeybindManager.cpp
2023-10-08 13:49:15 +01:00
vaxerski
728a8bb48e config: mention force_default_wallpaper in default configs 2023-10-07 15:55:33 +01:00
DaniD3v
7d7565e7ec renderer: add force_wallpaper instead of no_hypr_chan (#3459) 2023-10-07 14:08:38 +01:00
vaxerski
38e242953d animationmgr: damage floating windows on workspace anim
fixes #3514
2023-10-07 02:28:23 +01:00
Jeremy Huang
61d3d4dee7 master: fix drop_at_cursor on workspace 2 (#3512) 2023-10-06 22:33:11 +01:00
ItsDrike
1afb00a01b pluginapi: remove starting newline in demangled func name (#3502) 2023-10-06 14:01:40 +01:00
vaxerski
cb7dd1ac6e layershell: avoid configure on unchanged size
fixes #3496
2023-10-06 14:00:05 +01:00
vaxerski
4b3efc73c5 events: fixup empty events not being sent on unmap
Ref #3506 https://github.com/hyprwm/Hyprland/issues/3506#issuecomment-1749508111
2023-10-06 01:11:47 +01:00
vaxerski
24c04a8b7c crashreporter: avoid using empty CACHE_HOME 2023-10-05 16:57:54 +01:00
vaxerski
3d1a167960 wsrules: check for workspace ID matches on numbered workspaces
fixes #3494
2023-10-05 01:31:13 +01:00
vaxerski
1b99a69dc1 [gha] Nix: update wlroots 2023-10-04 14:06:09 +00:00
vaxerski
8e0eafc502 deps: update wlroots 2023-10-04 15:05:20 +01:00
vaxerski
e689b1ba11 [gha] Nix: update wlroots 2023-10-04 14:01:11 +00:00
vaxerski
322c5cc4b9 layout: add new_window_takes_over_fullscreen mode 2
fixes #3486
2023-10-04 15:00:24 +01:00
vaxerski
230356012b renderer: extract solitary check into frame handler 2023-10-04 13:48:44 +01:00
vaxerski
4531717f3e renderer: considerable fixes to repaint logic 2023-10-04 13:39:40 +01:00
vaxerski
f8c18ff797 renderer: fixup solitary conditions 2023-10-04 13:30:13 +01:00
vaxerski
f803be3d31 input: fixup frame schedule conditions on mouse move 2023-10-04 12:57:37 +01:00
vaxerski
2901bb0d2f renderer: add further conditions to solitary recheck 2023-10-04 09:44:07 +01:00
Jeremy Huang
d61e4f9ad7 master: add option to drag and drop windows at cursor position (#3465)
* add option to drag and drop a window at cursor position

* use getWindowIdealBoundingBoxIgnoreReserved
2023-10-03 22:16:57 +01:00
vaxerski
b784931e67 [gha] Nix: update wlroots 2023-10-02 15:48:58 +00:00
vaxerski
fcab2a4358 deps: update wlroots 2023-10-02 16:47:54 +01:00
vaxerski
b814ba98a7 misc: add misc:new_window_takes_over_fullscreen
referenced in #3474
2023-10-02 16:27:32 +01:00
vaxerski
50fecf084d events: set new window alpha to 0 if it's tiled behind fullscreen
fixes #3474
2023-10-02 16:22:39 +01:00
vaxerski
5ffb1032e1 events: don't focus new window if spawned behind fullscreen
fixes #3473
2023-10-02 14:09:23 +01:00
vaxerski
fc1d7acd9a renderer: fix missing check for surfaceCount in rechecking solitary
closes #3470
2023-10-02 14:02:19 +01:00
gnusenpai
778bdf730f internal: Add GTK portal as fallback (#3469)
needed for XDP 1.17+ since the fallback to the GTK portal was removed
2023-10-02 10:51:15 +01:00
vaxerski
763bb2d3bc renderer: remove old debug log 2023-10-02 10:50:33 +01:00
vaxerski
9ec656a37d makefile: make binaries 755 instead of 711
fixes #3466
2023-10-01 14:09:50 +01:00
memchr
161fee1d82 internal: include missing header (#3464) 2023-10-01 12:26:10 +01:00
vaxerski
a2a29a60e5 tearing: cleanup vars and avoid rendering before drm is ready 2023-09-30 17:07:50 +01:00
vaxerski
e2b72b2975 renderer: avoid spamming software cursor locks 2023-09-30 16:55:41 +01:00
Dickby
772c7d1d3c shaders: Remove redundant clamp of smoothsteps return value. (#3456) 2023-09-30 13:12:48 +01:00
vaxerski
6a4643842d hyprctl: log activelyTearing for monitors 2023-09-30 13:12:04 +01:00
vaxerski
a05076a7ee log: log wlr errors regardless of env 2023-09-30 13:12:04 +01:00
vaxerski
b8f8912db2 renderer: lock software cursors while tearing 2023-09-30 13:12:01 +01:00
vaxerski
86e8ed038f includes: add missing log include to animatedvariable 2023-09-30 02:09:08 +01:00
vaxerski
c298439433 renderer: nuke multisample_edges in favor of a faster algorithm 2023-09-30 01:41:05 +01:00
vaxerski
495d4f2d11 shaders: improve border rounding 2023-09-30 01:35:05 +01:00
vaxerski
7b002d609b input: fix warn 2023-09-29 20:06:20 +01:00
vaxerski
4daa515700 watchdog: don't hang on exit 2023-09-29 20:05:43 +01:00
vaxerski
e07e64458e input: fix unused variable warn 2023-09-29 17:55:41 +01:00
vaxerski
a44ab7748f renderer: optimize render pipeline when there is a solitary client 2023-09-29 17:51:07 +01:00
vaxerski
ab11bd2085 watchdog: remove thread on destroy 2023-09-29 17:33:26 +01:00
vaxerski
27cd7ef0c9 watchdog: initialize after config 2023-09-29 17:04:20 +01:00
vaxerski
9cc614d096 internal: add a watchdog
a watchdog will abort processing a signal if a timeout specified via debug:watchdog_timeout is reached.
2023-09-29 16:38:13 +01:00
vaxerski
3f09b14381 makefile: fixup permissions on installed binaries 2023-09-28 22:13:21 +01:00
vaxerski
453128ee0e examples/readme: mention tearing 2023-09-28 22:09:14 +01:00
Vaxry
88b63a00b6 renderer: Tearing implementation (#3441) 2023-09-28 21:48:33 +01:00
vaxerski
1e513e25d5 makefile: fix wrong path in chmod 2023-09-28 20:10:21 +01:00
vaxerski
d48c11cc3f input: minor constraint fixes
Better adjust to constraints that do not set a hint. Fixes #3436
2023-09-28 20:02:00 +01:00
vaxerski
08595f839b [gha] Nix: update wlroots 2023-09-28 17:06:54 +00:00
vaxerski
eab5967ef4 deps: update wlroots 2023-09-28 18:06:07 +01:00
vaxerski
280f385cf8 makefile: fix permissions on files 2023-09-28 18:05:55 +01:00
vaxerski
1f4eab176e layout: don't discard fullscreen on new tiling 2023-09-28 17:49:40 +01:00
Cascade aka. Isoheptane
6d7dc70f66 input: Add map to region options for tablets (#3425)
* Add region remap for tablets

* Fix code style
2023-09-27 19:49:30 +01:00
vaxerski
ffacd2efd1 hyprctl: fix crash in unsafe state with activeworkspace
fixes #3419
2023-09-25 15:00:28 +01:00
vaxerski
3b657257ec layout: check for specialworkspace in floating toggle 2023-09-24 23:20:15 +01:00
Legorel
6bdc45e9ce config: restore configCurrenPath after sourcing file (#3339) 2023-09-24 23:11:34 +01:00
Vaxry
46d66f4bcc internal: Further unsafe state improvements (#3404)
Instead of allowing Hyprland to sit in a state where there are no monitors, which various parts of the code don't like, we create a fake headless output on all monitor disconnect, and then remove it when a monitor appears
2023-09-24 18:04:38 +01:00
vaxerski
352ceb1117 config: trim spaces in the rule field in windowrulev2 2023-09-24 13:35:36 +01:00
vaxerski
2c4a06eb54 deps: update hyprland-protocols 2023-09-23 20:01:43 +01:00
vaxerski
3b445ec849 input: avoid using the wrong surface in drag focus force 2023-09-23 17:20:39 +01:00
memchr
8252957392 dispatchers: support number as arg in changegroupactive (#3329) 2023-09-23 13:36:40 +01:00
Jan Beich
8637bfb1b7 build: Unbreak build without precompiled headers (#3400)
* helpers: add missing header after 2e34548aea

src/helpers/VarList.cpp: In constructor 'CVarList::CVarList(const std::string&, size_t, char, bool)':
src/helpers/VarList.cpp:19:34: error: 'removeBeginEndSpacesTabs' was not declared in this scope
   19 |             m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~
src/helpers/VarList.cpp:23:30: error: 'removeBeginEndSpacesTabs' was not declared in this scope
   23 |         m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~

* helpers: add missing C linkage after 0dbd997003

ld.lld: error: undefined symbol: wlr_region_scale(pixman_region32*, pixman_region32 const*, float)
>>> referenced by Region.cpp
>>>               src/Hyprland.p/helpers_Region.cpp.o:(CRegion::scale(float))
>>> did you mean: extern "C" wlr_region_scale
>>> defined in: /usr/lib/libwlroots.so.12032
2023-09-23 13:31:37 +01:00
vaxerski
de95089552 input: fixup mouse down refocus conditions
fixes #3402
2023-09-23 13:30:53 +01:00
vaxerski
9c00381dfc hyprctl: avoid .pop_back() on empty string 2023-09-23 13:26:35 +01:00
vaxerski
fb80cbe415 input: properly track mouse focus on drag operations 2023-09-23 01:28:45 +01:00
190 changed files with 10374 additions and 4695 deletions

1
.clang-format-ignore Normal file
View File

@@ -0,0 +1 @@
subprojects/**/*

View File

@@ -9,12 +9,23 @@ body:
---
- type: input
- type: textarea
id: ver
attributes:
label: Hyprland Version
description: "Paste here the output of `hyprctl version`."
placeholder: Hyprland, built from branch main at commit...
description: "Paste here the output of `hyprctl version`. For hyprland after 0.34.0, paste `hyprctl systeminfo` instead."
value: "<details>
<summary>System/Version info</summary>
```sh
<Paste the output of the command here>
```
</details>"
validations:
required: true

75
.github/actions/setup_base/action.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: "Setup base"
inputs:
INSTALL_XORG_PKGS:
description: 'Install xorg dependencies'
required: false
default: false
runs:
using: "composite"
steps:
- name: Get required pacman pkgs
shell: bash
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy \
base-devel \
cairo \
clang \
cmake \
git \
glm \
glslang \
go \
jq \
libc++ \
libdisplay-info \
libdrm \
libepoxy \
libfontenc \
libglvnd \
libinput \
libliftoff \
libxcvt \
libxfont2 \
libxkbcommon \
libxkbfile \
lld \
meson \
ninja \
pango \
pixman \
pkgconf \
scdoc \
seatd \
systemd \
tomlplusplus \
wayland \
wayland-protocols \
xcb-util-errors \
xcb-util-renderutil \
xcb-util-wm
- name: Get Xorg pacman pkgs
shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true'
run: |
pacman --noconfirm --noprogressbar -Sy \
xorg-fonts-encodings \
xorg-server-common \
xorg-setxkbmap \
xorg-xkbcomp \
xorg-xwayland
- name: Checkout Hyprland
uses: actions/checkout@v4
with:
submodules: recursive
# Fix an issue with actions/checkout where the checkout repo is not mark as safe
- name: Mark directory as safe for git
shell: bash
run: |
git config --global --add safe.directory /__w/Hyprland/Hyprland

View File

@@ -8,29 +8,20 @@ jobs:
container:
image: archlinux
steps:
- name: Get required pacman pkgs
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff
- name: Set up user
run: |
useradd -m githubuser
echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
- name: Install libdisplay-info from the AUR
run: |
su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar"
- name: Fix permissions for git
run: |
git config --global --add safe.directory /__w/Hyprland/Hyprland
- name: Checkout Hyprland
uses: actions/checkout@v3
- name: Checkout repository actions
uses: actions/checkout@v4
with:
submodules: recursive
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
with:
INSTALL_XORG_PKGS: true
- name: Build Hyprland
run: |
git submodule sync --recursive && git submodule update --init --force --recursive
make all
- name: Compress and package artifacts
run: |
mkdir x86_64-pc-linux-gnu
@@ -39,12 +30,13 @@ jobs:
mkdir hyprland/assets
cp ./LICENSE hyprland/
cp build/Hyprland hyprland/
cp hyprctl/hyprctl hyprland/
cp subprojects/wlroots/build/libwlroots.so.12032 hyprland/
cp build/hyprctl/hyprctl hyprland/
cp subprojects/wlroots/build/libwlroots.so.13032 hyprland/
cp build/Hyprland hyprland/
cp -r example/ hyprland/
cp -r assets/ hyprland/
tar -cvf Hyprland.tar.xz hyprland
- name: Release
uses: actions/upload-artifact@v3
with:
@@ -57,28 +49,19 @@ jobs:
container:
image: archlinux
steps:
- name: Download dependencies
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd cmake jq python libliftoff
- name: Set up user
run: |
useradd -m githubuser
echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
- name: Install libdisplay-info from the AUR
run: |
su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar"
- name: Checkout Hyprland
uses: actions/checkout@v3
- name: Checkout repository actions
uses: actions/checkout@v4
with:
submodules: true
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: |
meson obj-x86_64-pc-linux-gnu \
-Ddefault_library=static
run: meson setup build -Ddefault_library=static
- name: Compile
run: ninja -C obj-x86_64-pc-linux-gnu
run: ninja -C build
noxwayland:
name: "Build Hyprland in pure Wayland (Arch)"
@@ -86,23 +69,36 @@ jobs:
container:
image: archlinux
steps:
- name: Download dependencies
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd libliftoff
- name: Set up user
run: |
useradd -m githubuser
echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
- name: Install libdisplay-info from the AUR
run: |
su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar"
- name: Checkout Hyprland
uses: actions/checkout@v3
- name: Checkout repository actions
uses: actions/checkout@v4
with:
submodules: true
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja
run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja
- name: Compile
run: make config && make release
run: make release
clang-format:
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

View File

@@ -10,7 +10,6 @@ jobs:
matrix:
package:
- hyprland
- hyprland-nvidia
- xdg-desktop-portal-hyprland
runs-on: ubuntu-latest
@@ -27,4 +26,4 @@ jobs:
name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix build -L ${{ matrix.command }}
- run: nix build .#${{ matrix.package }} -L

View File

@@ -15,10 +15,15 @@ jobs:
with:
submodules: recursive
- name: Generate version
id: genversion
run: |
bash -c scripts/generateVersion.sh
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak
- name: Create tarball with submodules
id: tar
run: |
sed -i "1s/^/#define GIT_COMMIT_HASH \"$(git rev-parse HEAD)\"\n#define GIT_TAG \"$(git describe --tags)\"\n/" ./src/macros.hpp
mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz *
- id: whatrelease

View File

@@ -24,13 +24,13 @@ jobs:
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
codeql:
name: CodeQL
runs-on: ubuntu-latest
container:
image: archlinux
permissions:
actions: read
contents: read
@@ -42,34 +42,25 @@ jobs:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Init Hyprland build
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff
useradd -m githubuser
echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar"
git config --global --add safe.directory /__w/Hyprland/Hyprland
- name: Checkout Hyprland
uses: actions/checkout@v3
- name: Setup base
uses: ./.github/actions/setup_base
with:
submodules: recursive
INSTALL_XORG_PKGS: true
- name: Build Hyprland
run: |
git submodule sync --recursive && git submodule update --init --force --recursive
make all
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:

28
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: '7 */4 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.STALEBOT_PAT }}
stale-issue-label: 'stale'
stale-pr-label: 'stale'
operations-per-run: 40
days-before-close: -1

2
.gitignore vendored
View File

@@ -29,3 +29,5 @@ gmon.out
*.tar.gz
PKGBUILD
src/version.h

120
CMakeLists.txt Normal file → Executable file
View File

@@ -21,37 +21,51 @@ message(STATUS "Gathering git info")
# Get git info
# hash and branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g'"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git diff-index --quiet HEAD -- || echo \"dirty\""
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DIRTY
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git describe --tags"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE)
COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
#
#
# udis
add_subdirectory("subprojects/udis86")
# wlroots
message(STATUS "Setting up wlroots")
include(ExternalProject)
if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
if(BUILDTYPE_LOWER STREQUAL "release")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "debug")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "relwithdebinfo")
set(BUILDTYPE_LOWER "debugoptimized")
elseif(BUILDTYPE_LOWER STREQUAL "minsizerel")
set(BUILDTYPE_LOWER "minsize")
elseif(BUILDTYPE_LOWER STREQUAL "none")
set(BUILDTYPE_LOWER "plain")
else()
set(BUILDTYPE_LOWER "release")
endif()
else()
set(BUILDTYPE_LOWER "release")
endif()
ExternalProject_Add(
wlroots
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots
PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/soversion = 13032/g" meson.build
CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
BUILD_COMMAND ninja -C build
BUILD_ALWAYS true
BUILD_IN_SOURCE true
BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032
INSTALL_COMMAND echo "wlroots: install not needed"
)
find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process(
@@ -85,14 +99,20 @@ set(CMAKE_ENABLE_EXPORTS TRUE)
message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1) # we do not check for wlroots, as we provide it ourselves
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(Hyprland ${SRCFILES})
set(TRACY_CPP_FILES "")
if(USE_TRACY)
set(TRACY_CPP_FILES "subprojects/tracy/public/TracyClient.cpp")
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
endif()
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
add_dependencies(Hyprland wlroots)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
@@ -101,7 +121,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan)
add_compile_options(-fsanitize=address)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()
if(USE_TRACY)
@@ -123,6 +143,12 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
add_link_options(-pg -no-pie -fno-builtin)
endif()
check_include_file("execinfo.h" EXECINFOH)
if(EXECINFOH)
message(STATUS "Configuration supports execinfo")
add_compile_definitions(HAS_EXECINFO)
endif()
include(CheckLibraryExists)
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
if(HAVE_LIBEXECINFO)
@@ -147,24 +173,21 @@ if(NO_SYSTEMD)
message(STATUS "SYSTEMD support is disabled...")
else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined) checking deps...")
pkg_check_modules(LIBSYSTEMD libsystemd)
check_include_file("systemd/sd-daemon.h" SYSTEMDH)
if(LIBSYSTEMD_FOUND AND SYSTEMDH)
add_compile_definitions(USES_SYSTEMD)
target_link_libraries(Hyprland "${LIBSYSTEMD_LIBRARIES}")
if(SYSTEMDH)
pkg_check_modules(LIBSYSTEMD libsystemd)
if (LIBSYSTEMD_FOUND)
add_compile_definitions(USES_SYSTEMD)
target_link_libraries(Hyprland "${LIBSYSTEMD_LIBRARIES}")
message(STATUS "Systemd found")
else()
message(WARNING "Systemd support requested but systemd libraries were not found")
endif()
else()
message(WARNING "Systemd support requested but libsystemd or systemd headers were not found")
message(WARNING "Systemd support requested but systemd headers were not found")
endif()
endif()
target_compile_definitions(Hyprland
PRIVATE
"GIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\""
"GIT_BRANCH=\"${GIT_BRANCH}\""
"GIT_COMMIT_MESSAGE=\"${GIT_COMMIT_MESSAGE}\""
"GIT_DIRTY=\"${GIT_DIRTY}\""
"GIT_TAG=\"${GIT_TAG}\"")
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
@@ -198,11 +221,11 @@ function(protocol protoPath protoName external)
endfunction()
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032 # wlroots is provided by us
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032 # wlroots is provided by us
OpenGL::EGL
OpenGL::GL
Threads::Threads
${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a
libudis86
)
protocol("protocols/idle.xml" "idle" true)
@@ -218,5 +241,10 @@ protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocol("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
# tools
add_subdirectory(hyprctl)
add_subdirectory(hyprpm)

View File

@@ -1,105 +1,79 @@
PREFIX = /usr/local
legacyrenderer:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
legacyrendererdebug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
release:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
debug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
clear:
rm -rf build
rm -f ./protocols/*-protocol.h ./protocols/*-protocol.c
rm -f ./hyprctl/hyprctl
rm -rf ./subprojects/wlroots/build
all:
@if [[ "$EUID" = 0 ]]; then echo -en "Avoid running $(MAKE) all as sudo.\n"; fi
$(MAKE) clear
$(MAKE) fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
$(MAKE) release
$(MAKE) -C hyprctl all
install:
$(MAKE) clear
$(MAKE) fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` && cd ../..
$(MAKE) release
$(MAKE) -C hyprctl all
@if [ ! -f ./build/Hyprland ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
@echo -en "!NOTE: Please note make install does not compile Hyprland and only installs the already built files."
mkdir -p ${PREFIX}/share/wayland-sessions
mkdir -p ${PREFIX}/bin
cp -f ./build/Hyprland ${PREFIX}/bin
cp -f ./hyprctl/hyprctl ${PREFIX}/bin
cp -f ./build/hyprctl/hyprctl ${PREFIX}/bin
cp -f ./build/hyprpm/hyprpm ${PREFIX}/bin
chmod 755 ${PREFIX}/bin/Hyprland
chmod 755 ${PREFIX}/bin/hyprctl
chmod 755 ${PREFIX}/bin/hyprpm
cd ${PREFIX}/bin && ln -sf Hyprland hyprland
if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland
cp ./assets/wall_* ${PREFIX}/share/hyprland
cp ./assets/wall* ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/xdg-desktop-portal
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal
mkdir -p ${PREFIX}/share/man/man1
install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/share/pkgconfig
mkdir -p ${PREFIX}/share/xdg-desktop-portal
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cd subprojects/wlroots/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal/
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
mkdir -p ${PREFIX}/lib/
cp ./subprojects/wlroots/build/libwlroots.so.13032 ${PREFIX}/lib/
cleaninstall:
echo -en "$(MAKE) cleaninstall has been DEPRECATED, you should avoid using it in the future.\nRunning $(MAKE) install instead...\n"
$(MAKE) install
$(MAKE) installheaders
uninstall:
rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
rm -f ${PREFIX}/bin/Hyprland
rm -f ${PREFIX}/bin/hyprland
rm -f ${PREFIX}/bin/hyprctl
rm -f ${PREFIX}/lib/libwlroots.so.12032
rm -f ${PREFIX}/bin/hyprpm
rm -f ${PREFIX}/lib/libwlroots.so.13032
rm -rf ${PREFIX}/share/hyprland
rm -f ${PREFIX}/share/man/man1/Hyprland.1
rm -f ${PREFIX}/share/man/man1/hyprctl.1
fixwlr:
sed -E -i -e 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build
rm -rf ./subprojects/wlroots/build
config:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false -Drenderers="gles2"
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
pluginenv:
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false
ninja -C subprojects/wlroots/build/
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
@exit 1
installheaders:
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
@@ -113,13 +87,8 @@ pluginenv:
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
configdebug:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=debug -Dwerror=false -Dexamples=false -Drenderers="gles2" -Db_sanitize=address
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
chmod -R 755 ${PREFIX}/include/hyprland
chmod 755 ${PREFIX}/share/pkgconfig
man:
pandoc ./docs/Hyprland.1.rst \

View File

@@ -41,6 +41,8 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
- Much more QoL stuff than other wlr-based compositors
- Custom bezier curves for the best animations
- Powerful plugin support
- Built-in plugin manager
- Tearing support for better gaming performance
- Easily expandable and readable codebase
- Fast and active development
- Not scared to provide bleeding-edge features
@@ -127,8 +129,8 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
<!----------------------------------{ Images }--------------------------------->
[Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg
[Preview A]: https://cdn.discordapp.com/attachments/1091569872535814185/1107675866101723277/screenshot-summer.png
[Preview B]: https://i.ibb.co/SX7GbYR/winter-rice.png
[Preview A]: https://i.ibb.co/C1yTb0r/falf.png
[Preview B]: https://cdn.discordapp.com/attachments/1091569872535814185/1107675866101723277/screenshot-summer.png
[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png

View File

@@ -1,2 +1,2 @@
[preferred]
default=hyprland
default=hyprland;gtk

View File

@@ -1,9 +1,7 @@
wallpaper_types = ['', 'anime_', 'anime2_']
wallpapers = ['0', '1', '2']
foreach type : wallpaper_types
foreach size : [2, 4, 8]
install_data(f'wall_@type@@size@K.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
endforeach
foreach type : wallpapers
install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
endforeach
install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime')

BIN
assets/wall0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

BIN
assets/wall1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

BIN
assets/wall2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1,8 +0,0 @@
# compile with HYPRLAND_HEADERS=<path_to_hl> make all
# make sure that the path above is to the root hl repo directory, NOT src/
# and that you have ran `make protocols` in the hl dir.
all:
$(CXX) -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b
clean:
rm ./examplePlugin.so

View File

@@ -1,74 +0,0 @@
#include "customDecoration.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
CCustomDecoration::CCustomDecoration(CWindow* pWindow) {
m_pWindow = pWindow;
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
}
CCustomDecoration::~CCustomDecoration() {
damageEntire();
}
SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() {
return m_seExtents;
}
void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
if (!g_pCompositor->windowValidMapped(m_pWindow))
return;
if (!m_pWindow->m_sSpecialRenderData.decorate)
return;
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue;
static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ?
0 :
(m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying());
// draw the border
wlr_box fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE),
(int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)};
fullBox.x -= pMonitor->vecPosition.x;
fullBox.y -= pMonitor->vecPosition.y;
m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2},
{fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2,
fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}};
fullBox.x += offset.x;
fullBox.y += offset.y;
if (fullBox.width < 1 || fullBox.height < 1)
return; // don't draw invisible shadows
g_pHyprOpenGL->scissor((wlr_box*)nullptr);
scaleBox(&fullBox, pMonitor->scale);
g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a);
}
eDecorationType CCustomDecoration::getDecorationType() {
return DECORATION_CUSTOM;
}
void CCustomDecoration::updateWindow(CWindow* pWindow) {
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
damageEntire();
}
void CCustomDecoration::damageEntire() {
wlr_box dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y),
(int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y};
g_pHyprRenderer->damageBox(&dm);
}

View File

@@ -1,29 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
class CCustomDecoration : public IHyprWindowDecoration {
public:
CCustomDecoration(CWindow*);
virtual ~CCustomDecoration();
virtual SWindowDecorationExtents getWindowDecorationExtents();
virtual void draw(CMonitor*, float a, const Vector2D& offset);
virtual eDecorationType getDecorationType();
virtual void updateWindow(CWindow*);
virtual void damageEntire();
private:
SWindowDecorationExtents m_seExtents;
CWindow* m_pWindow = nullptr;
Vector2D m_vLastWindowPos;
Vector2D m_vLastWindowSize;
};

View File

@@ -1,80 +0,0 @@
#include "customLayout.hpp"
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto SIZE = PMONITOR->vecSize;
// these are used for focus and move calculations, and are *required* to touch for moving focus to work properly.
pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)};
pWindow->m_vSize = SIZE / 2.0;
// this is the actual pos and size of the window (where it's rendered)
pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10};
pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20};
const auto PDATA = &m_vWindowData.emplace_back();
PDATA->pWindow = pWindow;
}
void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) {
std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; });
}
bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) {
return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end();
}
void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) {
; // empty
}
void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) {
; // empty
}
std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) {
return "";
}
SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) {
return {};
}
void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
; // empty
}
void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
; // empty
}
std::string CHyprCustomLayout::getLayoutName() {
return "custom";
}
void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
; // empty
}
void CHyprCustomLayout::onEnable() {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating)
continue;
onWindowCreatedTiling(w.get());
}
}
void CHyprCustomLayout::onDisable() {
m_vWindowData.clear();
}

View File

@@ -1,32 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/layout/IHyprLayout.hpp>
struct SWindowData {
CWindow* pWindow = nullptr;
};
class CHyprCustomLayout : public IHyprLayout {
public:
virtual void onWindowCreatedTiling(CWindow*);
virtual void onWindowRemovedTiling(CWindow*);
virtual bool isWindowTiled(CWindow*);
virtual void recalculateMonitor(const int&);
virtual void recalculateWindow(CWindow*);
virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
virtual void switchWindows(CWindow*, CWindow*);
virtual void alterSplitRatio(CWindow*, float, bool);
virtual std::string getLayoutName();
virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
virtual void onEnable();
virtual void onDisable();
private:
std::vector<SWindowData> m_vWindowData;
};

View File

@@ -1,5 +0,0 @@
#pragma once
#include <hyprland/src/plugins/PluginAPI.hpp>
inline HANDLE PHANDLE = nullptr;

View File

@@ -1,99 +0,0 @@
#define WLR_USE_UNSTABLE
#include "globals.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "customLayout.hpp"
#include "customDecoration.hpp"
#include <unistd.h>
#include <thread>
// Methods
inline std::unique_ptr<CHyprCustomLayout> g_pCustomLayout;
inline CFunctionHook* g_pFocusHook = nullptr;
inline CFunctionHook* g_pMotionHook = nullptr;
inline CFunctionHook* g_pMouseDownHook = nullptr;
typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
typedef void (*origMotion)(wlr_seat*, uint32_t, double, double);
typedef void (*origMouseDownNormal)(void*, wlr_pointer_button_event*);
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
static void onActiveWindowChange(void* self, std::any data) {
try {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: " + (PWINDOW ? PWINDOW->m_szTitle : "None"), CColor{0.f, 0.5f, 1.f, 1.f}, 5000);
} catch (std::bad_any_cast& e) { HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: None", CColor{0.f, 0.5f, 1.f, 1.f}, 5000); }
}
static void onNewWindow(void* self, std::any data) {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CCustomDecoration(PWINDOW));
}
void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("FocusWindow with %lx %lx", pWindow, pSurface), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origFocusWindow)g_pFocusHook->m_pOriginal)(thisptr, pWindow, pSurface);
}
void hkNotifyMotion(wlr_seat* wlr_seat, uint32_t time_msec, double sx, double sy) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("NotifyMotion with %lf %lf", sx, sy), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origMotion)g_pMotionHook->m_pOriginal)(wlr_seat, time_msec, sx, sy);
}
void hkProcessMouseDownNormal(void* thisptr, wlr_pointer_button_event* e) {
// HyprlandAPI::addNotification(PHANDLE, "Mouse down normal!", CColor{0.8f, 0.2f, 0.5f, 1.0f}, 5000);
(*(origMouseDownNormal)g_pMouseDownHook->m_pOriginal)(thisptr, e);
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
HyprlandAPI::addNotification(PHANDLE, "Hello World from an example plugin!", CColor{0.f, 1.f, 1.f, 1.f}, 5000);
HyprlandAPI::registerCallbackDynamic(PHANDLE, "activeWindow", [&](void* self, std::any data) { onActiveWindowChange(self, data); });
HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, std::any data) { onNewWindow(self, data); });
g_pCustomLayout = std::make_unique<CHyprCustomLayout>();
HyprlandAPI::addLayout(PHANDLE, "custom", g_pCustomLayout.get());
HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:border_color", SConfigValue{.intValue = configStringToInt("rgb(44ee44)")});
HyprlandAPI::addDispatcher(PHANDLE, "example", [](std::string arg) { HyprlandAPI::addNotification(PHANDLE, "Arg passed: " + arg, CColor{0.5f, 0.5f, 0.7f, 1.0f}, 5000); });
// Hook a public member
g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
// Hook a public non-member
g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
// Hook a private member
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "processMouseDownNormal");
g_pMouseDownHook = HyprlandAPI::createFunctionHook(PHANDLE, METHODS[0].address, (void*)&hkProcessMouseDownNormal);
static auto* const PBORDERCOLOR = HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color");
// fancy notifications
HyprlandAPI::addNotificationV2(
PHANDLE,
{{"text", "Example hint, color " + std::to_string(PBORDERCOLOR->intValue)}, {"time", (uint64_t)10000}, {"color", CColor{PBORDERCOLOR->intValue}}, {"icon", ICON_HINT}});
// Enable our hooks
g_pFocusHook->hook();
g_pMotionHook->hook();
g_pMouseDownHook->hook();
HyprlandAPI::reloadConfig();
return {"ExamplePlugin", "An example plugin", "Vaxry", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
HyprlandAPI::invokeHyprctlCommand("seterror", "disable");
}

View File

@@ -19,8 +19,14 @@ monitor=,preferred,auto,auto
# Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars.
env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that
# For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input {
@@ -49,6 +55,9 @@ general {
col.inactive_border = rgba(595959aa)
layout = dwindle
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
}
decoration {
@@ -60,6 +69,8 @@ decoration {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
drop_shadow = true
@@ -99,6 +110,11 @@ gestures {
workspace_swipe = false
}
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
}
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device:epic-mouse-v1 {
@@ -110,18 +126,19 @@ device:epic-mouse-v1 {
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = nomaximizerequest, class:.* # You'll probably like this.
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, kitty
bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive,
bind = $mainMod, M, exit,
bind = $mainMod, E, exec, dolphin
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, wofi --show drun
bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, togglesplit, # dwindle
@@ -155,6 +172,10 @@ bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, magic
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1

42
flake.lock generated
View File

@@ -23,13 +23,34 @@
"type": "github"
}
},
"hyprlang": {
"inputs": {
"nixpkgs": [
"xdph",
"nixpkgs"
]
},
"locked": {
"lastModified": 1704287638,
"narHash": "sha256-TuRXJGwtK440AXQNl5eiqmQqY4LZ/9+z/R7xC0ie3iA=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "6624f2bb66d4d27975766e81f77174adbe58ec97",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1694767346,
"narHash": "sha256-5uH27SiVFUwsTsqC5rs3kS7pBoNhtoy9QfTP9BmknGk=",
"lastModified": 1706191920,
"narHash": "sha256-eLihrZAPZX0R6RyM5fYAWeKVNuQPYjAkCUBr+JNvtdE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ace5093e36ab1e95cb9463863491bee90d5a4183",
"rev": "ae5c332cbb5827f6b1f02572496b141021de335f",
"type": "github"
},
"original": {
@@ -67,18 +88,18 @@
"flake": false,
"locked": {
"host": "gitlab.freedesktop.org",
"lastModified": 1695277534,
"narHash": "sha256-LEIUGXvKR5DYFQUTavC3yifcObvG4XZUUHfxXmu8nEM=",
"lastModified": 1706359063,
"narHash": "sha256-5HUTG0p+nCJv3cn73AmFHRZdfRV5AD5N43g8xAePSKM=",
"owner": "wlroots",
"repo": "wlroots",
"rev": "98a745d926d8048bc30aef11b421df207a01c279",
"rev": "00b869c1a96f300a8f25da95d624524895e0ddf2",
"type": "gitlab"
},
"original": {
"host": "gitlab.freedesktop.org",
"owner": "wlroots",
"repo": "wlroots",
"rev": "98a745d926d8048bc30aef11b421df207a01c279",
"rev": "00b869c1a96f300a8f25da95d624524895e0ddf2",
"type": "gitlab"
}
},
@@ -87,6 +108,7 @@
"hyprland-protocols": [
"hyprland-protocols"
],
"hyprlang": "hyprlang",
"nixpkgs": [
"nixpkgs"
],
@@ -95,11 +117,11 @@
]
},
"locked": {
"lastModified": 1694628480,
"narHash": "sha256-Qg9hstRw0pvjGu5hStkr2UX1D73RYcQ9Ns/KnZMIm9w=",
"lastModified": 1706145785,
"narHash": "sha256-j9MP4fv2U/vdRKAXXc2gyMTmYwVnHP6kHx1/y6jprrU=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "8f45a6435069b9e24ebd3160eda736d7a391cbf2",
"rev": "5a592647587cd20b9692a347df6939b6d371b3bb",
"type": "github"
},
"original": {

View File

@@ -12,7 +12,7 @@
host = "gitlab.freedesktop.org";
owner = "wlroots";
repo = "wlroots";
rev = "98a745d926d8048bc30aef11b421df207a01c279";
rev = "00b869c1a96f300a8f25da95d624524895e0ddf2";
flake = false;
};
@@ -62,13 +62,16 @@
inherit
(pkgsFor.${system})
# hyprland-packages
hyprland
hyprland-unwrapped
hyprland-debug
hyprland-nvidia
hyprland-legacy-renderer
# hyprland-extras
xdg-desktop-portal-hyprland
# dependencies
hyprland-protocols
wlroots-hyprland
udis86
@@ -76,17 +79,19 @@
});
devShells = eachSystem (system: {
default = pkgsFor.${system}.mkShell.override {
stdenv = pkgsFor.${system}.gcc13Stdenv;
} {
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [cmake python3];
buildInputs = [self.packages.${system}.wlroots-hyprland];
inputsFrom = [
self.packages.${system}.wlroots-hyprland
self.packages.${system}.hyprland
];
};
default =
pkgsFor.${system}.mkShell.override {
stdenv = pkgsFor.${system}.gcc13Stdenv;
} {
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [cmake python3];
buildInputs = [self.packages.${system}.wlroots-hyprland];
hardeningDisable = [ "fortify" ];
inputsFrom = [
self.packages.${system}.wlroots-hyprland
self.packages.${system}.hyprland
];
};
});
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);

8
hyprctl/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.19)
project(
hyprctl
DESCRIPTION "Control utility for Hyprland"
)
add_executable(hyprctl "main.cpp")

View File

@@ -1,4 +1,4 @@
all:
$(CXX) -std=c++2b ./main.cpp -o ./hyprctl
$(CXX) $(CXXFLAGS) -std=c++2b ./main.cpp -o ./hyprctl
clean:
rm ./hyprctl

View File

@@ -26,31 +26,35 @@
const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args]
commands:
monitors
workspaces
activeworkspace
clients
activewindow
layers
devices
activeworkspace
binds
clients
cursorpos
decorations
devices
dispatch
keyword
version
kill
splash
getoption
globalshortcuts
hyprpaper
instances
keyword
kill
layers
layouts
monitors
notify
plugin
reload
setcursor
getoption
cursorpos
switchxkblayout
seterror
setprop
plugin
notify
globalshortcuts
instances
splash
switchxkblayout
systeminfo
version
workspacerules
workspaces
flags:
-j -> output in JSON
@@ -273,7 +277,6 @@ bool isNumber(const std::string& str, bool allowfloat) {
}
int main(int argc, char** argv) {
int bflag = 0, sflag = 0, index, c;
bool parseArgs = true;
if (argc < 2) {
@@ -287,7 +290,7 @@ int main(int argc, char** argv) {
bool json = false;
std::string overrideInstance = "";
for (auto i = 0; i < ARGS.size(); ++i) {
for (std::size_t i = 0; i < ARGS.size(); ++i) {
if (ARGS[i] == "--") {
// Stop parsing arguments after --
parseArgs = false;
@@ -329,6 +332,12 @@ int main(int argc, char** argv) {
fullRequest = fullArgs + "/" + fullRequest;
// instances is HIS-independent
if (fullRequest.contains("/instances")) {
instancesRequest(json);
return 0;
}
if (overrideInstance.contains("_"))
instanceSignature = overrideInstance;
else if (!overrideInstance.empty()) {
@@ -341,7 +350,7 @@ int main(int argc, char** argv) {
const auto INSTANCES = instances();
if (INSTANCENO < 0 || INSTANCENO >= INSTANCES.size()) {
if (INSTANCENO < 0 || static_cast<std::size_t>(INSTANCENO) >= INSTANCES.size()) {
std::cout << "no such instance\n";
return 1;
}
@@ -370,6 +379,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/activeworkspace"))
request(fullRequest);
else if (fullRequest.contains("/workspacerules"))
request(fullRequest);
else if (fullRequest.contains("/activewindow"))
request(fullRequest);
else if (fullRequest.contains("/layers"))
@@ -378,6 +389,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/kill"))
request(fullRequest);
else if (fullRequest.contains("/systeminfo"))
request(fullRequest);
else if (fullRequest.contains("/splash"))
request(fullRequest);
else if (fullRequest.contains("/devices"))
@@ -394,8 +407,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/globalshortcuts"))
request(fullRequest);
else if (fullRequest.contains("/instances"))
instancesRequest(json);
else if (fullRequest.contains("/rollinglog"))
request(fullRequest);
else if (fullRequest.contains("/switchxkblayout"))
request(fullRequest, 2);
else if (fullRequest.contains("/seterror"))
@@ -414,8 +427,12 @@ int main(int argc, char** argv) {
request(fullRequest, 1);
else if (fullRequest.contains("/keyword"))
request(fullRequest, 2);
else if (fullRequest.contains("/decorations"))
request(fullRequest, 1);
else if (fullRequest.contains("/hyprpaper"))
requestHyprpaper(fullRequest);
else if (fullRequest.contains("/layouts"))
request(fullRequest);
else if (fullRequest.contains("/--help"))
printf("%s", USAGE.c_str());
else {

16
hyprpm/CMakeLists.txt Normal file
View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.19)
project(
hyprpm
DESCRIPTION "A Hyprland Plugin Manager"
)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23)
pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus)
add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::tomlplusplus)

View File

@@ -0,0 +1,241 @@
#include "DataState.hpp"
#include <toml++/toml.hpp>
#include <iostream>
#include <filesystem>
#include <fstream>
#include "PluginManager.hpp"
std::string DataState::getDataStatePath() {
const auto HOME = getenv("HOME");
if (!HOME) {
std::cerr << "DataState: no $HOME\n";
throw std::runtime_error("no $HOME");
return "";
}
const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME");
if (XDG_DATA_HOME)
return std::string{XDG_DATA_HOME} + "/hyprpm";
return std::string{HOME} + "/.local/share/hyprpm";
}
std::string DataState::getHeadersPath() {
return getDataStatePath() + "/headersRoot";
}
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());
}
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
ensureStateStoreExists();
const auto PATH = getDataStatePath() + "/" + repo.name;
std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"repository", toml::table{
{"name", repo.name},
{"hash", repo.hash},
{"url", repo.url}
}}
};
for (auto& p : repo.plugins) {
// copy .so to the good place
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");
DATA.emplace(p.name, toml::table{
{"filename", p.name + ".so"},
{"enabled", p.enabled},
{"failed", p.failed}
});
}
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
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("");
if (URL == urlOrName || NAME == urlOrName)
return true;
}
return false;
}
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("");
if (URL == urlOrName || NAME == urlOrName) {
// unload the plugins!!
for (const auto& file : std::filesystem::directory_iterator(entry.path())) {
if (!file.path().string().ends_with(".so"))
continue;
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
}
std::filesystem::remove_all(entry.path());
return;
}
}
}
void DataState::updateGlobalState(const SGlobalState& state) {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"state", toml::table{
{"hash", state.headersHashCompiled},
{"dont_warn_install", state.dontWarnInstall}
}}
};
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
SGlobalState DataState::getGlobalState() {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
if (!std::filesystem::exists(PATH + "/state.toml"))
return SGlobalState{};
auto DATA = toml::parse_file(PATH + "/state.toml");
SGlobalState state;
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
return state;
}
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");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
const auto HASH = STATE["repository"]["hash"].value_or("");
SPluginRepository repo;
repo.hash = HASH;
repo.name = NAME;
repo.url = URL;
for (const auto& [key, val] : STATE) {
if (key == "repository")
continue;
const auto ENABLED = STATE[key]["enabled"].value_or(false);
const auto FAILED = STATE[key]["failed"].value_or(false);
const auto FILENAME = STATE[key]["filename"].value_or("");
repo.plugins.push_back(SPlugin{std::string{key.str()}, FILENAME, ENABLED, FAILED});
}
repos.push_back(repo);
}
return repos;
}
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& [key, val] : STATE) {
if (key == "repository")
continue;
if (key.str() != name)
continue;
const auto FAILED = STATE[key]["failed"].value_or(false);
if (FAILED)
return false;
(*STATE[key].as_table()).insert_or_assign("enabled", enabled);
std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc);
state << STATE;
state.close();
return true;
}
}
return false;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <vector>
#include "Plugin.hpp"
struct SGlobalState {
std::string headersHashCompiled = "";
bool dontWarnInstall = false;
};
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();
};

View File

@@ -0,0 +1,104 @@
#include "Manifest.hpp"
#include <toml++/toml.hpp>
#include <iostream>
CManifest::CManifest(const eManifestType type, const std::string& path) {
auto manifest = toml::parse_file(path);
if (type == MANIFEST_HYPRLOAD) {
for (auto& [key, val] : manifest) {
if (key.str().ends_with(".build"))
continue;
CManifest::SManifestPlugin plugin;
plugin.name = key;
m_vPlugins.push_back(plugin);
}
for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.version = manifest[plugin.name]["version"].value_or("?");
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
auto authors = manifest[plugin.name]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
plugin.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest[plugin.name]["author"].value_or("");
if (!std::string{author}.empty())
plugin.authors.push_back(author);
}
auto buildSteps = manifest[plugin.name]["build"]["steps"].as_array();
if (buildSteps) {
for (auto&& s : *buildSteps) {
plugin.buildSteps.push_back(s.as_string()->value_or("?"));
}
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
m_bGood = false;
return;
}
}
} else if (type == MANIFEST_HYPRPM) {
m_sRepository.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("?"));
}
} else {
auto author = manifest["repository"]["author"].value_or("");
if (!std::string{author}.empty())
m_sRepository.authors.push_back(author);
}
auto pins = manifest["repository"]["commit_pins"].as_array();
if (pins) {
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()));
}
}
for (auto& [key, val] : manifest) {
if (key.str() == "repository")
continue;
CManifest::SManifestPlugin plugin;
plugin.name = key;
m_vPlugins.push_back(plugin);
}
for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.output = manifest[plugin.name]["output"].value_or("?");
auto authors = manifest[plugin.name]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
plugin.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest[plugin.name]["author"].value_or("");
if (!std::string{author}.empty())
plugin.authors.push_back(author);
}
auto buildSteps = manifest[plugin.name]["build"].as_array();
if (buildSteps) {
for (auto&& s : *buildSteps) {
plugin.buildSteps.push_back(s.as_string()->value_or("?"));
}
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
m_bGood = false;
return;
}
}
} else {
// ???
m_bGood = false;
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <vector>
enum eManifestType {
MANIFEST_HYPRLOAD,
MANIFEST_HYPRPM
};
class CManifest {
public:
CManifest(const eManifestType type, const std::string& path);
struct SManifestPlugin {
std::string name;
std::string description;
std::string version;
std::vector<std::string> authors;
std::vector<std::string> buildSteps;
std::string output;
bool failed = false;
};
struct {
std::string name;
std::vector<std::string> authors;
std::vector<std::pair<std::string, std::string>> commitPins;
} m_sRepository;
std::vector<SManifestPlugin> m_vPlugins;
bool m_bGood = true;
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <vector>
struct SPlugin {
std::string name;
std::string filename;
bool enabled = false;
bool failed = false;
};
struct SPluginRepository {
std::string url;
std::string name;
std::vector<SPlugin> plugins;
std::string hash;
};

View File

@@ -0,0 +1,770 @@
#include "PluginManager.hpp"
#include "../helpers/Colors.hpp"
#include "../progress/CProgressBar.hpp"
#include "Manifest.hpp"
#include "DataState.hpp"
#include <iostream>
#include <array>
#include <filesystem>
#include <thread>
#include <fstream>
#include <algorithm>
#include <format>
#include <toml++/toml.hpp>
static std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
int countBefore = 0;
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
countBefore++;
}
int countAfter = 0;
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
countAfter++;
}
str = str.substr(countBefore, str.length() - countBefore - countAfter);
return str;
}
static std::string execAndGet(std::string cmd) {
cmd += " 2>&1";
std::array<char, 128> buffer;
std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe)
return "";
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
SHyprlandVersion CPluginManager::getHyprlandVersion() {
static SHyprlandVersion ver;
static bool once = false;
if (once)
return ver;
once = true;
const auto HLVERCALL = execAndGet("hyprctl version");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "version returned: " << HLVERCALL << "\n";
if (!HLVERCALL.contains("Tag:")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " You don't seem to be running Hyprland.";
return SHyprlandVersion{};
}
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << "\n";
ver = SHyprlandVersion{hlbranch, hlcommit};
return ver;
}
bool CPluginManager::addNewPluginRepo(const std::string& url) {
const auto HLVER = getHyprlandVersion();
if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
return false;
}
auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) {
std::cout << Colors::YELLOW << "!" << Colors::RED << " Disclaimer:\n " << Colors::RESET
<< "plugins, especially not official, have no guarantee of stability, availablity or security.\n Run them at your own risk.\n "
<< "This message will not appear again.\n";
GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE);
}
std::cout << Colors::GREEN << "" << Colors::RESET << Colors::RED << " adding a new plugin repository " << Colors::RESET << "from " << url << "\n " << Colors::RED
<< "MAKE SURE" << Colors::RESET << " that you trust the authors. " << Colors::RED << "DO NOT" << Colors::RESET
<< " install random plugins without verifying the code and author.\n "
<< "Are you sure? [Y/n] ";
std::fflush(stdout);
std::string input;
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
return false;
}
CProgressBar progress;
progress.m_iMaxSteps = 5;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Cloning the plugin repository";
progress.print();
if (!std::filesystem::exists("/tmp/hyprpm")) {
std::filesystem::create_directory("/tmp/hyprpm");
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
if (std::filesystem::exists("/tmp/hyprpm/new")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old plugin repo build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/new");
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " new");
if (!std::filesystem::exists("/tmp/hyprpm/new")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. shell returned:\n" << ret << "\n";
return false;
}
progress.m_iSteps = 1;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.m_szCurrentMessage = "Reading the manifest";
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/new/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/new/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/new/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/new/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
return false;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
return false;
}
progress.m_iSteps = 2;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:");
for (auto& pl : pManifest->m_vPlugins) {
std::string message = std::string{Colors::RESET} + "" + pl.name + " by ";
for (auto& a : pl.authors) {
message += a + ", ";
}
if (pl.authors.size() > 0) {
message.pop_back();
message.pop_back();
}
message += " version " + pl.version;
progress.printMessageAbove(message);
}
if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins
progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking");
for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd /tmp/hyprpm/new/ && git reset --hard --recurse-submodules " + plugin);
}
}
progress.m_szCurrentMessage = "Verifying headers";
progress.print();
const auto HEADERSSTATUS = headersValid();
if (HEADERSSTATUS != HEADERS_OK) {
std::cerr << "\n" << headerError(HEADERSSTATUS);
return false;
}
progress.m_iSteps = 3;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " Hyprland headers OK");
progress.m_szCurrentMessage = "Building plugin(s)";
progress.print();
for (auto& p : pManifest->m_vPlugins) {
std::string out;
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd /tmp/hyprpm/new && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " failed to build.\n");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " all plugins built");
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing repository";
progress.print();
// add repo toml to DataState
SPluginRepository repo;
std::string repohash = execAndGet("cd /tmp/hyprpm/new/ && 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.url = url;
repo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false, p.failed});
}
DataState::addNewPluginRepo(repo);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed repository");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " you can now enable the plugin(s) with hyprpm enable");
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/new");
return true;
}
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
if (!DataState::pluginRepoExists(urlOrName)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not remove the repository. Repository is not installed.\n";
return false;
}
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
<< "Are you sure? [Y/n] ";
std::fflush(stdout);
std::string input;
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
return false;
}
DataState::removePluginRepo(urlOrName);
return true;
}
eHeadersErrors CPluginManager::headersValid() {
const auto HLVER = getHyprlandVersion();
if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc"))
return HEADERS_MISSING;
// find headers commit
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkg-config --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str());
if (!headers.contains("-I/"))
return HEADERS_MISSING;
headers.pop_back(); // pop newline
std::string verHeader = "";
while (!headers.empty()) {
const auto PATH = headers.substr(0, headers.find(" -I/", 3));
if (headers.find(" -I/", 3) != std::string::npos)
headers = headers.substr(headers.find("-I/", 3));
else
headers = "";
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots"))
continue;
verHeader = removeBeginEndSpacesTabs(PATH.substr(2)) + "/hyprland/src/version.h";
break;
}
if (verHeader.empty())
return HEADERS_CORRUPTED;
// read header
std::ifstream ifs(verHeader);
if (!ifs.good())
return HEADERS_CORRUPTED;
std::string verHeaderContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
ifs.close();
std::string hash = verHeaderContent.substr(verHeaderContent.find("#define GIT_COMMIT_HASH") + 23);
hash = hash.substr(0, hash.find_first_of('\n'));
hash = hash.substr(hash.find_first_of('"') + 1);
hash = hash.substr(0, hash.find_first_of('"'));
if (hash != HLVER.hash)
return HEADERS_MISMATCHED;
return HEADERS_OK;
}
bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion();
if (!std::filesystem::exists("/tmp/hyprpm")) {
std::filesystem::create_directory("/tmp/hyprpm");
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
if (!force && headersValid() == HEADERS_OK) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Headers up to date.\n";
return true;
}
CProgressBar progress;
progress.m_iMaxSteps = 5;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Cloning the hyprland repository";
progress.print();
if (std::filesystem::exists("/tmp/hyprpm/hyprland")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old hyprland source files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
}
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment.");
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland");
if (!std::filesystem::exists("/tmp/hyprpm/hyprland")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the hyprland repository. shell returned:\n" << ret << "\n";
return false;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.m_iSteps = 2;
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
ret =
execAndGet("cd /tmp/hyprpm/hyprland && git checkout " + HLVER.branch + " 2>&1 && git submodule update --init 2>&1 && git reset --hard --recurse-submodules " + HLVER.hash);
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned: " + ret);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " checked out to running ver");
progress.m_iSteps = 3;
progress.m_szCurrentMessage = "Building Hyprland";
progress.print();
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath());
ret = execAndGet(
std::format("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja",
DataState::getHeadersPath()));
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret);
// le hack. Wlroots has to generate its build/include
ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources";
progress.print();
// progress.printMessageAbove(
// std::string{Colors::YELLOW} + "!" + Colors::RESET +
// " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running.");
std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" /tmp/hyprpm/hyprland/Makefile && cd /tmp/hyprpm/hyprland && make installheaders",
DataState::getHeadersPath());
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd);
ret = execAndGet(cmd);
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed headers");
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
} else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
std::cout << "\n";
std::cerr << "\n" << headerError(HEADERSVALID);
return false;
}
return true;
}
bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (headersValid() != HEADERS_OK) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
return false;
}
const auto REPOS = DataState::getAllRepositories();
if (REPOS.size() < 1) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " No repos to update.\n";
return true;
}
const auto HLVER = getHyprlandVersion();
CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Updating repositories";
progress.print();
for (auto& repo : REPOS) {
bool update = forceUpdateAll;
progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating " + repo.name;
progress.print();
progress.printMessageAbove(std::string{Colors::RESET} + " → checking for updates for " + repo.name);
if (std::filesystem::exists("/tmp/hyprpm/update")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old update build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/update");
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + repo.url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " update");
if (!std::filesystem::exists("/tmp/hyprpm/update")) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not clone repo: shell returned:\n" + ret;
return false;
}
if (!update) {
// check if git has updates
std::string hash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
if (!hash.empty())
hash.pop_back();
update = update || hash != repo.hash;
}
if (!update) {
std::filesystem::remove_all("/tmp/hyprpm/update");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " is up-to-date.");
progress.m_iSteps++;
progress.print();
continue;
}
// we need to update
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " has updates.");
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + repo.name);
progress.m_iSteps++;
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/update/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/update/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/update/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/update/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
continue;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
continue;
}
if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins
progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking");
for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd /tmp/hyprpm/update/ && git reset --hard --recurse-submodules " + plugin);
}
}
bool failed = false;
for (auto& p : pManifest->m_vPlugins) {
std::string out;
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd /tmp/hyprpm/update && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n";
failed = true;
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
break;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
}
if (failed)
continue;
// add repo toml to DataState
SPluginRepository newrepo = repo;
newrepo.plugins.clear();
execAndGet(
"cd /tmp/hyprpm/update/ && git pull --recurse-submodules && git reset --hard --recurse-submodules"); // repo hash in the state.toml has to match head and not any pin
std::string repohash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
newrepo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
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, "/tmp/hyprpm/update/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
}
DataState::removePluginRepo(newrepo.name);
DataState::addNewPluginRepo(newrepo);
std::filesystem::remove_all("/tmp/hyprpm/update");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
}
progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating global state...";
progress.print();
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
progress.m_iSteps++;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
return true;
}
bool CPluginManager::enablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, true);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Enabled " << name << "\n";
return ret;
}
bool CPluginManager::disablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, false);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Disabled " << name << "\n";
return ret;
}
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (headersValid() != HEADERS_OK) {
std::cerr << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
return LOADSTATE_HEADERS_OUTDATED;
}
const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) {
std::cerr << "PluginManager: no $HOME or HIS\n";
return LOADSTATE_FAIL;
}
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
auto pluginLines = execAndGet("hyprctl plugins list | grep Plugin");
std::vector<std::string> loadedPlugins;
std::cout << Colors::GREEN << "" << Colors::RESET << " Ensuring plugin load state\n";
// 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();
auto enabled = [REPOS](const std::string& plugin) -> bool {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (p.name == plugin && p.enabled)
return true;
}
}
return false;
};
auto repoForName = [REPOS](const std::string& name) -> std::string {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (p.name == name)
return r.name;
}
}
return "";
};
// unload disabled plugins
for (auto& p : loadedPlugins) {
if (!enabled(p)) {
// unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::cout << Colors::GREEN << "" << Colors::RESET << " Unloaded " << p << "\n";
}
}
// load enabled plugins
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (!p.enabled)
continue;
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
continue;
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
std::cout << Colors::GREEN << "" << Colors::RESET << " Loaded " << p.name << "\n";
}
}
std::cout << Colors::GREEN << "" << Colors::RESET << " Plugin load state ensured\n";
return LOADSTATE_OK;
}
bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
if (load)
execAndGet("hyprctl plugin load " + path);
else
execAndGet("hyprctl plugin unload " + path);
return true;
}
void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories();
for (auto& r : REPOS) {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n";
for (auto& p : r.plugins) {
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name;
if (!p.failed)
std::cout << "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") << Colors::RESET << "\n";
else
std::cout << "\n └─ enabled: " << Colors::RED << "Plugin failed to build" << Colors::RESET << "\n";
}
}
}
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);
}
std::string CPluginManager::headerError(const eHeadersErrors err) {
switch (err) {
case HEADERS_CORRUPTED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers corrupted. Please run hyprpm update to fix those.\n";
case HEADERS_MISMATCHED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers version mismatch. Please run hyprpm update to fix those.\n";
case HEADERS_NOT_HYPRLAND: return std::string{Colors::RED} + "" + Colors::RESET + " It doesn't seem you are running on hyprland.\n";
case HEADERS_MISSING: return std::string{Colors::RED} + "" + Colors::RESET + " Headers missing. Please run hyprpm update to fix those.\n";
case HEADERS_DUPLICATED: {
return std::string{Colors::RED} + "" + Colors::RESET + " Headers duplicated!!! This is a very bad sign.\n" +
" This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" +
" If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n";
}
default: break;
}
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <memory>
#include <string>
enum eHeadersErrors {
HEADERS_OK = 0,
HEADERS_NOT_HYPRLAND,
HEADERS_MISSING,
HEADERS_CORRUPTED,
HEADERS_MISMATCHED,
HEADERS_DUPLICATED
};
enum eNotifyIcons {
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
ICON_ERROR,
ICON_CONFUSED,
ICON_OK,
ICON_NONE
};
enum ePluginLoadStateReturn {
LOADSTATE_OK = 0,
LOADSTATE_FAIL,
LOADSTATE_PARTIAL_FAIL,
LOADSTATE_HEADERS_OUTDATED
};
struct SHyprlandVersion {
std::string branch;
std::string hash;
};
class CPluginManager {
public:
bool addNewPluginRepo(const std::string& url);
bool removePluginRepo(const std::string& urlOrName);
eHeadersErrors headersValid();
bool updateHeaders(bool force = false);
bool updatePlugins(bool forceUpdateAll);
void listAllPlugins();
bool enablePlugin(const std::string& name);
bool disablePlugin(const std::string& name);
ePluginLoadStateReturn ensurePluginsLoadState();
bool loadUnloadPlugin(const std::string& path, bool load);
SHyprlandVersion getHyprlandVersion();
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
bool m_bVerbose = false;
private:
std::string headerError(const eHeadersErrors err);
};
inline std::unique_ptr<CPluginManager> g_pPluginManager;

View File

@@ -0,0 +1,11 @@
#pragma once
namespace Colors {
constexpr const char* RED = "\x1b[31m";
constexpr const char* GREEN = "\x1b[32m";
constexpr const char* YELLOW = "\x1b[33m";
constexpr const char* BLUE = "\x1b[34m";
constexpr const char* MAGENTA = "\x1b[35m";
constexpr const char* CYAN = "\x1b[36m";
constexpr const char* RESET = "\x1b[0m";
};

158
hyprpm/src/main.cpp Normal file
View File

@@ -0,0 +1,158 @@
#include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp"
#include "core/PluginManager.hpp"
#include "core/DataState.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
add [url] Install a new plugin repository from git
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
Flags:
--notify | -n Send a hyprland notification for important events (e.g. load fail)
--help | -h Show this menu
--verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f)
)#";
int main(int argc, char** argv, char** envp) {
std::vector<std::string> ARGS{argc};
for (int i = 0; i < argc; ++i) {
ARGS[i] = std::string{argv[i]};
}
if (ARGS.size() < 2) {
std::cout << HELP;
return 1;
}
std::vector<std::string> command;
bool notify = false, verbose = false, force = false;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
if (ARGS[i] == "--help" || ARGS[i] == "-h") {
std::cout << HELP;
return 0;
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
} else {
std::cerr << "Unrecognized option " << ARGS[i];
return 1;
}
} else {
command.push_back(ARGS[i]);
}
}
if (command.empty()) {
std::cout << HELP;
return 0;
}
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
if (command[0] == "add") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for add.\n";
return 1;
}
return g_pPluginManager->addNewPluginRepo(command[1]) ? 0 : 1;
} else if (command[0] == "remove") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for remove.\n";
return 1;
}
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);
if (headers) {
const auto HLVER = g_pPluginManager->getHyprlandVersion();
auto GLOBALSTATE = DataState::getGlobalState();
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
if (!ret1)
return 1;
auto ret2 = g_pPluginManager->ensurePluginsLoadState();
if (ret2 != LOADSTATE_OK)
return 1;
} else if (notify)
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
} else if (command[0] == "enable") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for enable.\n";
return 1;
}
if (!g_pPluginManager->enablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't enable plugin (missing?)\n";
return 1;
}
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "disable") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for disable.\n";
return 1;
}
if (!g_pPluginManager->disablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't disable plugin (missing?)\n";
return 1;
}
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState();
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;
}
} else if (notify) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}
} else if (command[0] == "list") {
g_pPluginManager->listAllPlugins();
} else {
std::cout << HELP;
return 1;
}
return 0;
}

10
hyprpm/src/meson.build Normal file
View File

@@ -0,0 +1,10 @@
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n')
executable('hyprpm', src,
dependencies: [
dependency('threads'),
dependency('tomlplusplus')
],
install : true
)

View File

@@ -0,0 +1,80 @@
#include "CProgressBar.hpp"
#include <iostream>
#include <algorithm>
#include <cmath>
#include <format>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include "../helpers/Colors.hpp"
void CProgressBar::printMessageAbove(const std::string& msg) {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
std::string spaces;
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r" << msg << "\n";
print();
}
void CProgressBar::print() {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (m_bFirstPrint)
std::cout << "\n";
m_bFirstPrint = false;
std::string spaces;
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r";
std::string message = "";
float percentDone = 0;
if (m_fPercentage >= 0)
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 += "";
}
if (i < BARWIDTH) {
i++;
message += std::string{""} + Colors::RESET;
for (; i < BARWIDTH; ++i) {
message += "";
}
} else
message += Colors::RESET;
// draw progress
if (m_fPercentage >= 0)
message += " " + std::format("{}%", static_cast<int>(percentDone * 100.0)) + " ";
else
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
// draw message
std::cout << message + " " + m_szCurrentMessage;
std::fflush(stdout);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <string>
class CProgressBar {
public:
void print();
void printMessageAbove(const std::string& msg);
std::string m_szCurrentMessage = "";
size_t m_iSteps = 0;
size_t m_iMaxSteps = 0;
float m_fPercentage = -1; // if != -1, use percentage
private:
bool m_bFirstPrint = true;
};

View File

@@ -20,25 +20,19 @@ else
error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)')
endif
GIT_BRANCH = run_command('git', 'rev-parse', '--abbrev-ref', 'HEAD', check: false).stdout().strip()
GIT_COMMIT_HASH = run_command('git', 'rev-parse', 'HEAD', check: false).stdout().strip()
GIT_COMMIT_MESSAGE = run_command('sh', '-c', 'git show | head -n 5 | tail -n 1', check: false).stdout().strip()
GIT_DIRTY = run_command('sh', '-c', 'git diff-index --quiet HEAD -- || echo "dirty"', check: false).stdout().strip()
add_project_arguments(
[
'-Wno-unused-parameter',
'-Wno-unused-value',
'-Wno-missing-field-initializers',
'-Wno-narrowing',
f'-DGIT_BRANCH="@GIT_BRANCH@"',
f'-DGIT_COMMIT_HASH="@GIT_COMMIT_HASH@"',
f'-DGIT_COMMIT_MESSAGE="@GIT_COMMIT_MESSAGE@"',
f'-DGIT_DIRTY="@GIT_DIRTY@"',
],
language: 'cpp')
if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
@@ -75,6 +69,8 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
version_h = run_command('sh', '-c', 'scripts/generateVersion.sh')
globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
@@ -84,6 +80,7 @@ endforeach
subdir('protocols')
subdir('src')
subdir('hyprctl')
subdir('hyprpm/src')
subdir('assets')
subdir('example')
subdir('docs')

View File

@@ -10,6 +10,7 @@
git,
hyprland-protocols,
jq,
libGL,
libdrm,
libinput,
libxcb,
@@ -18,6 +19,7 @@
pango,
pciutils,
systemd,
tomlplusplus,
udis86,
wayland,
wayland-protocols,
@@ -26,21 +28,23 @@
xcbutilwm,
xwayland,
debug ? false,
enableNvidiaPatches ? false,
enableXWayland ? true,
legacyRenderer ? false,
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
wrapRuntimeDeps ? true,
version ? "git",
commit,
date,
# deprecated flags
enableNvidiaPatches ? false,
nvidiaPatches ? false,
hidpiXWayland ? false,
}:
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been renamed `enableNvidiaPatches`";
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert lib.assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
stdenv.mkDerivation {
pname = "hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}${lib.optionalString debug "-debug"}";
pname = "hyprland${lib.optionalString debug "-debug"}";
inherit version;
src = lib.cleanSourceWith {
@@ -68,19 +72,21 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
buildInputs =
[
git
cairo
git
hyprland-protocols
libdrm
libGL
libinput
libxkbcommon
mesa
pango
pciutils
tomlplusplus
udis86
wayland
wayland-protocols
pciutils
(wlroots.override {inherit enableNvidiaPatches;})
wlroots
]
++ lib.optionals enableXWayland [libxcb xcbutilwm xwayland]
++ lib.optionals withSystemd [systemd];
@@ -90,8 +96,9 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
then "debug"
else "release";
mesonAutoFeatures = "disabled";
mesonFlags = builtins.concatLists [
["-Dauto_features=disabled"]
(lib.optional enableXWayland "-Dxwayland=enabled")
(lib.optional legacyRenderer "-Dlegacy_renderer=enabled")
(lib.optional withSystemd "-Dsystemd=enabled")
@@ -105,9 +112,16 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
substituteInPlace meson.build \
--replace "@GIT_COMMIT_HASH@" '${commit}' \
--replace "@GIT_DIRTY@" '${
# Generate version.h
cp src/version.h.in src/version.h
substituteInPlace src/version.h \
--replace "@HASH@" '${commit}' \
--replace "@BRANCH@" "" \
--replace "@MESSAGE@" "" \
--replace "@DATE@" "${date}" \
--replace "@TAG@" "" \
--replace "@DIRTY@" '${
if commit == ""
then "dirty"
else ""
@@ -118,7 +132,11 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
ln -s ${wlroots}/include/wlr $dev/include/hyprland/wlroots
${lib.optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [binutils pciutils]}
--suffix PATH : ${lib.makeBinPath [
stdenv.cc
binutils
pciutils
]}
''}
'';

View File

@@ -4,174 +4,11 @@ self: {
pkgs,
...
}: let
cfg = config.wayland.windowManager.hyprland;
defaultHyprlandPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable;
inherit (cfg) enableNvidiaPatches;
};
inherit (pkgs.stdenv.hostPlatform) system;
package = self.packages.${system}.default;
in {
disabledModules = ["services/window-managers/hyprland.nix"];
meta.maintainers = [lib.maintainers.fufexan];
options.wayland.windowManager.hyprland = {
enable =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to enable Hyprland, the dynamic tiling Wayland compositor
that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on
a TTY.
See <https://wiki.hyprland.org> for more information.
'';
};
package = lib.mkOption {
type = with lib.types; nullOr package;
default = defaultHyprlandPackage;
defaultText = lib.literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.wayland.windowManager.hyprland.xwayland.enable;
inherit (config.wayland.windowManager.hyprland) enableNvidiaPatches;
}
'';
description = lib.mdDoc ''
Hyprland package to use. Will override the 'xwayland' and
'enableNvidiaPatches' options.
Defaults to the one provided by the flake. Set it to
{package}`pkgs.hyprland` to use the one provided by nixpkgs or
if you have an overlay.
Set to null to not add any Hyprland package to your path. This should
be done if you want to use the NixOS module to install Hyprland.
'';
};
plugins = lib.mkOption {
type = with lib.types; listOf (either package path);
default = [];
description = lib.mdDoc ''
List of Hyprland plugins to use. Can either be packages or
absolute plugin paths.
'';
};
systemdIntegration = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.isLinux;
description = lib.mdDoc ''
Whether to enable {file}`hyprland-session.target` on
Hyprland startup. This links to {file}`graphical-session.target`.
Some important environment variables will be imported to systemd
and dbus user environment before reaching the target, including
- {env}`DISPLAY`
- {env}`HYPRLAND_INSTANCE_SIGNATURE`
- {env}`WAYLAND_DISPLAY`
- {env}`XDG_CURRENT_DESKTOP`
'';
};
disableAutoreload =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to disable automatically reloading Hyprland's configuration when
rebuilding the Home Manager profile.
'';
};
xwayland.enable = lib.mkEnableOption (lib.mdDoc "XWayland") // {default = true;};
enableNvidiaPatches = lib.mkEnableOption (lib.mdDoc "patching wlroots for better Nvidia support.");
extraConfig = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
default = "";
description = lib.mdDoc ''
Extra configuration lines to add to {file}`~/.config/hypr/hyprland.conf`.
'';
};
recommendedEnvironment =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to set the recommended environment variables.
'';
};
config = {
wayland.windowManager.hyprland.package = lib.mkDefault package;
};
config = lib.mkIf cfg.enable {
warnings =
if (cfg.systemdIntegration || cfg.plugins != []) && cfg.extraConfig == null
then [
''
You have enabled hyprland.systemdIntegration or listed plugins in hyprland.plugins.
Your Hyprland config will be linked by home manager.
Set hyprland.extraConfig or unset hyprland.systemdIntegration and hyprland.plugins to remove this warning.
''
]
else [];
home.packages =
lib.optional (cfg.package != null) cfg.package
++ lib.optional cfg.xwayland.enable pkgs.xwayland;
home.sessionVariables =
lib.mkIf cfg.recommendedEnvironment {NIXOS_OZONE_WL = "1";};
xdg.configFile."hypr/hyprland.conf" = lib.mkIf (cfg.systemdIntegration || cfg.extraConfig != null || cfg.plugins != []) {
text =
(lib.optionalString cfg.systemdIntegration ''
exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && systemctl --user start hyprland-session.target
'')
+ lib.concatStrings (builtins.map (entry: let
plugin =
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in "plugin = ${plugin}\n")
cfg.plugins)
+ (
if cfg.extraConfig != null
then cfg.extraConfig
else ""
);
onChange = let
hyprlandPackage =
if cfg.package == null
then defaultHyprlandPackage
else cfg.package;
in
lib.mkIf (!cfg.disableAutoreload) ''
( # execute in subshell so that `shopt` won't affect other scripts
shopt -s nullglob # so that nothing is done if /tmp/hypr/ does not exist or is empty
for instance in /tmp/hypr/*; do
HYPRLAND_INSTANCE_SIGNATURE=''${instance##*/} ${hyprlandPackage}/bin/hyprctl reload config-only \
|| true # ignore dead instance(s)
done
)
'';
};
systemd.user.targets.hyprland-session = lib.mkIf cfg.systemdIntegration {
Unit = {
Description = "Hyprland compositor session";
Documentation = ["man:systemd.special(7)"];
BindsTo = ["graphical-session.target"];
Wants = ["graphical-session-pre.target"];
After = ["graphical-session-pre.target"];
};
};
};
imports = [
(lib.mkRemovedOptionModule ["wayland" "windowManager" "hyprland" "xwayland" "hidpi"]
"Support for this option has been removed. Refer to https://wiki.hyprland.org/Configuring/XWayland for more info")
];
}

View File

@@ -2,98 +2,20 @@ inputs: {
config,
lib,
pkgs,
options,
...
}:
with lib; let
cfg = config.programs.hyprland;
}: let
inherit (pkgs.stdenv.hostPlatform) system;
cfg = config.programs.hyprland;
finalPortalPackage = cfg.portalPackage.override {
package = inputs.self.packages.${system}.hyprland;
portalPackage = inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
hyprland = cfg.finalPackage;
};
in {
# disables Nixpkgs Hyprland module to avoid conflicts
disabledModules = ["programs/hyprland.nix"];
options.programs.hyprland = {
enable =
mkEnableOption null
// {
description = mdDoc ''
Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`.
See <https://wiki.hyprland.org> for more information.
'';
};
package = mkPackageOptionMD inputs.self.packages.${system} "hyprland" { };
finalPackage = mkOption {
type = types.package;
readOnly = true;
default = cfg.package.override {
enableXWayland = cfg.xwayland.enable;
enableNvidiaPatches = cfg.enableNvidiaPatches;
};
defaultText =
literalExpression
"`programs.hyprland.package` with applied configuration";
description = mdDoc ''
The Hyprland package after applying configuration.
'';
};
portalPackage = mkPackageOptionMD inputs.xdph.packages.${system} "xdg-desktop-portal-hyprland" {};
xwayland.enable = mkEnableOption (mdDoc "support for XWayland") // {default = true;};
enableNvidiaPatches =
mkEnableOption null
// {
description = mdDoc "Whether to apply patches to wlroots for better Nvidia support.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [cfg.finalPackage];
# NixOS changed the name of this attribute between NixOS 23.05 and
# 23.11
fonts = if builtins.hasAttr "enableDefaultPackages" options.fonts
then {enableDefaultPackages = mkDefault true;}
else {enableDefaultFonts = mkDefault true;};
hardware.opengl.enable = mkDefault true;
programs = {
dconf.enable = mkDefault true;
xwayland.enable = mkDefault cfg.xwayland.enable;
};
security.polkit.enable = true;
services.xserver.displayManager.sessionPackages = [cfg.finalPackage];
xdg.portal = {
enable = mkDefault true;
extraPortals = [finalPortalPackage];
config = {
programs.hyprland = {
package = lib.mkDefault package;
portalPackage = lib.mkDefault portalPackage;
};
};
imports = with lib; [
(
mkRemovedOptionModule
["programs" "hyprland" "xwayland" "hidpi"]
"XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland"
)
(
mkRenamedOptionModule
["programs" "hyprland" "nvidiaPatches"]
["programs" "hyprland" "enableNvidiaPatches"]
)
];
}

View File

@@ -10,35 +10,41 @@
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in {
# Contains what a user is most likely to care about:
# Hyprland itself, XDPH and the Share Picker.
default = mkJoinedOverlays (with self.overlays; [
default = lib.composeManyExtensions (with self.overlays; [
hyprland-packages
hyprland-extras
]);
# Packages for variations of Hyprland, dependencies included.
hyprland-packages = mkJoinedOverlays [
hyprland-packages = lib.composeManyExtensions [
# Dependencies
inputs.hyprland-protocols.overlays.default
self.overlays.wlroots-hyprland
self.overlays.udis86
# Hyprland packages themselves
(final: prev: {
(final: prev: let
date = mkDate (self.lastModifiedDate or "19700101");
in {
hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${mkDate (self.lastModifiedDate or "19700101")}_${self.shortRev or "dirty"}";
wlroots = final.wlroots-hyprland;
version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
wlroots = prev.wlroots-hyprland;
commit = self.rev or "";
inherit (final) udis86 hyprland-protocols;
inherit date;
};
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
hyprland-debug = final.hyprland.override {debug = true;};
hyprland-nvidia = final.hyprland.override {enableNvidiaPatches = true;};
hyprland-legacy-renderer = final.hyprland.override {legacyRenderer = true;};
hyprland-nvidia =
builtins.trace ''
hyprland-nvidia was removed. Please use the hyprland package.
Nvidia patches are no longer needed.
''
final.hyprland;
hyprland-hidpi =
builtins.trace ''
hyprland-hidpi was removed. Please use the hyprland package.
@@ -50,7 +56,7 @@ in {
# Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility.
hyprland-extras = mkJoinedOverlays [
hyprland-extras = lib.composeManyExtensions [
inputs.xdph.overlays.xdg-desktop-portal-hyprland
];
@@ -65,30 +71,6 @@ in {
wlroots-hyprland = final.callPackage ./wlroots.nix {
version = "${mkDate (inputs.wlroots.lastModifiedDate or "19700101")}_${inputs.wlroots.shortRev or "dirty"}";
src = inputs.wlroots;
libdisplay-info = prev.libdisplay-info.overrideAttrs (old: {
version = "0.1.1+date=2023-03-02";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "147d6611a64a6ab04611b923e30efacaca6fc678";
sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4=";
};
});
libliftoff = prev.libliftoff.overrideAttrs (old: {
version = "0.5.0-dev";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "d98ae243280074b0ba44bff92215ae8d785658c0";
sha256 = "sha256-DjwlS8rXE7srs7A8+tHqXyUsFGtucYSeq6X0T/pVOc8=";
};
NIX_CFLAGS_COMPILE = toString ["-Wno-error=sign-conversion"];
});
};
};
}

View File

@@ -1,23 +1,11 @@
diff --git a/meson.build b/meson.build
index f3802553..6a924a79 100644
index 1d2c7f9f..c5ef4e67 100644
--- a/meson.build
+++ b/meson.build
@@ -21,9 +21,9 @@ else
@@ -33,20 +33,7 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
GIT_BRANCH = run_command('git', 'rev-parse', '--abbrev-ref', 'HEAD', check: false).stdout().strip()
-GIT_COMMIT_HASH = run_command('git', 'rev-parse', 'HEAD', check: false).stdout().strip()
+GIT_COMMIT_HASH = '@GIT_COMMIT_HASH@'
GIT_COMMIT_MESSAGE = run_command('sh', '-c', 'git show | head -n 5 | tail -n 1', check: false).stdout().strip()
-GIT_DIRTY = run_command('sh', '-c', 'git diff-index --quiet HEAD -- || echo "dirty"', check: false).stdout().strip()
+GIT_DIRTY = '@GIT_DIRTY@'
add_project_arguments(
[
@@ -39,21 +39,8 @@ add_project_arguments(
],
language: 'cpp')
-wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
-have_xwlr = wlroots.get_variable('features').get('xwayland')
-xcb_dep = dependency('xcb', required: get_option('xwayland'))
@@ -32,17 +20,24 @@ index f3802553..6a924a79 100644
-have_xwayland = xcb_dep.found() and have_xwlr
-
-if not have_xwayland
- add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
+if get_option('xwayland').disabled()
+ add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
@@ -69,8 +56,6 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
-version_h = run_command('sh', '-c', 'scripts/generateVersion.sh')
-
globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
diff --git a/src/meson.build b/src/meson.build
index 7b658d31..60aa4057 100644
index 0af864b9..38723b8c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -7,16 +7,16 @@ executable('Hyprland', src,
@@ -9,16 +9,16 @@ executable('Hyprland', src,
server_protos,
dependency('wayland-server'),
dependency('wayland-client'),

View File

@@ -1,41 +0,0 @@
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index 9fe934f7..9662d4ee 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -176,7 +176,7 @@ static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer,
assert(wlr_egl_is_current(renderer->egl));
push_gles2_debug(renderer);
- glFlush();
+ glFinish();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pop_gles2_debug(renderer);
diff --git a/types/output/render.c b/types/output/render.c
index 2e38919a..97f78608 100644
--- a/types/output/render.c
+++ b/types/output/render.c
@@ -240,22 +240,7 @@ bool output_pick_format(struct wlr_output *output,
}
uint32_t wlr_output_preferred_read_format(struct wlr_output *output) {
- struct wlr_renderer *renderer = output->renderer;
- assert(renderer != NULL);
-
- if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) {
- return DRM_FORMAT_INVALID;
- }
-
- if (!wlr_output_attach_render(output, NULL)) {
- return false;
- }
-
- uint32_t fmt = renderer->impl->preferred_read_format(renderer);
-
- output_clear_back_buffer(output);
-
- return fmt;
+ return DRM_FORMAT_XRGB8888;
}
struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output,

View File

@@ -8,7 +8,7 @@ CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }')
if [ "$SUB_REV" != "$CRT_REV" ]; then
echo "Updating wlroots..."
# update wlroots to submodule revision
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix
nix flake lock
echo "wlroots: $CRT_REV -> $SUB_REV"

View File

@@ -1,28 +1,13 @@
{
lib,
version,
src,
wlroots,
hwdata,
libdisplay-info,
libliftoff,
enableXWayland ? true,
enableNvidiaPatches ? false,
}:
wlroots.overrideAttrs (old: {
inherit version src enableXWayland;
pname = "${old.pname}-hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}";
pname = "${old.pname}-hyprland";
patches =
(old.patches or [])
++ (lib.optionals enableNvidiaPatches [
./patches/wlroots-nvidia.patch
]);
buildInputs = old.buildInputs ++ [hwdata libliftoff libdisplay-info];
NIX_CFLAGS_COMPILE = toString [
"-Wno-error=maybe-uninitialized"
];
patches = [ ]; # don't inherit old.patches
})

View File

@@ -1,3 +1,3 @@
{
"version": "0.30.0"
"version": "0.35.0"
}

View File

@@ -1,5 +1,5 @@
wayland_protos = dependency('wayland-protocols',
version: '>=1.25',
version: '>=1.32',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)
@@ -25,6 +25,7 @@ protocols = [
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],

16
scripts/generateVersion.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
cp -fr ./src/version.h.in ./src/version.h
HASH=$(git rev-parse HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
MESSAGE=$(git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g')
DATE=$(git show ${GIT_COMMIT_HASH} --no-patch --format=%cd --date=local)
DIRTY=$(git diff-index --quiet HEAD -- || echo dirty)
TAG=$(git describe --tags)
sed -i -e "s#@HASH@#${HASH}#" ./src/version.h
sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h
sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h
sed -i -e "s#@DATE@#${DATE}#" ./src/version.h
sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h
sed -i -e "s#@TAG@#${TAG}#" ./src/version.h

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@
#include "render/OpenGL.hpp"
#include "hyprerror/HyprError.hpp"
#include "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp"
enum eManagersInitStage {
STAGE_PRIORITY = 0,
@@ -52,7 +53,6 @@ class CCompositor {
wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr;
wlr_xdg_activation_v1* m_sWLRXDGActivation;
wlr_output_layout* m_sWLROutputLayout;
wlr_idle* m_sWLRIdle;
wlr_idle_notifier_v1* m_sWLRIdleNotifier;
wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell;
@@ -61,7 +61,6 @@ class CCompositor {
wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr;
wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation;
wlr_input_inhibit_manager* m_sWLRInhibitMgr;
wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
@@ -84,6 +83,7 @@ class CCompositor {
wlr_session_lock_manager_v1* m_sWLRSessionLockMgr;
wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr;
wlr_cursor_shape_manager_v1* m_sWLRCursorShapeMgr;
wlr_tearing_control_manager_v1* m_sWLRTearingControlMgr;
// ------------------------------------------------- //
std::string m_szWLDisplaySocket = "";
@@ -118,7 +118,9 @@ class CCompositor {
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_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false; // because wlroots
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bIsShuttingDown = false;
// ------------------------------------------------- //
@@ -133,13 +135,11 @@ class CCompositor {
void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr);
bool windowExists(CWindow*);
bool windowValidMapped(CWindow*);
CWindow* vectorToWindow(const Vector2D&);
CWindow* vectorToWindowIdeal(const Vector2D&); // used only for finding a window to focus on, basically a "findFocusableWindow"
CWindow* vectorToWindowTiled(const Vector2D&);
CWindow* vectorToWindowUnified(const Vector2D&, uint8_t properties, CWindow* pIgnoreWindow = nullptr);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**);
SIMEPopup* vectorToIMEPopup(const Vector2D& pos, std::list<SIMEPopup>& popups);
wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl);
CWindow* windowFromCursor();
CWindow* windowFloatingFromCursor();
Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*);
CMonitor* getMonitorFromOutput(wlr_output*);
CWindow* getWindowForPopup(wlr_xdg_popup*);
CWindow* getWindowFromSurface(wlr_surface*);
@@ -162,16 +162,18 @@ class CCompositor {
void changeWindowZOrder(CWindow*, bool);
void cleanupFadingOut(const int& monid);
CWindow* getWindowInDirection(CWindow*, char);
CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false);
CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false);
CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
int getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CWindow* getConstraintWindow(SMouse*);
CMonitor* getMonitorInDirection(const char&);
void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const int64_t& id);
void updateWindowAnimatedDecorationValues(CWindow*);
int getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&);
@@ -188,7 +190,6 @@ class CCompositor {
void closeWindow(CWindow*);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&);
bool cursorOnReservedArea();
CWorkspace* createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const int&, const std::string& name = "");
void setActiveMonitor(CMonitor*);
@@ -200,13 +201,20 @@ class CCompositor {
void notifyIdleActivity();
void setIdleActivityInhibit(bool inhibit);
void arrangeMonitors();
void enterUnsafeState();
void leaveUnsafeState();
void setPreferredScaleForSurface(wlr_surface* pSurface, double scale);
void setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform);
void updateSuspendedStates();
std::string explicitConfigPath;
private:
void initAllSignals();
void removeAllSignals();
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
uint64_t m_iHyprlandPID = 0;
};

View File

@@ -1,7 +1,9 @@
#pragma once
enum eIcons
{
#include "helpers/Vector2D.hpp"
#include <functional>
enum eIcons {
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
@@ -11,8 +13,7 @@ enum eIcons
ICON_NONE
};
enum eRenderStage
{
enum eRenderStage {
RENDER_PRE = 0, /* Before binding the gl context */
RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */
RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */
@@ -22,4 +23,50 @@ enum eRenderStage
RENDER_POST_MIRROR, /* After rendering a mirror */
RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */
RENDER_POST_WINDOW, /* After rendering a window (any pass) */
};
};
enum eInputType {
INPUT_TYPE_AXIS = 0,
INPUT_TYPE_BUTTON,
INPUT_TYPE_DRAG_START,
INPUT_TYPE_DRAG_END,
INPUT_TYPE_MOTION
};
struct SCallbackInfo {
bool cancelled = false; /* on cancellable events, will cancel the event. */
};
struct SWindowDecorationExtents {
Vector2D topLeft;
Vector2D bottomRight;
//
SWindowDecorationExtents operator*(const double& scale) const {
return SWindowDecorationExtents{topLeft * scale, bottomRight * scale};
}
SWindowDecorationExtents round() {
return {topLeft.round(), bottomRight.round()};
}
bool operator==(const SWindowDecorationExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight;
}
void addExtents(const SWindowDecorationExtents& other) {
topLeft = topLeft.getComponentMax(other.topLeft);
bottomRight = bottomRight.getComponentMax(other.bottomRight);
}
};
enum eHyprCtlOutputFormat {
FORMAT_NORMAL = 0,
FORMAT_JSON
};
struct SHyprCtlCommand {
std::string name = "";
bool exact = true;
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
};

View File

@@ -2,6 +2,7 @@
#include "Compositor.hpp"
#include "render/decorations/CHyprDropShadowDecoration.hpp"
#include "render/decorations/CHyprGroupBarDecoration.hpp"
#include "render/decorations/CHyprBorderDecoration.hpp"
CWindow::CWindow() {
m_vRealPosition.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE);
@@ -13,7 +14,8 @@ CWindow::CWindow() {
m_cRealShadowColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), (void*)this, AVARDAMAGE_SHADOW);
m_fDimPercent.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), (void*)this, AVARDAMAGE_ENTIRE);
m_dWindowDecorations.emplace_back(std::make_unique<CHyprDropShadowDecoration>(this)); // put the shadow so it's the first deco (has to be rendered first)
addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(this));
addWindowDeco(std::make_unique<CHyprBorderDecoration>(this));
}
CWindow::~CWindow() {
@@ -21,6 +23,12 @@ CWindow::~CWindow() {
g_pCompositor->m_pLastFocus = nullptr;
g_pCompositor->m_pLastWindow = nullptr;
}
if (!g_pHyprOpenGL)
return;
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first == this; });
}
SWindowDecorationExtents CWindow::getFullWindowExtents() {
@@ -37,30 +45,27 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
for (auto& wd : m_dWindowDecorations) {
const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this);
const auto EXTENTS = wd->getWindowDecorationExtents();
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
}
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
if (m_pWLSurface.exists() && !m_bIsX11) {
wlr_box surfaceExtents = {0, 0, 0, 0};
CBox surfaceExtents = {0, 0, 0, 0};
// TODO: this could be better, perhaps make a getFullWindowRegion?
wlr_xdg_surface_for_each_popup_surface(
m_uSurface.xdg,
[](wlr_surface* surf, int sx, int sy, void* data) {
wlr_box* pSurfaceExtents = (wlr_box*)data;
CBox* pSurfaceExtents = (CBox*)data;
if (sx < pSurfaceExtents->x)
pSurfaceExtents->x = sx;
if (sy < pSurfaceExtents->y)
@@ -88,21 +93,21 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
return maxExtents;
}
wlr_box CWindow::getFullWindowBoundingBox() {
CBox CWindow::getFullWindowBoundingBox() {
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
auto maxExtents = getFullWindowExtents();
auto maxExtents = getFullWindowExtents();
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
CBox finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
return finalBox;
}
wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
@@ -113,7 +118,7 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
POS = PMONITOR->vecPosition;
SIZE = PMONITOR->vecSize;
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
}
if (DELTALESSTHAN(POS.y - PMONITOR->vecPosition.y, PMONITOR->vecReservedTopLeft.y, 1)) {
@@ -131,71 +136,48 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
SIZE.y += PMONITOR->vecReservedBottomRight.y;
}
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
}
wlr_box CWindow::getWindowInputBox() {
const int BORDERSIZE = getRealBorderSize();
CBox CWindow::getWindowBoxUnified(uint64_t properties) {
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
SWindowDecorationExtents EXTENTS = {{0, 0}, {0, 0}};
if (properties & RESERVED_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(this));
if (properties & INPUT_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, true));
if (properties & FULL_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, false));
for (auto& wd : m_dWindowDecorations) {
CBox box = {m_vRealPosition.vec().x, m_vRealPosition.vec().y, m_vRealSize.vec().x, m_vRealSize.vec().y};
box.addExtents(EXTENTS);
if (!wd->allowsInput())
continue;
return box;
}
const auto EXTENTS = wd->getWindowDecorationExtents();
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
}
// Add extents to the real base BB and return
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
return finalBox;
CBox CWindow::getWindowMainSurfaceBox() {
return {m_vRealPosition.vec().x, m_vRealPosition.vec().y, m_vRealSize.vec().x, m_vRealSize.vec().y};
}
SWindowDecorationExtents CWindow::getFullWindowReservedArea() {
SWindowDecorationExtents extents;
for (auto& wd : m_dWindowDecorations) {
const auto RESERVED = wd->getWindowDecorationReservedArea();
if (RESERVED.bottomRight == Vector2D{} && RESERVED.topLeft == Vector2D{})
continue;
extents.topLeft = extents.topLeft + RESERVED.topLeft;
extents.bottomRight = extents.bottomRight + RESERVED.bottomRight;
}
return extents;
return g_pDecorationPositioner->getWindowDecorationReserved(this);
}
void CWindow::updateWindowDecos() {
for (auto& wd : m_dWindowDecorations)
wd->updateWindow(this);
bool recalc = false;
if (!m_bIsMapped || isHidden())
return;
for (auto& wd : m_vDecosToRemove) {
for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) {
if (it->get() == wd) {
g_pDecorationPositioner->uncacheDecoration(it->get());
it = m_dWindowDecorations.erase(it);
recalc = true;
if (it == m_dWindowDecorations.end())
@@ -204,10 +186,54 @@ void CWindow::updateWindowDecos() {
}
}
g_pDecorationPositioner->onWindowUpdate(this);
if (recalc)
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
m_vDecosToRemove.clear();
for (auto& wd : m_dWindowDecorations) {
wd->updateWindow(this);
}
}
void CWindow::addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco) {
m_dWindowDecorations.emplace_back(std::move(deco));
g_pDecorationPositioner->forceRecalcFor(this);
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
}
void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
m_vDecosToRemove.push_back(deco);
g_pDecorationPositioner->forceRecalcFor(this);
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
}
void CWindow::uncacheWindowDecos() {
for (auto& wd : m_dWindowDecorations) {
g_pDecorationPositioner->uncacheDecoration(wd.get());
}
}
bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoords, std::any data) {
if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords))
return false;
for (auto& wd : m_dWindowDecorations) {
if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
continue;
if (!g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords))
continue;
if (wd->onInputOnDeco(type, mouseCoords, data))
return true;
}
return false;
}
pid_t CWindow::getPID() {
@@ -219,6 +245,9 @@ pid_t CWindow::getPID() {
wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr);
} else {
if (!m_bIsMapped)
return -1;
PID = m_uSurface.xwayland->pid;
}
@@ -279,7 +308,7 @@ void CWindow::destroyToplevelHandle() {
}
void CWindow::updateToplevel() {
updateSurfaceOutputs();
updateSurfaceScaleTransformDetails();
if (!m_phForeignToplevel)
return;
@@ -306,8 +335,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) {
wlr_surface_send_leave(pSurface, OUTPUT);
}
void CWindow::updateSurfaceOutputs() {
if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden || !m_bMappedX11)
void CWindow::updateSurfaceScaleTransformDetails() {
if (!m_bIsMapped || m_bHidden)
return;
const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID);
@@ -316,16 +345,24 @@ void CWindow::updateSurfaceOutputs() {
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled)
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output);
if (PNEWMONITOR != PLASTMONITOR) {
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled)
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output);
}
wlr_surface_for_each_surface(
m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(surf, PMONITOR ? PMONITOR->scale : 1.f);
const auto PSURFACE = CWLSurface::surfaceFromWlr(surf);
if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale)
return;
g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform);
},
this);
}
@@ -334,6 +371,10 @@ void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID == workspaceID)
return;
static auto* const PCLOSEONLASTSPECIAL = &g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty")->intValue;
const int OLDWORKSPACE = m_iWorkspaceID;
m_iWorkspaceID = workspaceID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
@@ -352,6 +393,15 @@ void CWindow::moveToWorkspace(int workspaceID) {
// update xwayland coords
g_pXWaylandManager->setWindowSize(this, m_vRealSize.vec());
if (g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE) == 0 && *PCLOSEONLASTSPECIAL) {
const auto PWS = g_pCompositor->getWorkspaceByID(OLDWORKSPACE);
if (PWS) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWS->m_iMonitorID); PMONITOR)
PMONITOR->setSpecialWorkspace(nullptr);
}
}
}
CWindow* CWindow::X11TransientFor() {
@@ -414,11 +464,19 @@ void CWindow::onUnmap() {
if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID)
PMONITOR->setSpecialWorkspace(nullptr);
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient == this)
PMONITOR->solitaryClient = nullptr;
g_pCompositor->updateWorkspaceWindows(m_iWorkspaceID);
}
void CWindow::onMap() {
m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(this));
m_pWLSurface.m_pOwner = this;
// JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped)
m_vRealPosition.resetAllCallbacks();
@@ -448,6 +506,16 @@ void CWindow::onMap() {
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this,
"CWindow");
m_vReportedSize = m_vPendingReportedSize;
for (const auto& ctrl : g_pHyprRenderer->m_vTearingControllers) {
if (ctrl->pWlrHint->surface != m_pWLSurface.wlr())
continue;
m_bTearingHint = ctrl->pWlrHint->current;
break;
}
}
void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -472,6 +540,8 @@ void CWindow::setHidden(bool hidden) {
if (hidden && g_pCompositor->m_pLastWindow == this) {
g_pCompositor->m_pLastWindow = nullptr;
}
setSuspended(hidden);
}
bool CWindow::isHidden() {
@@ -492,15 +562,19 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} else if (r.szRule == "opaque") {
if (!m_sAdditionalConfigData.forceOpaqueOverridden)
m_sAdditionalConfigData.forceOpaque = true;
} else if (r.szRule.find("rounding") == 0) {
} else if (r.szRule == "immediate") {
m_sAdditionalConfigData.forceTearing = true;
} else if (r.szRule == "nearestneighbor") {
m_sAdditionalConfigData.nearestNeighbor = true;
} else if (r.szRule.starts_with("rounding")) {
try {
m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Rounding rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("bordersize") == 0) {
} else if (r.szRule.starts_with("bordersize")) {
try {
m_sAdditionalConfigData.borderSize = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Bordersize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("opacity") == 0) {
} else if (r.szRule.starts_with("opacity")) {
try {
CVarList vars(r.szRule, 0, ' ');
@@ -533,37 +607,78 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "noanim") {
m_sAdditionalConfigData.forceNoAnims = true;
} else if (r.szRule.find("animation") == 0) {
} else if (r.szRule.starts_with("animation")) {
auto STYLE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
m_sAdditionalConfigData.animationStyle = STYLE;
} else if (r.szRule.find("bordercolor") == 0) {
} else if (r.szRule.starts_with("bordercolor")) {
try {
std::string colorPart = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
if (colorPart.contains(' ')) {
// we have a space, 2 values
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' ')));
m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1));
} else {
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0])));
m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1])));
return;
}
for (auto& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_vColors.push_back(configStringToInt(token));
else
inactiveBorderGradient.m_vColors.push_back(configStringToInt(token));
}
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule);
else if (activeBorderGradient.m_vColors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule);
else if (inactiveBorderGradient.m_vColors.empty())
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
else {
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient;
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "dimaround") {
m_sAdditionalConfigData.dimAround = true;
} else if (r.szRule == "keepaspectratio") {
m_sAdditionalConfigData.keepAspectRatio = true;
} else if (r.szRule.find("xray") == 0) {
} else if (r.szRule.starts_with("xray")) {
CVarList vars(r.szRule, 0, ' ');
try {
m_sAdditionalConfigData.xray = configStringToInt(vars[1]);
} catch (...) {}
} else if (r.szRule.starts_with("idleinhibit")) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
if (IDLERULE == "none")
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
else if (IDLERULE == "always")
m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
else if (IDLERULE == "focus")
m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
else if (IDLERULE == "fullscreen")
m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
}
}
void CWindow::updateDynamicRules() {
m_sSpecialRenderData.activeBorderColor = -1;
m_sSpecialRenderData.inactiveBorderColor = -1;
m_sSpecialRenderData.activeBorderColor = CGradientValueData();
m_sSpecialRenderData.inactiveBorderColor = CGradientValueData();
m_sSpecialRenderData.alpha = 1.f;
m_sSpecialRenderData.alphaInactive = -1.f;
m_sAdditionalConfigData.forceNoBlur = false;
@@ -580,6 +695,9 @@ void CWindow::updateDynamicRules() {
m_sAdditionalConfigData.borderSize = -1;
m_sAdditionalConfigData.keepAspectRatio = false;
m_sAdditionalConfigData.xray = -1;
m_sAdditionalConfigData.forceTearing = false;
m_sAdditionalConfigData.nearestNeighbor = false;
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this);
for (auto& r : WINDOWRULES) {
@@ -622,9 +740,9 @@ bool CWindow::isInCurvedCorner(double x, double y) {
void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) {
const auto DATA = (SExtensionFindingData*)data;
wlr_box box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
CBox box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
if (wlr_box_contains_point(&box, DATA->vec.x, DATA->vec.y))
if (box.containsPoint(DATA->vec))
*DATA->found = surface;
}
@@ -654,14 +772,14 @@ void CWindow::createGroup() {
Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle);
return;
}
if (!m_sGroupData.pNextWindow) {
m_sGroupData.pNextWindow = this;
m_sGroupData.head = true;
m_sGroupData.locked = false;
m_sGroupData.deny = false;
m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(this));
updateWindowDecos();
addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(this));
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@@ -735,6 +853,15 @@ int CWindow::getGroupSize() {
return size;
}
bool CWindow::canBeGroupedInto(CWindow* pWindow) {
return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged
&& ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or
|| (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked
&& !(m_sGroupData.pNextWindow && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !m_sGroupData.deny // source is not denied entry
&& !(m_eGroupRules & GROUP_BARRED && m_bFirstMap); // group rule doesn't prevent adding window
}
CWindow* CWindow::getGroupWindowByIndex(int index) {
const int SIZE = getGroupSize();
index = ((index % SIZE) + SIZE) % SIZE;
@@ -791,6 +918,8 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
g_pHyprRenderer->damageWindow(pWindow);
pWindow->updateWindowDecos();
}
void CWindow::insertWindowToGroup(CWindow* pWindow) {
@@ -798,7 +927,7 @@ void CWindow::insertWindowToGroup(CWindow* pWindow) {
const auto ENDAT = m_sGroupData.pNextWindow;
if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
if (!pWindow->m_sGroupData.pNextWindow) {
BEGINAT->m_sGroupData.pNextWindow = pWindow;
@@ -874,6 +1003,9 @@ bool CWindow::opaque() {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
if (m_pWLSurface.small() && !m_pWLSurface.m_bFillIgnoreSmall)
return false;
if (PWORKSPACE->m_fAlpha.fl() != 1.f)
return false;
@@ -895,7 +1027,7 @@ float CWindow::rounding() {
float rounding = m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_sAdditionalConfigData.rounding.toUnderlying();
return rounding;
return m_sSpecialRenderData.rounding ? rounding : 0;
}
void CWindow::updateSpecialRenderData() {
@@ -925,3 +1057,23 @@ int CWindow::getRealBorderSize() {
return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
}
bool CWindow::canBeTorn() {
return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint);
}
bool CWindow::shouldSendFullscreenState() {
const auto MODE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID)->m_efFullscreenMode;
return m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL));
}
void CWindow::setSuspended(bool suspend) {
if (suspend == m_bSuspended)
return;
if (m_bIsX11)
return;
wlr_xdg_toplevel_set_suspended(m_uSurface.xdg->toplevel, suspend);
m_bSuspended = suspend;
}

View File

@@ -30,6 +30,18 @@ enum eGroupRules {
GROUP_OVERRIDE = 1 << 6, // Override other rules
};
enum eGetWindowProperties {
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
};
class IWindowTransformer;
template <typename T>
class CWindowOverridableVar {
public:
@@ -104,13 +116,13 @@ class CWindowOverridableVar {
};
struct SWindowSpecialRenderData {
CWindowOverridableVar<bool> alphaOverride = false;
CWindowOverridableVar<float> alpha = 1.f;
CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<bool> alphaOverride = false;
CWindowOverridableVar<float> alpha = 1.f;
CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<int64_t> activeBorderColor = -1; // -1 means unset
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset
CWindowOverridableVar<CGradientValueData> activeBorderColor = CGradientValueData(); // empty color vector means unset
CWindowOverridableVar<CGradientValueData> inactiveBorderColor = CGradientValueData(); // empty color vector means unset
// set by the layout
CWindowOverridableVar<int> borderSize = -1; // -1 means unset
@@ -138,6 +150,8 @@ struct SWindowAdditionalConfigData {
CWindowOverridableVar<bool> keepAspectRatio = false;
CWindowOverridableVar<int> xray = -1; // -1 means unset, takes precedence over the renderdata one
CWindowOverridableVar<int> borderSize = -1; // -1 means unset, takes precedence over the renderdata one
CWindowOverridableVar<bool> forceTearing = false;
CWindowOverridableVar<bool> nearestNeighbor = false;
};
struct SWindowRule {
@@ -147,11 +161,15 @@ struct SWindowRule {
bool v2 = false;
std::string szTitle;
std::string szClass;
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
std::string szWorkspace = ""; // empty means any
std::string szInitialTitle;
std::string szInitialClass;
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
int iOnWorkspace = -1;
std::string szWorkspace = ""; // empty means any
};
class CWindow {
@@ -179,6 +197,7 @@ class CWindow {
DYNLISTENER(setOverrideRedirect);
DYNLISTENER(associateX11);
DYNLISTENER(dissociateX11);
DYNLISTENER(ackConfigure);
// DYNLISTENER(newSubsurfaceWindow);
CWLSurface m_pWLSurface;
@@ -198,8 +217,11 @@ class CWindow {
CAnimatedVariable m_vRealSize;
// for not spamming the protocols
Vector2D m_vReportedPosition;
Vector2D m_vReportedSize;
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;
// for restoring floating statuses
Vector2D m_vLastFloatingSize;
@@ -229,7 +251,6 @@ class CWindow {
// XWayland stuff
bool m_bIsX11 = false;
bool m_bMappedX11 = false;
CWindow* m_pX11Parent = nullptr;
uint64_t m_iX11Type = 0;
bool m_bIsModal = false;
@@ -286,6 +307,9 @@ class CWindow {
SWindowSpecialRenderData m_sSpecialRenderData;
SWindowAdditionalConfigData m_sAdditionalConfigData;
// Transformers
std::vector<std::unique_ptr<IWindowTransformer>> m_vTransformers;
// for alpha
CAnimatedVariable m_fActiveInactiveAlpha;
@@ -317,6 +341,8 @@ class CWindow {
} m_sGroupData;
uint16_t m_eGroupRules = GROUP_NONE;
bool m_bTearingHint = false;
// For the list lookup
bool operator==(const CWindow& rhs) {
return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
@@ -324,18 +350,23 @@ class CWindow {
}
// methods
wlr_box getFullWindowBoundingBox();
CBox getFullWindowBoundingBox();
SWindowDecorationExtents getFullWindowExtents();
wlr_box getWindowInputBox();
wlr_box getWindowIdealBoundingBoxIgnoreReserved();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowMainSurfaceBox();
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 createToplevelHandle();
void destroyToplevelHandle();
void updateToplevel();
void updateSurfaceOutputs();
void updateSurfaceScaleTransformDetails();
void moveToWorkspace(int);
CWindow* X11TransientFor();
void onUnmap();
@@ -348,6 +379,9 @@ class CWindow {
Vector2D middle();
bool opaque();
float rounding();
bool canBeTorn();
bool shouldSendFullscreenState();
void setSuspended(bool suspend);
int getRealBorderSize();
void updateSpecialRenderData();
@@ -365,6 +399,7 @@ class CWindow {
CWindow* getGroupPrevious();
CWindow* getGroupWindowByIndex(int);
int getGroupSize();
bool canBeGroupedInto(CWindow* pWindow);
void setGroupCurrent(CWindow* pWindow);
void insertWindowToGroup(CWindow* pWindow);
void updateGroupOutputs();
@@ -372,7 +407,8 @@ class CWindow {
private:
// For hidden windows and stuff
bool m_bHidden = false;
bool m_bHidden = false;
bool m_bSuspended = false;
};
/**
@@ -414,4 +450,4 @@ struct std::formatter<CWindow*, CharT> : std::formatter<CharT> {
std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w));
return std::format_to(out, "]");
}
};
};

View File

@@ -16,6 +16,7 @@ class ICustomConfigValueData {
class CGradientValueData : public ICustomConfigValueData {
public:
CGradientValueData(){};
CGradientValueData(CColor col) {
m_vColors.push_back(col);
};

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
#include <algorithm>
#include <regex>
#include <optional>
#include <functional>
#include <xf86drmMode.h>
#include "../Window.hpp"
#include "../helpers/WLClasses.hpp"
@@ -36,18 +37,21 @@ struct SConfigValue {
};
struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
std::string workspaceName = "";
int workspaceId = -1;
bool isDefault = false;
std::optional<int64_t> gapsIn;
std::optional<int64_t> gapsOut;
std::optional<int64_t> borderSize;
std::optional<int> border;
std::optional<int> rounding;
std::optional<int> decorate;
std::optional<int> shadow;
std::string monitor = "";
std::string workspaceString = "";
std::string workspaceName = "";
int workspaceId = -1;
bool isDefault = false;
bool isPersistent = false;
std::optional<int64_t> gapsIn;
std::optional<int64_t> gapsOut;
std::optional<int64_t> borderSize;
std::optional<int> border;
std::optional<int> rounding;
std::optional<int> decorate;
std::optional<int> shadow;
std::optional<std::string> onCreatedEmptyRunCmd;
std::map<std::string, std::string> layoutopts;
};
struct SMonitorAdditionalReservedArea {
@@ -69,6 +73,12 @@ struct SAnimationPropertyConfig {
SAnimationPropertyConfig* pParentAnimation = nullptr;
};
struct SPluginKeyword {
HANDLE handle = 0;
std::string name = "";
std::function<void(const std::string&, const std::string&)> fn;
};
struct SExecRequestedRule {
std::string szRule = "";
uint64_t iPid = 0;
@@ -83,13 +93,16 @@ class CConfigManager {
int getInt(const std::string&);
float getFloat(const std::string&);
Vector2D getVec(const std::string&);
std::string getString(const std::string&);
void setFloat(const std::string&, float);
void setInt(const std::string&, int);
void setVec(const std::string&, Vector2D);
void setString(const std::string&, const std::string&);
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = "");
std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = "");
bool deviceConfigExists(const std::string&);
bool shouldBlurLS(const std::string&);
@@ -115,7 +128,8 @@ class CConfigManager {
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
void addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value);
void removePluginConfig(HANDLE handle);
void addPluginKeyword(HANDLE handle, const std::string& name, std::function<void(const std::string& cmd, const std::string& val)> fun);
void removePluginConfig(HANDLE handle);
// no-op when done.
void dispatchExecOnce();
@@ -158,6 +172,7 @@ class CConfigManager {
std::vector<std::string> m_vDeclaredPlugins;
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
std::vector<SPluginKeyword> pluginKeywords;
bool isFirstLaunch = true; // For exec-once

View File

@@ -28,8 +28,14 @@ monitor=,preferred,auto,auto
# Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars.
env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that
# For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input {
@@ -58,6 +64,9 @@ general {
col.inactive_border = rgba(595959aa)
layout = dwindle
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
}
decoration {
@@ -108,6 +117,11 @@ gestures {
workspace_swipe = off
}
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
}
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
device:epic-mouse-v1 {
@@ -119,18 +133,19 @@ device:epic-mouse-v1 {
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = nomaximizerequest, class:.* # You'll probably like this.
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, kitty
bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive,
bind = $mainMod, M, exit,
bind = $mainMod, E, exec, dolphin
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, wofi --show drun
bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, togglesplit, # dwindle
@@ -164,6 +179,10 @@ bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, magic
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1

View File

@@ -1,7 +1,6 @@
#include "CrashReporter.hpp"
#include <random>
#include <sys/utsname.h>
#include <execinfo.h>
#include <fstream>
#include <signal.h>
@@ -64,8 +63,8 @@ void CrashReporter::createAndSaveCrash(int sig) {
struct utsname unameInfo;
uname(&unameInfo);
finalCrashReport +=
std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", unameInfo.sysname, unameInfo.nodename, unameInfo.release, unameInfo.version);
finalCrashReport += std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", std::string{unameInfo.sysname}, std::string{unameInfo.nodename},
std::string{unameInfo.release}, std::string{unameInfo.version});
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
@@ -120,7 +119,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += execAndGet(("cat \"" + Debug::logFile + "\" | tail -n 50").c_str());
finalCrashReport += Debug::rollingLog.substr(Debug::rollingLog.find("\n") + 1);
const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME");
@@ -130,25 +129,19 @@ void CrashReporter::createAndSaveCrash(int sig) {
std::ofstream ofs;
std::string path;
if (!CACHE_HOME) {
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) {
if (!CACHE_HOME || std::string(CACHE_HOME).empty()) {
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland"))
std::filesystem::create_directory(std::string(HOME) + "/.hyprland");
std::filesystem::permissions(std::string(HOME) + "/.hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
path = std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc);
} else if (CACHE_HOME) {
if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland")) {
} else {
if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland"))
std::filesystem::create_directory(std::string(CACHE_HOME) + "/hyprland");
std::filesystem::permissions(std::string(CACHE_HOME) + "/hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
path = std::string(CACHE_HOME) + "/hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc);
} else {
return;
}
ofs << finalCrashReport;

View File

@@ -7,6 +7,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
@@ -28,13 +29,22 @@ static std::string getWorkspaceNameFromSpecialID(const int workspaceID) {
return workspace->m_szName;
}
std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
auto allMonitors = false;
if (vars.size() > 2)
return "too many args";
if (vars.size() == 2 && vars[1] == "all")
allMonitors = true;
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output || m->ID == -1ull)
continue;
result += std::format(
@@ -63,74 +73,77 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
"transform": {},
"focused": {},
"dpmsStatus": {},
"vrr": {}
"vrr": {},
"activelyTearing": {}
}},)#",
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""),
(m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate,
(int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspace, escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName),
m->specialWorkspaceID, escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szDescription), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
m->activeWorkspace, (m->activeWorkspace == -1 ? "" : escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName)), m->specialWorkspaceID,
escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"));
(m->dpmsStatus ? "true" : "false"), (m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"),
m->tearingState.activelyTearing ? "true" : "false");
}
trimTrailingComma(result);
result += "]";
} else {
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output || m->ID == -1ull)
continue;
result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial "
"workspace: {} ({})\n\treserved: {} "
"{} {} {}\n\tscale: {:.2f}\n\ttransform: "
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->serial ? m->output->serial : ""), m->activeWorkspace, g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName, m->specialWorkspaceID,
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szDescription,
(m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspace,
(m->activeWorkspace == -1 ? "" : g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName), m->specialWorkspaceID,
getWorkspaceNameFromSpecialID(m->specialWorkspaceID), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x,
(int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus,
(int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED));
(int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED), m->tearingState.activelyTearing);
}
}
return result;
}
static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) {
const bool isJson = format == HyprCtl::FORMAT_JSON;
static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) {
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
if (!w->m_sGroupData.pNextWindow)
return isJson ? "" : "0";
std::vector<CWindow*> groupMembers;
CWindow* curr = w;
do {
groupMembers.push_back(curr);
curr = curr->m_sGroupData.pNextWindow;
} while (curr != w);
const auto comma = isJson ? ", " : ",";
std::ostringstream result;
bool first = true;
for (auto& gw : groupMembers) {
if (first)
first = false;
else
result << comma;
CWindow* head = w->getGroupHead();
CWindow* curr = head;
while (true) {
if (isJson)
result << std::format("\"0x{:x}\"", (uintptr_t)gw);
result << std::format("\"0x{:x}\"", (uintptr_t)curr);
else
result << std::format("{:x}", (uintptr_t)gw);
result << std::format("{:x}", (uintptr_t)curr);
curr = curr->m_sGroupData.pNextWindow;
// We've wrapped around to the start, break out without trailing comma
if (curr == head)
break;
result << (isJson ? ", " : ",");
}
return result.str();
}
static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) {
if (format == HyprCtl::FORMAT_JSON) {
static std::string getWindowData(CWindow* w, eHyprCtlOutputFormat format) {
auto getFocusHistoryID = [](CWindow* wnd) -> int {
for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) {
if (g_pCompositor->m_vWindowFocusHistory[i] == wnd)
return i;
}
return -1;
};
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(
R"#({{
"address": "0x{:x}",
@@ -155,7 +168,8 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
"fullscreenMode": {},
"fakeFullscreen": {},
"grouped": [{}],
"swallowing": "0x{:x}"
"swallowing": "0x{:x}",
"focusHistoryID": {}
}},)#",
(uintptr_t)w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
@@ -166,13 +180,13 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(),
((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"),
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? (int)g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed);
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\n",
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
(uintptr_t)w, w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
(w->m_iWorkspaceID == -1 ? "" :
@@ -181,13 +195,13 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
(int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle,
w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed);
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
}
}
std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWindows) {
@@ -205,38 +219,85 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat format) {
static std::string getWorkspaceData(CWorkspace* w, eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#({{
"id": {},
"name": "{}",
"monitor": "{}",
"monitorID": {},
"windows": {},
"hasfullscreen": {},
"lastwindow": "0x{:x}",
"lastwindowtitle": "{}"
}})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"),
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
} else {
return std::format("workspace ID {} ({}) on monitor {}:\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID, w->m_szName,
PMONITOR ? PMONITOR->szName : "?", g_pCompositor->getWindowsOnWorkspace(w->m_iID), (int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW,
PLASTW ? PLASTW->m_szTitle : "");
return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID,
w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", g_pCompositor->getWindowsOnWorkspace(w->m_iID),
(int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW, PLASTW ? PLASTW->m_szTitle : "");
}
}
std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputFormat format) {
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor));
const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : "";
const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : "";
const std::string gapsIn = (bool)(r.gapsIn) ? std::format(",\n \"gapsIn\": {}", r.gapsIn.value()) : "";
const std::string gapsOut = (bool)(r.gapsOut) ? std::format(",\n \"gapsOut\": {}", r.gapsOut.value()) : "";
const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : "";
const std::string border = (bool)(r.border) ? std::format(",\n \"border\": {}", boolToString(r.border.value())) : "";
const std::string rounding = (bool)(r.rounding) ? std::format(",\n \"rounding\": {}", boolToString(r.rounding.value())) : "";
const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : "";
const std::string shadow = (bool)(r.shadow) ? std::format(",\n \"shadow\": {}", boolToString(r.shadow.value())) : "";
std::string result = std::format(R"#({{
"workspaceString": "{}"{}{}{}{}{}{}{}{}
}})#",
escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate, shadow);
return result;
} else {
const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "<unset>" : escapeJSONStrings(r.monitor));
const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : "<unset>");
const std::string gapsIn = std::format("\tgapsIn: {}\n", (bool)(r.gapsIn) ? std::to_string(r.gapsIn.value()) : "<unset>");
const std::string gapsOut = std::format("\tgapsOut: {}\n", (bool)(r.gapsOut) ? std::to_string(r.gapsOut.value()) : "<unset>");
const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : "<unset>");
const std::string border = std::format("\tborder: {}\n", (bool)(r.border) ? boolToString(r.border.value()) : "<unset>");
const std::string rounding = std::format("\trounding: {}\n", (bool)(r.rounding) ? boolToString(r.rounding.value()) : "<unset>");
const std::string decorate = std::format("\tdecorate: {}\n", (bool)(r.decorate) ? boolToString(r.decorate.value()) : "<unset>");
const std::string shadow = std::format("\tshadow: {}\n", (bool)(r.shadow) ? boolToString(r.shadow.value()) : "<unset>");
std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut,
borderSize, border, rounding, decorate, shadow);
return result;
}
}
std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
if (!g_pCompositor->m_pLastMonitor)
return "unsafe state";
std::string result = "";
auto w = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
if (!w)
return "internal error";
return getWorkspaceData(w, format);
}
std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format);
@@ -254,24 +315,44 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
result += ",";
}
trimTrailingComma(result);
result += "]";
} else {
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
}
}
return result;
}
std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_pLastWindow;
if (!g_pCompositor->windowValidMapped(PWINDOW))
return format == HyprCtl::FORMAT_JSON ? "{}" : "Invalid";
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
auto result = getWindowData(PWINDOW, format);
if (format == HyprCtl::FORMAT_JSON)
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
result.pop_back();
return result;
}
std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) {
@@ -342,10 +423,32 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format(
R"#(
"{}",)#",
m);
}
trimTrailingComma(result);
result += "\n]\n";
} else {
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format("{}\n", m);
}
}
return result;
}
std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
result += "\"mice\": [\n";
@@ -501,9 +604,9 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
@@ -554,10 +657,24 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[\n\"log\":\"";
result += escapeJSONStrings(Debug::rollingLog);
result += "\"]";
} else {
result = Debug::rollingLog;
}
return result;
}
std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else {
@@ -577,9 +694,9 @@ std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& kb : g_pKeybindManager->m_lKeybinds) {
ret += "bind";
if (kb.locked)
@@ -625,22 +742,19 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
#endif
#ifndef NDEBUG
result += "debug\n";
#endif
#ifdef HYPRLAND_DEBUG
#ifndef ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
@@ -655,17 +769,15 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
"commit": "{}",
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_TAG);
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG);
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
#endif
#ifndef NDEBUG
result += "\"debug\",";
#endif
#ifdef HYPRLAND_DEBUG
#ifndef ISDEBUG
result += "\"debug\",";
#endif
#ifdef NO_XWAYLAND
@@ -682,7 +794,40 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ""; // make the compiler happy
}
std::string dispatchRequest(std::string in) {
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "");
result += "\n\nSystem Information:\n";
struct utsname unameInfo;
uname(&unameInfo);
result += "System name: " + std::string{unameInfo.sysname} + "\n";
result += "Node name: " + std::string{unameInfo.nodename} + "\n";
result += "Release: " + std::string{unameInfo.release} + "\n";
result += "Version: " + std::string{unameInfo.version} + "\n";
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
#endif
result += "GPU information: \n" + GPUINFO + "\n\n";
result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n";
result += "plugins:\n";
for (auto& pl : g_pPluginSystem->getAllPlugins()) {
result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version);
}
return result;
}
std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
// get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1);
@@ -703,7 +848,7 @@ std::string dispatchRequest(std::string in) {
return "ok";
}
std::string dispatchKeyword(std::string in) {
std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// get rid of the keyword keyword
in = in.substr(in.find_first_of(' ') + 1);
@@ -751,7 +896,7 @@ std::string dispatchKeyword(std::string in) {
return retval;
}
std::string reloadRequest(const std::string& request) {
std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {
const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
@@ -766,20 +911,20 @@ std::string reloadRequest(const std::string& request) {
return "ok";
}
std::string killRequest() {
std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
g_pInputManager->setClickMode(CLICKMODE_KILL);
return "ok";
}
std::string splashRequest() {
std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
return g_pCompositor->m_szCurrentSplash;
}
std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();
if (format == HyprCtl::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y);
} else {
return std::format(R"#(
@@ -794,9 +939,7 @@ std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
return "error";
}
std::string getReply(std::string);
std::string dispatchBatch(std::string request) {
std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
// split by ;
request = request.substr(9);
@@ -820,7 +963,7 @@ std::string dispatchBatch(std::string request) {
nextItem();
while (curitem != "") {
reply += getReply(curitem);
reply += g_pHyprCtl->getReply(curitem);
nextItem();
}
@@ -828,14 +971,15 @@ std::string dispatchBatch(std::string request) {
return reply;
}
std::string dispatchSetCursor(std::string request) {
std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto SIZESTR = vars[vars.size() - 1];
std::string theme = "";
for (size_t i = 1; i < vars.size() - 1; ++i)
theme += vars[i] + " ";
theme.pop_back();
if (!theme.empty())
theme.pop_back();
int size = 0;
try {
@@ -859,7 +1003,7 @@ std::string dispatchSetCursor(std::string request) {
return "ok";
}
std::string switchXKBLayoutRequest(const std::string& request) {
std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto KB = vars[1];
@@ -905,7 +1049,7 @@ std::string switchXKBLayoutRequest(const std::string& request) {
return "ok";
}
std::string dispatchSeterror(std::string request) {
std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
std::string errorMessage = "";
@@ -934,7 +1078,7 @@ std::string dispatchSeterror(std::string request) {
return "ok";
}
std::string dispatchSetProp(std::string request) {
std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 4)
@@ -951,7 +1095,7 @@ std::string dispatchSetProp(std::string request) {
bool lock = false;
if (vars.size() > 4) {
if (vars[4].find("lock") == 0) {
if (vars[4].starts_with("lock")) {
lock = true;
}
}
@@ -992,15 +1136,19 @@ std::string dispatchSetProp(std::string request) {
} else if (PROP == "alphainactive") {
PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor") {
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "inactivebordercolor") {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") {
PWINDOW->m_sSpecialRenderData.borderSize.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "keepaspectratio") {
PWINDOW->m_sAdditionalConfigData.keepAspectRatio.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "immediate") {
PWINDOW->m_sAdditionalConfigData.forceTearing.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "nearestneighbor") {
PWINDOW->m_sAdditionalConfigData.nearestNeighbor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else {
return "prop not found";
}
@@ -1014,7 +1162,7 @@ std::string dispatchSetProp(std::string request) {
return "ok";
}
std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = "";
auto nextItem = [&]() {
@@ -1039,7 +1187,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
if (!PCFGOPT)
return "no such option";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL)
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL)
return std::format("option {}\n\tint: {}\n\tfloat: {:.5f}\n\tstr: \"{}\"\n\tdata: {:x}\n\tset: {}", curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue,
(uintptr_t)PCFGOPT->data.get(), PCFGOPT->set);
else {
@@ -1058,6 +1206,32 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
}
}
std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
if (!PWINDOW)
return "none";
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
}
trimTrailingComma(result);
result += "]";
} else {
result = +"Decoration\tPriority\n";
for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n";
}
}
return result;
}
void createOutputIter(wlr_backend* backend, void* data) {
const auto DATA = (std::pair<std::string, bool>*)data;
@@ -1089,7 +1263,7 @@ void createOutputIter(wlr_backend* backend, void* data) {
}
}
std::string dispatchOutput(std::string request) {
std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = "";
auto nextItem = [&]() {
@@ -1138,7 +1312,7 @@ std::string dispatchOutput(std::string request) {
return "ok";
}
std::string dispatchPlugin(std::string request) {
std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
@@ -1154,7 +1328,7 @@ std::string dispatchPlugin(std::string request) {
const auto PLUGIN = g_pPluginSystem->loadPlugin(PATH);
if (!PLUGIN)
return "error in loading plugin";
return "error in loading plugin, last error: " + g_pPluginSystem->m_szLastError;
} else if (OPERATION == "unload") {
if (vars.size() < 3)
return "not enough args";
@@ -1181,7 +1355,7 @@ std::string dispatchPlugin(std::string request) {
return "ok";
}
std::string dispatchNotify(std::string request) {
std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 5)
@@ -1222,84 +1396,100 @@ std::string dispatchNotify(std::string request) {
return "ok";
}
std::string getReply(std::string request) {
auto format = HyprCtl::FORMAT_NORMAL;
CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
registerCommand(SHyprCtlCommand{"activeworkspace", true, activeWorkspaceRequest});
registerCommand(SHyprCtlCommand{"clients", true, clientsRequest});
registerCommand(SHyprCtlCommand{"kill", true, killRequest});
registerCommand(SHyprCtlCommand{"activewindow", true, activeWindowRequest});
registerCommand(SHyprCtlCommand{"layers", true, layersRequest});
registerCommand(SHyprCtlCommand{"version", true, versionRequest});
registerCommand(SHyprCtlCommand{"devices", true, devicesRequest});
registerCommand(SHyprCtlCommand{"splash", true, splashRequest});
registerCommand(SHyprCtlCommand{"cursorpos", true, cursorPosRequest});
registerCommand(SHyprCtlCommand{"binds", true, bindsRequest});
registerCommand(SHyprCtlCommand{"globalshortcuts", true, globalShortcutsRequest});
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
registerCommand(SHyprCtlCommand{"output", false, dispatchOutput});
registerCommand(SHyprCtlCommand{"dispatch", false, dispatchRequest});
registerCommand(SHyprCtlCommand{"keyword", false, dispatchKeyword});
registerCommand(SHyprCtlCommand{"setcursor", false, dispatchSetCursor});
registerCommand(SHyprCtlCommand{"getoption", false, dispatchGetOption});
registerCommand(SHyprCtlCommand{"decorations", false, decorationRequest});
registerCommand(SHyprCtlCommand{"[[BATCH]]", false, dispatchBatch});
startHyprCtlSocket();
}
std::shared_ptr<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
return m_vCommands.emplace_back(std::make_shared<SHyprCtlCommand>(cmd));
}
void CHyprCtl::unregisterCommand(const std::shared_ptr<SHyprCtlCommand>& cmd) {
std::erase(m_vCommands, cmd);
}
std::string CHyprCtl::getReply(std::string request) {
auto format = eHyprCtlOutputFormat::FORMAT_NORMAL;
// process flags for non-batch requests
if (!request.contains("[[BATCH]]") && request.contains("/")) {
if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
long unsigned int sepIndex = 0;
for (const auto& c : request) {
if (c == '/') { // stop at separator
break;
}
// after whitespace assume the first word as a keyword,
// so its value can have slashes (e.g., a path)
if (c == ' ') {
sepIndex = request.size();
break;
}
sepIndex++;
if (c == 'j')
format = HyprCtl::FORMAT_JSON;
format = eHyprCtlOutputFormat::FORMAT_JSON;
}
if (sepIndex < request.size())
request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
}
if (request == "monitors")
return monitorsRequest(format);
else if (request == "workspaces")
return workspacesRequest(format);
else if (request == "activeworkspace")
return activeWorkspaceRequest(format);
else if (request == "clients")
return clientsRequest(format);
else if (request == "kill")
return killRequest();
else if (request == "activewindow")
return activeWindowRequest(format);
else if (request == "layers")
return layersRequest(format);
else if (request == "version")
return versionRequest(format);
else if (request.find("reload") == 0)
return reloadRequest(request);
else if (request == "devices")
return devicesRequest(format);
else if (request == "splash")
return splashRequest();
else if (request == "cursorpos")
return cursorPosRequest(format);
else if (request == "binds")
return bindsRequest(format);
else if (request == "globalshortcuts")
return globalShortcutsRequest(format);
else if (request == "animations")
return animationsRequest(format);
else if (request.find("plugin") == 0)
return dispatchPlugin(request);
else if (request.find("notify") == 0)
return dispatchNotify(request);
else if (request.find("setprop") == 0)
return dispatchSetProp(request);
else if (request.find("seterror") == 0)
return dispatchSeterror(request);
else if (request.find("switchxkblayout") == 0)
return switchXKBLayoutRequest(request);
else if (request.find("output") == 0)
return dispatchOutput(request);
else if (request.find("dispatch") == 0)
return dispatchRequest(request);
else if (request.find("keyword") == 0)
return dispatchKeyword(request);
else if (request.find("setcursor") == 0)
return dispatchSetCursor(request);
else if (request.find("getoption") == 0)
return dispatchGetOption(request, format);
else if (request.find("[[BATCH]]") == 0)
return dispatchBatch(request);
// parse exact cmds first, then non-exact.
for (auto& cmd : m_vCommands) {
if (!cmd->exact)
continue;
if (cmd->name == request)
return cmd->fn(format, request);
}
for (auto& cmd : m_vCommands) {
if (cmd->exact)
continue;
if (request.starts_with(cmd->name))
return cmd->fn(format, request);
}
return "unknown request";
}
std::string HyprCtl::makeDynamicCall(const std::string& input) {
std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input);
}
@@ -1307,14 +1497,14 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0;
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
char readBuffer[1024];
std::array<char, 1024> readBuffer;
fd_set fdset;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(ACCEPTEDCONNECTION, &fdset);
timeval timeout = {.tv_sec = 0, .tv_usec = 5000};
@@ -1325,15 +1515,22 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
return 0;
}
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer, 1024);
readBuffer[messageSize == 1024 ? 1023 : messageSize] = '\0';
std::string request(readBuffer);
std::string request;
while (true) {
readBuffer.fill(0);
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer.data(), 1023);
if (messageSize < 1)
break;
std::string recvd = readBuffer.data();
request += recvd;
if (messageSize < 1023)
break;
}
std::string reply = "";
try {
reply = getReply(request);
reply = g_pHyprCtl->getReply(request);
} catch (std::exception& e) {
Debug::log(ERR, "Error in request: {}", e.what());
reply = "Err: " + std::string(e.what());
@@ -1343,18 +1540,17 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_bWantsMonitorReload) {
if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->ensureMonitorStatus();
}
return 0;
}
void HyprCtl::startHyprCtlSocket() {
void CHyprCtl::startHyprCtlSocket() {
iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (iSocketFD < 0) {
if (m_iSocketFD < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return;
}
@@ -1365,15 +1561,15 @@ void HyprCtl::startHyprCtlSocket() {
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());
if (bind(iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
return;
}
// 10 max queued.
listen(iSocketFD, 10);
listen(m_iSocketFD, 10);
Debug::log(LOG, "Hypr socket started at {}", socketPath);
wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View File

@@ -3,24 +3,23 @@
#include "../Compositor.hpp"
#include <fstream>
#include "../helpers/MiscFunctions.hpp"
#include <functional>
namespace HyprCtl {
void startHyprCtlSocket();
std::string makeDynamicCall(const std::string& input);
class CHyprCtl {
public:
CHyprCtl();
// very simple thread-safe request method
inline bool requestMade = false;
inline bool requestReady = false;
inline std::string request = "";
std::string makeDynamicCall(const std::string& input);
std::shared_ptr<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
void unregisterCommand(const std::shared_ptr<SHyprCtlCommand>& cmd);
std::string getReply(std::string);
inline std::ifstream requestStream;
int m_iSocketFD = -1;
inline wl_event_source* hyprCtlTickSource = nullptr;
private:
void startHyprCtlSocket();
inline int iSocketFD = -1;
std::vector<std::shared_ptr<SHyprCtlCommand>> m_vCommands;
};
enum eHyprCtlOutputFormat {
FORMAT_NORMAL = 0,
FORMAT_JSON
};
};
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;

View File

@@ -233,6 +233,6 @@ void CHyprDebugOverlay::draw() {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
wlr_box pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
CBox pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
}

View File

@@ -24,7 +24,7 @@ class CHyprMonitorDebugOverlay {
std::deque<float> m_dLastAnimationTicks;
std::chrono::high_resolution_clock::time_point m_tpLastFrame;
CMonitor* m_pMonitor = nullptr;
wlr_box m_wbLastDrawnBox;
CBox m_wbLastDrawnBox;
friend class CHyprRenderer;
};

View File

@@ -3,7 +3,7 @@
#include <pango/pangocairo.h>
CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (m_dNotifications.size() == 0)
return;
@@ -44,9 +44,13 @@ void CHyprNotificationOverlay::addNotification(const std::string& text, const CC
PNOTIF->started.reset();
PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon;
for (auto& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get());
}
}
wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
static constexpr auto ANIM_DURATION_MS = 600.0;
static constexpr auto ANIM_LAG_MS = 100.0;
static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0;
@@ -166,7 +170,7 @@ wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
// cleanup notifs
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
return wlr_box{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
return CBox{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
}
void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
@@ -197,7 +201,7 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
cairo_surface_flush(m_pCairoSurface);
wlr_box damage = drawNotifications(pMonitor);
CBox damage = drawNotifications(pMonitor);
g_pHyprRenderer->damageBox(&damage);
g_pHyprRenderer->damageBox(&m_bLastDamage);
@@ -220,6 +224,6 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
wlr_box pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y};
CBox pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y};
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
}

View File

@@ -10,16 +10,15 @@
#include <cairo/cairo.h>
enum eIconBackend
{
enum eIconBackend {
ICONS_BACKEND_NONE = 0,
ICONS_BACKEND_NF,
ICONS_BACKEND_FA
};
static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = {
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "󰸞", ""},
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""},
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "󰸞", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
static const std::array<CColor, ICON_NONE + 1> ICONS_COLORS = {CColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0},
CColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
CColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
@@ -44,8 +43,8 @@ class CHyprNotificationOverlay {
void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE);
private:
wlr_box drawNotifications(CMonitor* pMonitor);
wlr_box m_bLastDamage;
CBox drawNotifications(CMonitor* pMonitor);
CBox m_bLastDamage;
std::deque<std::unique_ptr<SNotification>> m_dNotifications;

View File

@@ -10,19 +10,24 @@ void Debug::init(const std::string& IS) {
}
void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
char* outputStr = nullptr;
if (level > wlr_log_get_verbosity())
return;
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
char* outputStr = nullptr;
vasprintf(&outputStr, fmt, args);
std::string output = std::string(outputStr);
free(outputStr);
ofs << "[wlr] " << output << "\n";
rollingLog += output + "\n";
ofs.close();
if (!disableLogs || !*disableLogs) {
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << "[wlr] " << output << "\n";
ofs.close();
}
if (!disableStdout)
std::cout << output << "\n";

View File

@@ -1,13 +1,14 @@
#pragma once
#include <string>
#include <wlr/util/log.h>
#include <format>
#include <iostream>
#include <fstream>
#include <chrono>
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp"
#define LOGMESSAGESIZE 1024
#define LOGMESSAGESIZE 1024
#define ROLLING_LOG_SIZE 4096
enum LogLevel {
NONE = -1,
@@ -25,14 +26,17 @@ namespace Debug {
inline int64_t* disableTime = nullptr;
inline bool disableStdout = false;
inline bool trace = false;
inline bool shuttingDown = false;
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
void init(const std::string& IS);
template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
if (disableLogs && *disableLogs)
if (level == TRACE && !trace)
return;
if (level == TRACE && !trace)
if (shuttingDown)
return;
std::string logMsg = "";
@@ -47,10 +51,6 @@ namespace Debug {
default: break;
}
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
// print date and time to the ofs
if (disableTime && !*disableTime) {
#ifndef _LIBCPP_VERSION
@@ -69,9 +69,18 @@ namespace Debug {
// 3. this is actually what std::format in stdlib does
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
ofs << logMsg << "\n";
rollingLog += logMsg + "\n";
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
ofs.close();
if (!disableLogs || !*disableLogs) {
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << logMsg << "\n";
ofs.close();
}
// log it to the stdout too.
if (!disableStdout)
@@ -79,4 +88,4 @@ namespace Debug {
}
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
};
};

View File

@@ -13,15 +13,6 @@ inline PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp"
inline void loadGLProc(void* pProc, const char* name) {
void* proc = (void*)eglGetProcAddress(name);
if (proc == NULL) {
Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
abort();
}
*(void**)pProc = proc;
}
#define TRACY_GPU_CONTEXT TracyGpuContext
#define TRACY_GPU_ZONE(e) TracyGpuZone(e)
#define TRACY_GPU_COLLECT TracyGpuCollect

View File

@@ -42,7 +42,7 @@ namespace Events {
DYNLISTENFUNC(repositionPopupXDG);
// Surface XDG (window)
LISTENER(newXDGSurface);
LISTENER(newXDGToplevel);
LISTENER(activateXDG);
// Window events
@@ -62,6 +62,7 @@ namespace Events {
DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(associateX11);
DYNLISTENFUNC(dissociateX11);
DYNLISTENFUNC(ackConfigure);
// Window subsurfaces
// LISTENER(newSubsurfaceWindow);
@@ -120,10 +121,6 @@ namespace Events {
DYNLISTENFUNC(destroyDragIcon);
DYNLISTENFUNC(commitDragIcon);
// Inhibit
LISTENER(InhibitActivate);
LISTENER(InhibitDeactivate);
// Deco XDG
LISTENER(NewXDGDeco);
@@ -174,4 +171,7 @@ namespace Events {
// Cursor shape
LISTENER(setCursorShape);
// Tearing hints
LISTENER(newTearingHint);
};

View File

@@ -95,8 +95,8 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) {
PMONITOR->scheduledRecalc = true;
// and damage
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
}
@@ -142,7 +142,10 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output);
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive &&
if (layersurface->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(layersurface);
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint);
@@ -157,8 +160,8 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
layersurface->position = Vector2D(layersurface->geometry.x, layersurface->geometry.y);
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
@@ -171,7 +174,8 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("openLayer", layersurface);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
}
void Events::listener_unmapLayerSurface(void* owner, void* data) {
@@ -182,6 +186,11 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", layersurface);
std::erase(g_pInputManager->m_dExclusiveLSes, layersurface);
if (!g_pInputManager->m_dExclusiveLSes.empty())
g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface);
if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
@@ -246,13 +255,15 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
}
}
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
geomFixed = {layersurface->geometry.x + (int)PMONITOR->vecPosition.x, layersurface->geometry.y + (int)PMONITOR->vecPosition.y,
(int)layersurface->layerSurface->surface->current.width, (int)layersurface->layerSurface->surface->current.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused();
}
void Events::listener_commitLayerSurface(void* owner, void* data) {
@@ -269,7 +280,7 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
if (layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
wlr_box geomFixed = {layersurface->geometry.x, layersurface->geometry.y, layersurface->geometry.width, layersurface->geometry.height};
CBox geomFixed = {layersurface->geometry.x, layersurface->geometry.y, layersurface->geometry.width, layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
// fix if it changed its mon
@@ -342,5 +353,6 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
g_pHyprRenderer->damageSurface(layersurface->layerSurface->surface, layersurface->position.x, layersurface->position.y);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
}

View File

@@ -156,20 +156,6 @@ void Events::listener_commitDragIcon(void* owner, void* data) {
Debug::log(LOG, "Drag icon committed.");
}
void Events::listener_InhibitActivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Activated exclusive for {:x}.", (uintptr_t)g_pCompositor->m_sSeat.exclusiveClient);
g_pInputManager->refocus();
g_pCompositor->m_sSeat.exclusiveClient = g_pCompositor->m_sWLRInhibitMgr->active_client;
}
void Events::listener_InhibitDeactivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Deactivated exclusive.");
g_pCompositor->m_sSeat.exclusiveClient = nullptr;
g_pInputManager->refocus();
}
void Events::listener_RendererDestroy(wl_listener* listener, void* data) {
Debug::log(LOG, "!!Renderer destroyed!!");
}
@@ -189,11 +175,17 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) {
Debug::log(LOG, "PowerMgr set mode!");
const auto EVENT = (wlr_output_power_v1_set_mode_event*)data;
const auto EVENT = (wlr_output_power_v1_set_mode_event*)data;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(EVENT->output);
wlr_output_enable(EVENT->output, EVENT->mode == 1);
if (!PMONITOR) {
Debug::log(ERR, "Invalid powerMgrSetMode output");
return;
}
if (!wlr_output_commit(EVENT->output))
wlr_output_state_set_enabled(PMONITOR->state.wlr(), EVENT->mode == 1);
if (!PMONITOR->state.commit())
Debug::log(ERR, "Couldn't set power mode");
}
@@ -237,3 +229,36 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) {
g_pInputManager->processMouseRequest(E);
}
void Events::listener_newTearingHint(wl_listener* listener, void* data) {
const auto TCTL = (wlr_tearing_control_v1*)data;
Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data);
const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get();
NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data;
NEWCTRL->hyprListener_destroy.initCallback(
&NEWCTRL->pWlrHint->events.destroy,
[&](void* owner, void* data) {
Debug::log(LOG, "Destroyed {:x} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint);
std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; });
},
NEWCTRL, "TearingController");
NEWCTRL->hyprListener_set.initCallback(
&NEWCTRL->pWlrHint->events.set_hint,
[&](void* owner, void* data) {
const auto TEARINGHINT = (STearingController*)owner;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface);
if (PWINDOW) {
PWINDOW->m_bTearingHint = (bool)TEARINGHINT->pWlrHint->current;
Debug::log(LOG, "Hint {:x} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current);
}
},
NEWCTRL, "TearingController");
}

View File

@@ -22,14 +22,18 @@ void Events::listener_change(wl_listener* listener, void* data) {
if (!CONFIG)
return;
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output)
continue;
if (g_pCompositor->m_pUnsafeOutput == m.get())
continue;
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
wlr_box BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, &BOX);
CBox BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, BOX.pWlr());
BOX.applyFromWlr();
//m->vecSize.x = BOX.width;
// m->vecSize.y = BOX.height;
@@ -67,45 +71,31 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
return;
}
if (g_pCompositor->m_bUnsafeState)
Debug::log(WARN, "Recovering from an unsafe state. May you be lucky.");
// add it to real
std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr;
PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>());
if (std::string("HEADLESS-1") == OUTPUT->name)
g_pCompositor->m_pUnsafeOutput = PNEWMONITORWRAP->get();
(*PNEWMONITORWRAP)->ID = g_pCompositor->getNextAvailableMonitorID(OUTPUT->name);
(*PNEWMONITORWRAP)->output = OUTPUT;
const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? OUTPUT == g_pCompositor->m_pUnsafeOutput->output : false;
(*PNEWMONITORWRAP)->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(OUTPUT->name);
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
PNEWMONITOR->isUnsafeFallback = FALLBACK;
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
if (!FALLBACK)
PNEWMONITOR->onConnect(false);
PNEWMONITOR->output = OUTPUT;
PNEWMONITOR->m_pThisWrap = PNEWMONITORWRAP;
if (!PNEWMONITOR->m_bEnabled || FALLBACK)
return;
PNEWMONITOR->onConnect(false);
// ready to process if we have a real monitor
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
// ready to process cuz we have a monitor
if (PNEWMONITOR->m_bEnabled) {
if (g_pCompositor->m_bUnsafeState) {
// recover workspaces
for (auto& ws : g_pCompositor->m_vWorkspaces) {
g_pCompositor->moveWorkspaceToMonitor(ws.get(), PNEWMONITOR);
}
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
const auto POS = PNEWMONITOR->middle();
if (g_pCompositor->m_sSeat.mouse)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y);
}
g_pCompositor->m_bReadyToProcess = true;
g_pCompositor->m_bUnsafeState = false;
}
g_pCompositor->m_bReadyToProcess = true;
g_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR);
@@ -119,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceOutputs();
w->updateSurfaceScaleTransformDetails();
}
}
}
@@ -131,8 +121,12 @@ void Events::listener_monitorFrame(void* owner, void* data) {
if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState)
g_pConfigManager->performMonitorReload();
if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) {
return m->output != g_pCompositor->m_pUnsafeOutput->output;
})) {
// restore from unsafe state
g_pCompositor->leaveUnsafeState();
}
return; // cannot draw on session inactive (different tty)
}
@@ -140,12 +134,25 @@ void Events::listener_monitorFrame(void* owner, void* data) {
if (!PMONITOR->m_bEnabled)
return;
g_pHyprRenderer->recheckSolitaryForMonitor(PMONITOR);
PMONITOR->tearingState.busy = false;
if (PMONITOR->tearingState.activelyTearing && PMONITOR->solitaryClient /* can be invalidated by a recheck */) {
if (!PMONITOR->tearingState.frameScheduledWhileBusy)
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
PMONITOR->tearingState.nextRenderTorn = true;
PMONITOR->tearingState.frameScheduledWhileBusy = false;
}
static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue;
static auto* const PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue;
PMONITOR->lastPresentationTimer.reset();
if (*PENABLERAT) {
if (*PENABLERAT && !PMONITOR->tearingState.nextRenderTorn) {
if (!PMONITOR->RATScheduled) {
// render
g_pHyprRenderer->renderMonitor(PMONITOR);
@@ -191,7 +198,7 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
pMonitor->onDisconnect();
pMonitor->onDisconnect(true);
pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false;
@@ -202,10 +209,10 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
}
void Events::listener_monitorStateRequest(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_request_state*)data;
//const auto PMONITOR = (CMonitor*)owner;
//const auto E = (wlr_output_event_request_state*)data;
wlr_output_commit_state(PMONITOR->output, E->state);
// TODO: maybe don't ignore?
}
void Events::listener_monitorDamage(void* owner, void* data) {
@@ -226,7 +233,7 @@ void Events::listener_monitorCommit(void* owner, void* data) {
const auto E = (wlr_output_event_commit*)data;
if (E->committed & WLR_OUTPUT_STATE_BUFFER) {
if (E->state->committed & WLR_OUTPUT_STATE_BUFFER) {
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E);
}

View File

@@ -64,9 +64,9 @@ void createNewPopup(wlr_xdg_popup* popup, SXDGPopup* pHyprPopup) {
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
wlr_box box = {.x = PMONITOR->vecPosition.x - pHyprPopup->lx, .y = PMONITOR->vecPosition.y - pHyprPopup->ly, .width = PMONITOR->vecSize.x, .height = PMONITOR->vecSize.y};
CBox box = {PMONITOR->vecPosition.x - pHyprPopup->lx, PMONITOR->vecPosition.y - pHyprPopup->ly, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
wlr_xdg_popup_unconstrain_from_box(popup, &box);
wlr_xdg_popup_unconstrain_from_box(popup, box.pWlr());
pHyprPopup->monitor = PMONITOR;
@@ -159,13 +159,16 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
if (PPOPUP->monitor)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale);
if (PPOPUP->monitor) {
g_pCompositor->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale);
g_pCompositor->setPreferredTransformForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->transform);
}
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode {:x}", (uintptr_t)PPOPUP->pSurfaceTree);
}
@@ -178,11 +181,18 @@ void Events::listener_repositionPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
PPOPUP->lastPos = {lx - extents.x, ly - extents.y};
PPOPUP->repositionRequested = true;
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
CBox box = {PMONITOR->vecPosition.x - lx + PPOPUP->popup->current.geometry.x, PMONITOR->vecPosition.y - ly + PPOPUP->popup->current.geometry.y, PMONITOR->vecSize.x,
PMONITOR->vecSize.y};
wlr_xdg_popup_unconstrain_from_box(PPOPUP->popup, box.pWlr());
}
void Events::listener_unmapPopupXDG(void* owner, void* data) {
@@ -199,8 +209,9 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
@@ -225,8 +236,9 @@ void Events::listener_commitPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
if (PPOPUP->repositionRequested)
g_pHyprRenderer->damageBox(PPOPUP->lastPos.x, PPOPUP->lastPos.y, extents.width + 2, extents.height + 2);

View File

@@ -46,12 +46,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
static auto* const PSWALLOW = &g_pConfigManager->getConfigValuePtr("misc:enable_swallow")->intValue;
static auto* const PSWALLOWREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_regex")->strValue;
static auto* const PSWALLOWEXREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_exception_regex")->strValue;
static auto* const PNEWTAKESOVERFS = &g_pConfigManager->getConfigValuePtr("misc:new_window_takes_over_fullscreen")->intValue;
auto PMONITOR = g_pCompositor->m_pLastMonitor;
const auto PWORKSPACE =
PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
PWINDOW->m_iMonitorID = PMONITOR->ID;
PWINDOW->m_bMappedX11 = true;
PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
PWINDOW->m_bIsMapped = true;
PWINDOW->m_bReadyToDelete = false;
@@ -95,7 +95,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PWORKSPACE->m_bDefaultPseudo) {
PWINDOW->m_bIsPseudotiled = true;
wlr_box desiredGeometry = {0};
CBox desiredGeometry = {0};
g_pXWaylandManager->getGeometryForWindow(PWINDOW, &desiredGeometry);
PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
}
@@ -116,7 +116,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
for (auto& r : WINDOWRULES) {
if (r.szRule.find("monitor") == 0) {
if (r.szRule.starts_with("monitor")) {
try {
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' ')));
@@ -150,7 +150,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r.szRule, r.szValue, e.what()); }
} else if (r.szRule.find("workspace") == 0) {
} else if (r.szRule.starts_with("workspace")) {
// check if it isnt unset
const auto WORKSPACERQ = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
@@ -166,19 +166,19 @@ void Events::listener_mapWindow(void* owner, void* data) {
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r.szValue);
} else if (r.szRule.find("float") == 0) {
} else if (r.szRule.starts_with("float")) {
PWINDOW->m_bIsFloating = true;
} else if (r.szRule.find("tile") == 0) {
} else if (r.szRule.starts_with("tile")) {
PWINDOW->m_bIsFloating = false;
} else if (r.szRule.find("pseudo") == 0) {
} else if (r.szRule.starts_with("pseudo")) {
PWINDOW->m_bIsPseudotiled = true;
} else if (r.szRule.find("nofocus") == 0) {
} else if (r.szRule.starts_with("nofocus")) {
PWINDOW->m_bNoFocus = true;
} else if (r.szRule.find("noinitialfocus") == 0) {
} else if (r.szRule.starts_with("noinitialfocus")) {
PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.find("nofullscreenrequest") == 0) {
} else if (r.szRule.starts_with("nofullscreenrequest")) {
PWINDOW->m_bNoFullscreenRequest = true;
} else if (r.szRule.find("nomaximizerequest") == 0) {
} else if (r.szRule.starts_with("nomaximizerequest")) {
PWINDOW->m_bNoMaximizeRequest = true;
} else if (r.szRule == "fullscreen") {
requestsFullscreen = true;
@@ -198,7 +198,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
overridingNoMaximize = true;
} else if (r.szRule == "stayfocused") {
PWINDOW->m_bStayFocused = true;
} else if (r.szRule.find("group") == 0) {
} else if (r.szRule.starts_with("group")) {
if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE)
continue;
@@ -245,31 +245,10 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
vPrev = v;
}
} else if (r.szRule.find("idleinhibit") == 0) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
if (IDLERULE == "none") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_NONE;
} else if (IDLERULE == "always") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
} else if (IDLERULE == "focus") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
} else if (IDLERULE == "fullscreen") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
} else {
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
}
}
PWINDOW->applyDynamicRule(r);
}
CWindow* pFullscreenWindow = nullptr;
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
pFullscreenWindow = PFULLWINDOW;
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, PWORKSPACE->m_efFullscreenMode);
}
PWINDOW->updateSpecialRenderData();
// disallow tiled pinned
@@ -279,13 +258,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
const CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' ');
if (!WORKSPACEARGS[0].empty()) {
if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].find("silent") == 0)
if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].starts_with("silent"))
workspaceSilent = true;
std::string requestedWorkspaceName;
const int REQUESTEDWORKSPACEID = getWorkspaceIDFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0), requestedWorkspaceName);
if (REQUESTEDWORKSPACEID != INT_MAX) {
if (REQUESTEDWORKSPACEID != WORKSPACE_INVALID) {
auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID);
if (!pWorkspace)
@@ -315,7 +294,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
// size and move rules
for (auto& r : WINDOWRULES) {
if (r.szRule.find("size") == 0) {
if (r.szRule.starts_with("size")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@@ -337,7 +316,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("minsize") == 0) {
} else if (r.szRule.starts_with("minsize")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@@ -351,7 +330,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule minsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("maxsize") == 0) {
} else if (r.szRule.starts_with("maxsize")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@@ -365,16 +344,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule maxsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("move") == 0) {
} else if (r.szRule.starts_with("move")) {
try {
auto value = r.szRule.substr(r.szRule.find(' ') + 1);
const bool ONSCREEN = value.find("onscreen") == 0;
const bool ONSCREEN = value.starts_with("onscreen");
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
const bool CURSOR = value.find("cursor") == 0;
const bool CURSOR = value.starts_with("cursor");
if (CURSOR)
value = value.substr(value.find_first_of(' ') + 1);
@@ -385,7 +364,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
int posX = 0;
int posY = 0;
if (POSXSTR.find("100%-") == 0) {
if (POSXSTR.starts_with("100%-")) {
const auto POSXRAW = POSXSTR.substr(5);
posX =
PMONITOR->vecSize.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
@@ -404,7 +383,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
}
if (POSYSTR.find("100%-") == 0) {
if (POSYSTR.starts_with("100%-")) {
const auto POSYRAW = POSYSTR.substr(5);
posY =
PMONITOR->vecSize.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
@@ -439,7 +418,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("center") == 0) {
} else if (r.szRule.starts_with("center")) {
auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r.szRule, 2, ' ');
if (ARGS[1] == "1")
@@ -474,6 +453,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus);
if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.keyboard_interactive)
PWINDOW->m_bNoInitialFocus = true;
if (PWORKSPACE->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) {
if (*PNEWTAKESOVERFS == 0)
PWINDOW->m_bNoInitialFocus = true;
else if (*PNEWTAKESOVERFS == 2)
g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), false, FULLSCREEN_INVALID);
else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED)
requestsMaximize = true;
else
requestsFullscreen = true;
}
if (!PWINDOW->m_bNoFocus && !PWINDOW->m_bNoInitialFocus &&
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent &&
@@ -489,7 +478,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Window got assigned a surfaceTreeNode {:x}", (uintptr_t)PWINDOW->m_pSurfaceTree);
if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xdg->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_newPopupXDG.initCallback(&PWINDOW->m_uSurface.xdg->events.new_popup, &Events::listener_newPopupXDG, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW,
@@ -500,6 +488,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
"XDG Window Late");
PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW, "XDG Window Late");
} else {
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
"XWayland Window Late");
@@ -515,14 +504,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
"XWayland Window Late");
}
// do the animation thing
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false);
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
if ((requestsFullscreen && (!PWINDOW->m_bNoFullscreenRequest || overridingNoFullscreen)) || (requestsMaximize && (!PWINDOW->m_bNoMaximizeRequest || overridingNoMaximize)) ||
requestsFakeFullscreen) {
// fix fullscreen on requested (basically do a switcheroo)
@@ -543,10 +524,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
}
if (pFullscreenWindow && workspaceSilent) {
g_pCompositor->setWindowFullscreen(pFullscreenWindow, true, PWORKSPACE->m_efFullscreenMode);
}
// recheck idle inhibitors
g_pInputManager->recheckIdleInhibitorStatus();
@@ -640,10 +617,35 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)});
EMIT_HOOK_EVENT("openWindow", PWINDOW);
// apply data from default decos. Borders, shadows.
g_pDecorationPositioner->forceRecalcFor(PWINDOW);
PWINDOW->updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
// do animations
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false);
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
// recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
// avoid this window being visible
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFullscreen && !PWINDOW->m_bIsFloating)
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform);
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->constraintActive)
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize;
g_pCompositor->updateWorkspaceWindows(PWINDOW->m_iWorkspaceID);
}
void Events::listener_unmapWindow(void* owner, void* data) {
@@ -657,6 +659,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
return;
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
@@ -664,7 +673,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (!PWINDOW->m_bIsX11) {
Debug::log(LOG, "Unregistered late callbacks XDG");
PWINDOW->hyprListener_commitWindow.removeCallback();
PWINDOW->hyprListener_setTitleWindow.removeCallback();
PWINDOW->hyprListener_newPopupXDG.removeCallback();
PWINDOW->hyprListener_requestMaximize.removeCallback();
@@ -672,6 +680,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->hyprListener_requestMove.removeCallback();
PWINDOW->hyprListener_requestResize.removeCallback();
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
PWINDOW->hyprListener_ackConfigure.removeCallback();
} else {
Debug::log(LOG, "Unregistered late callbacks XWL");
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
@@ -685,13 +694,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (PWINDOW->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
// Allow the renderer to catch the last frame.
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
@@ -712,8 +714,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pInputManager->releaseAllMouseButtons();
}
PWINDOW->m_bMappedX11 = false;
// remove the fullscreen window status from workspace if we closed it
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
@@ -731,13 +731,16 @@ void Events::listener_unmapWindow(void* owner, void* data) {
Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE);
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow) {
if (!PWINDOWCANDIDATE)
g_pInputManager->simulateMouseMovement();
else
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
} else {
g_pInputManager->simulateMouseMovement();
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow && PWINDOWCANDIDATE)
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
g_pInputManager->sendMotionEventsToFocused();
// CWindow::onUnmap will remove this window's active status, but we can't really do it above.
if (PWINDOW == g_pCompositor->m_pLastWindow || !g_pCompositor->m_pLastWindow) {
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
EMIT_HOOK_EVENT("activeWindow", (CWindow*)nullptr);
}
} else {
Debug::log(LOG, "Unmapped was not focused, ignoring a refocus.");
@@ -774,13 +777,32 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->onUnmap();
}
void Events::listener_ackConfigure(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
const auto E = (wlr_xdg_surface_configure*)data;
// find last matching serial
const auto SERIAL = std::find_if(PWINDOW->m_vPendingSizeAcks.rbegin(), PWINDOW->m_vPendingSizeAcks.rend(), [&](const auto& e) { return e.first == E->serial; });
if (SERIAL == PWINDOW->m_vPendingSizeAcks.rend())
return;
PWINDOW->m_pPendingSizeAck = *SERIAL;
std::erase_if(PWINDOW->m_vPendingSizeAcks, [&](const auto& el) { return el.first == SERIAL->first; });
}
void Events::listener_commitWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
if (!PWINDOW->m_bMappedX11 || PWINDOW->isHidden() || (PWINDOW->m_bIsX11 && !PWINDOW->m_bMappedX11))
if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden())
return;
PWINDOW->updateSurfaceOutputs();
if (PWINDOW->m_bIsX11)
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
else if (PWINDOW->m_pPendingSizeAck.has_value()) {
PWINDOW->m_vReportedSize = PWINDOW->m_pPendingSizeAck->second;
PWINDOW->m_pPendingSizeAck.reset();
}
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
@@ -788,22 +810,29 @@ void Events::listener_commitWindow(void* owner, void* data) {
if (PWINDOW->m_bIsX11 || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
return;
const auto ISRIGID = PWINDOW->m_uSurface.xdg->toplevel->current.max_height == PWINDOW->m_uSurface.xdg->toplevel->current.min_height &&
PWINDOW->m_uSurface.xdg->toplevel->current.max_width == PWINDOW->m_uSurface.xdg->toplevel->current.min_width;
const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height};
const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
if (!ISRIGID)
if (MAXSIZE < Vector2D{1, 1})
return;
const Vector2D REQUESTEDSIZE = {PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
const auto REALSIZE = PWINDOW->m_vRealSize.goalv();
Vector2D newSize = REALSIZE;
if (REQUESTEDSIZE == PWINDOW->m_vReportedSize || REQUESTEDSIZE.x < 5 || REQUESTEDSIZE.y < 5)
return;
if (MAXSIZE.x < newSize.x)
newSize.x = MAXSIZE.x;
if (MAXSIZE.y < newSize.y)
newSize.y = MAXSIZE.y;
if (MINSIZE.x > newSize.x)
newSize.x = MINSIZE.x;
if (MINSIZE.y > newSize.y)
newSize.y = MINSIZE.y;
const Vector2D DELTA = PWINDOW->m_vReportedSize - REQUESTEDSIZE;
const Vector2D DELTA = REALSIZE - newSize;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0;
PWINDOW->m_vRealSize = REQUESTEDSIZE;
g_pXWaylandManager->setWindowSize(PWINDOW, REQUESTEDSIZE, true);
PWINDOW->m_vRealSize = newSize;
g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true);
g_pHyprRenderer->damageWindow(PWINDOW);
}
@@ -820,6 +849,7 @@ void Events::listener_destroyWindow(void* owner, void* data) {
g_pCompositor->m_pLastFocus = nullptr;
}
PWINDOW->hyprListener_commitWindow.removeCallback();
PWINDOW->hyprListener_mapWindow.removeCallback();
PWINDOW->hyprListener_unmapWindow.removeCallback();
PWINDOW->hyprListener_destroyWindow.removeCallback();
@@ -839,8 +869,8 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->m_bReadyToDelete = true;
if (!PWINDOW->m_bFadingOut) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
}
}
@@ -995,8 +1025,10 @@ void Events::listener_configureX11(void* owner, void* data) {
const auto E = (wlr_xwayland_surface_configure_event*)data;
if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bMappedX11) {
if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bIsMapped) {
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
PWINDOW->m_vPendingReportedSize = {E->width, E->height};
PWINDOW->m_vReportedSize = {E->width, E->height};
return;
}
@@ -1030,6 +1062,14 @@ void Events::listener_configureX11(void* owner, void* data) {
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
PWINDOW->m_vPendingReportedSize = {E->width, E->height};
PWINDOW->m_vReportedSize = {E->width, E->height};
PWINDOW->updateWindowDecos();
if (!g_pCompositor->isWorkspaceVisible(PWINDOW->m_iWorkspaceID))
return; // further things are only for visible windows
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f)->activeWorkspace;
g_pCompositor->changeWindowZOrder(PWINDOW, true);
@@ -1040,14 +1080,12 @@ void Events::listener_configureX11(void* owner, void* data) {
g_pInputManager->refocus();
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->updateWindowDecos();
}
void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
if (!PWINDOW->m_bMappedX11)
if (!PWINDOW->m_bIsMapped)
return;
const auto POS = PWINDOW->m_vRealPosition.goalv();
@@ -1080,8 +1118,11 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->width, PWINDOW->m_uSurface.xwayland->height));
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR)
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) {
const Vector2D DELTA = PWINDOW->m_vRealSize.goalv() - PWINDOW->m_vRealSize.goalv() / PMONITOR->scale;
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale);
PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0);
}
}
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.goalv();
@@ -1092,6 +1133,9 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->updateWindowDecos();
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goalv();
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goalv();
}
}
@@ -1107,12 +1151,14 @@ void Events::listener_associateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW, "XWayland Window");
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XWayland Window");
}
void Events::listener_dissociateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.removeCallback();
PWINDOW->hyprListener_commitWindow.removeCallback();
}
void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
@@ -1137,20 +1183,19 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
}
void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
// A window got opened
const auto XDGSURFACE = (wlr_xdg_surface*)data;
const auto XDGTOPLEVEL = (wlr_xdg_toplevel*)data;
const auto XDGSURFACE = XDGTOPLEVEL->base;
if (XDGSURFACE->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
return;
Debug::log(LOG, "New XDG Surface created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW, "XDG Window");
}
void Events::listener_NewXDGDeco(wl_listener* listener, void* data) {
@@ -1173,7 +1218,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
} else {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1)
if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1)
return;
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED);
@@ -1186,7 +1231,7 @@ void Events::listener_requestMinimize(void* owner, void* data) {
Debug::log(LOG, "Minimize request for {}", PWINDOW);
if (PWINDOW->m_bIsX11) {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1)
if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1)
return;
const auto E = (wlr_xwayland_minimize_event*)data;

View File

@@ -6,17 +6,16 @@
#include "Vector2D.hpp"
#include "Color.hpp"
#include "../macros.hpp"
#include "../debug/Log.hpp"
enum ANIMATEDVARTYPE
{
enum ANIMATEDVARTYPE {
AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT,
AVARTYPE_VECTOR,
AVARTYPE_COLOR
};
enum AVARDAMAGEPOLICY
{
enum AVARDAMAGEPOLICY {
AVARDAMAGE_NONE = -1,
AVARDAMAGE_ENTIRE = 0,
AVARDAMAGE_BORDER,
@@ -36,10 +35,10 @@ class CAnimatedVariable {
void create(ANIMATEDVARTYPE, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
void create(ANIMATEDVARTYPE, std::any val, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
CAnimatedVariable(const CAnimatedVariable&) = delete;
CAnimatedVariable(CAnimatedVariable&&) = delete;
CAnimatedVariable(const CAnimatedVariable&) = delete;
CAnimatedVariable(CAnimatedVariable&&) = delete;
CAnimatedVariable& operator=(const CAnimatedVariable&) = delete;
CAnimatedVariable& operator=(CAnimatedVariable&&) = delete;
CAnimatedVariable& operator=(CAnimatedVariable&&) = delete;
~CAnimatedVariable();

140
src/helpers/Box.cpp Normal file
View File

@@ -0,0 +1,140 @@
#include "Box.hpp"
#include <limits>
#include <algorithm>
wlr_box CBox::wlr() {
CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h};
return m_bWlrBox;
}
wlr_box* CBox::pWlr() {
CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h};
return &m_bWlrBox;
}
CBox& CBox::scale(double scale) {
x *= scale;
y *= scale;
w *= scale;
h *= scale;
return *this;
}
CBox& CBox::scale(const Vector2D& scale) {
x *= scale.x;
y *= scale.y;
w *= scale.x;
h *= scale.y;
return *this;
}
CBox& CBox::translate(const Vector2D& vec) {
x += vec.x;
y += vec.y;
return *this;
}
Vector2D CBox::middle() const {
return Vector2D{x + w / 2.0, y + h / 2.0};
}
bool CBox::containsPoint(const Vector2D& vec) const {
return VECINRECT(vec, x, y, x + w, y + h);
}
bool CBox::empty() const {
return w == 0 || h == 0;
}
CBox& CBox::applyFromWlr() {
x = m_bWlrBox.x;
y = m_bWlrBox.y;
w = m_bWlrBox.width;
h = m_bWlrBox.height;
return *this;
}
CBox& CBox::round() {
float newW = x + w - std::round(x);
float newH = y + h - std::round(y);
x = std::round(x);
y = std::round(y);
w = std::round(newW);
h = std::round(newH);
return *this;
}
CBox& CBox::transform(const wl_output_transform t, double w, double h) {
wlr_box_transform(&m_bWlrBox, pWlr(), t, w, h);
applyFromWlr();
return *this;
}
CBox& CBox::addExtents(const SWindowDecorationExtents& e) {
x -= e.topLeft.x;
y -= e.topLeft.y;
w += e.topLeft.x + e.bottomRight.x;
h += e.topLeft.y + e.bottomRight.y;
return *this;
}
CBox& CBox::scaleFromCenter(double scale) {
double oldW = w, oldH = h;
w *= scale;
h *= scale;
x -= (w - oldW) / 2.0;
y -= (h - oldH) / 2.0;
return *this;
}
CBox& CBox::expand(const double& value) {
x -= value;
y -= value;
w += value * 2.0;
h += value * 2.0;
return *this;
}
CBox& CBox::noNegativeSize() {
std::clamp(w, 0.0, std::numeric_limits<double>::infinity());
std::clamp(h, 0.0, std::numeric_limits<double>::infinity());
return *this;
}
CBox CBox::roundInternal() {
float newW = x + w - std::floor(x);
float newH = y + h - std::floor(y);
return CBox{std::floor(x), std::floor(y), std::floor(newW), std::floor(newH)};
}
CBox CBox::copy() const {
return CBox{*this};
}
Vector2D CBox::pos() const {
return {x, y};
}
Vector2D CBox::size() const {
return {w, h};
}
SWindowDecorationExtents CBox::extentsFrom(const CBox& small) {
return {{small.x - x, small.y - y}, {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
}

88
src/helpers/Box.hpp Normal file
View File

@@ -0,0 +1,88 @@
#pragma once
#include "Vector2D.hpp"
#include "../SharedDefs.hpp"
#include "../includes.hpp"
class CBox {
public:
CBox(double x_, double y_, double w_, double h_) {
x = x_;
y = y_;
w = w_;
h = h_;
}
CBox() {
w = 0;
h = 0;
}
CBox(const wlr_box& box) {
x = box.x;
y = box.y;
w = box.width;
h = box.height;
}
CBox(const double d) {
x = d;
y = d;
w = d;
h = d;
}
CBox(const Vector2D& pos, const Vector2D& size) {
x = pos.x;
y = pos.y;
w = size.x;
h = size.y;
}
wlr_box wlr();
wlr_box* pWlr();
CBox& applyFromWlr();
CBox& scale(double scale);
CBox& scaleFromCenter(double scale);
CBox& scale(const Vector2D& scale);
CBox& translate(const Vector2D& vec);
CBox& round();
CBox& transform(const wl_output_transform t, double w, double h);
CBox& addExtents(const SWindowDecorationExtents& e);
CBox& expand(const double& value);
CBox& noNegativeSize();
CBox copy() const;
SWindowDecorationExtents extentsFrom(const CBox&); // this is the big box
Vector2D middle() const;
Vector2D pos() const;
Vector2D size() const;
bool containsPoint(const Vector2D& vec) const;
bool empty() const;
double x = 0, y = 0;
union {
double w;
double width;
};
union {
double h;
double height;
};
double rot = 0; /* rad, ccw */
//
bool operator==(const CBox& rhs) const {
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
}
private:
CBox roundInternal();
wlr_box m_bWlrBox;
};

View File

@@ -5,7 +5,7 @@
class CColor {
public:
CColor();
CColor(float, float, float, float);
CColor(float r, float g, float b, float a);
CColor(uint64_t);
float r = 0, g = 0, b = 0, a = 1.f;
@@ -27,4 +27,8 @@ class CColor {
bool operator==(const CColor& c2) const {
return r == c2.r && g == c2.g && b == c2.b && a == c2.a;
}
CColor stripA() const {
return {r, g, b, 1};
}
};

View File

@@ -2,11 +2,14 @@
#include "../defines.hpp"
#include <algorithm>
#include "../Compositor.hpp"
#include <optional>
#include <set>
#include <sys/utsname.h>
#include <iomanip>
#include <sstream>
#ifdef HAS_EXECINFO
#include <execinfo.h>
#endif
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
@@ -156,6 +159,13 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const
Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString);
}
void removeWLSignal(wl_listener* pListener) {
wl_list_remove(&pListener->link);
wl_list_init(&pListener->link);
Debug::log(LOG, "Removed listener {:x}", (uintptr_t)pListener);
}
void handleNoop(struct wl_listener* listener, void* data) {
// Do nothing
}
@@ -182,13 +192,6 @@ std::string escapeJSONStrings(const std::string& str) {
return oss.str();
}
void scaleBox(wlr_box* box, float scale) {
box->width = std::round(box->width * scale);
box->height = std::round(box->height * scale);
box->x = std::round(box->x * scale);
box->y = std::round(box->y * scale);
}
std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
@@ -208,12 +211,12 @@ std::string removeBeginEndSpacesTabs(std::string str) {
return str;
}
float getPlusMinusKeywordResult(std::string source, float relative) {
std::optional<float> getPlusMinusKeywordResult(std::string source, float relative) {
try {
return relative + stof(source);
} catch (...) {
Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source);
return INT_MAX;
return {};
}
}
@@ -246,9 +249,13 @@ bool isDirection(const std::string& arg) {
return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b";
}
bool isDirection(const char& arg) {
return arg == 'l' || arg == 'r' || arg == 'u' || arg == 'd' || arg == 't' || arg == 'b';
}
int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
int result = INT_MAX;
if (in.find("special") == 0) {
int result = WORKSPACE_INVALID;
if (in.starts_with("special")) {
outName = "special";
if (in.length() > 8) {
@@ -262,7 +269,7 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
}
return SPECIAL_WORKSPACE_START;
} else if (in.find("name:") == 0) {
} else if (in.starts_with("name:")) {
const auto WORKSPACENAME = in.substr(in.find_first_of(':') + 1);
const auto WORKSPACE = g_pCompositor->getWorkspaceByName(WORKSPACENAME);
if (!WORKSPACE) {
@@ -271,26 +278,26 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
result = WORKSPACE->m_iID;
}
outName = WORKSPACENAME;
} else if (in.find("empty") == 0) {
} else if (in.starts_with("empty")) {
int id = 0;
while (++id < INT_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0))
return id;
}
} else if (in.find("prev") == 0) {
} else if (in.starts_with("prev")) {
if (!g_pCompositor->m_pLastMonitor)
return INT_MAX;
return WORKSPACE_INVALID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
if (!PWORKSPACE)
return INT_MAX;
return WORKSPACE_INVALID;
const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->m_sPrevWorkspace.iID);
if (!PLASTWORKSPACE)
return INT_MAX;
return WORKSPACE_INVALID;
outName = PLASTWORKSPACE->m_szName;
return PLASTWORKSPACE->m_iID;
@@ -298,10 +305,15 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (in[0] == 'r' && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Relative monitor workspace on monitor null!");
result = INT_MAX;
return result;
return WORKSPACE_INVALID;
}
result = (int)getPlusMinusKeywordResult(in.substr(1), 0);
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = (int)PLUSMINUSRESULT.value();
int remains = (int)result;
@@ -389,12 +401,12 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
int beginID = finalWSID;
int curID = finalWSID;
while (--curID > 0 && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) {
if (!invalidWSes.contains(curID)) {
remainingWSes--;
}
finalWSID = curID;
}
if (finalWSID <= 0 || invalidWSes.find(finalWSID) != invalidWSes.end()) {
if (finalWSID <= 0 || invalidWSes.contains(finalWSID)) {
if (namedWSes.size()) {
// Go to the named workspaces
// Need remainingWSes more
@@ -414,7 +426,7 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (walkDir == '+') {
int curID = finalWSID;
while (++curID < INT32_MAX && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) {
if (!invalidWSes.contains(curID)) {
remainingWSes--;
}
finalWSID = curID;
@@ -433,12 +445,16 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Relative monitor workspace on monitor null!");
result = INT_MAX;
return result;
return WORKSPACE_INVALID;
}
// monitor relative
result = (int)getPlusMinusKeywordResult(in.substr(1), 0);
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = (int)PLUSMINUSRESULT.value();
// result now has +/- what we should move on mon
int remains = (int)result;
@@ -479,11 +495,15 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
outName = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName;
} else {
if (in[0] == '+' || in[0] == '-') {
if (g_pCompositor->m_pLastMonitor)
result = std::max((int)getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace), 1);
else {
if (g_pCompositor->m_pLastMonitor) {
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace);
if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = std::max((int)PLUSMINUSRESULT.value(), 1);
} else {
Debug::log(ERR, "Relative workspace on no mon!");
result = INT_MAX;
return WORKSPACE_INVALID;
}
} else if (isNumber(in))
result = std::max(std::stoi(in), 1);
@@ -501,6 +521,43 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
return result;
}
std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceName, std::string dirtyCmd) {
std::string cmd = removeBeginEndSpacesTabs(dirtyCmd);
if (!cmd.empty()) {
std::string rules;
const std::string workspaceRule = "workspace " + inWorkspaceName;
if (cmd[0] == '[') {
const int closingBracketIdx = cmd.find_last_of(']');
auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
cmd = cmd.substr(closingBracketIdx + 1);
auto rulesList = CVarList(tmpRules, 0, ';');
bool hadWorkspaceRule = false;
rulesList.map([&](std::string& rule) {
if (rule.find("workspace") == 0) {
rule = workspaceRule;
hadWorkspaceRule = true;
}
});
if (!hadWorkspaceRule)
rulesList.append(workspaceRule);
rules = "[" + rulesList.join(";") + "]";
} else {
rules = "[" + workspaceRule + "]";
}
return std::optional<std::string>(rules + " " + cmd);
}
return std::nullopt;
}
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2) {
const float DX = std::max({0.0, p1.x - vec.x, vec.x - p2.x});
const float DY = std::max({0.0, p1.y - vec.y, vec.y - p2.y});
@@ -527,10 +584,10 @@ void logSystemInfo() {
uname(&unameInfo);
Debug::log(LOG, "System name: {}", unameInfo.sysname);
Debug::log(LOG, "Node name: {}", unameInfo.nodename);
Debug::log(LOG, "Release: {}", unameInfo.release);
Debug::log(LOG, "Version: {}", unameInfo.version);
Debug::log(LOG, "System name: {}", std::string{unameInfo.sysname});
Debug::log(LOG, "Node name: {}", std::string{unameInfo.nodename});
Debug::log(LOG, "Release: {}", std::string{unameInfo.release});
Debug::log(LOG, "Version: {}", std::string{unameInfo.version});
Debug::log(NONE, "\n");
@@ -626,11 +683,11 @@ int64_t getPPIDof(int64_t pid) {
}
int64_t configStringToInt(const std::string& VALUE) {
if (VALUE.find("0x") == 0) {
if (VALUE.starts_with("0x")) {
// Values with 0x are hex
const auto VALUEWITHOUTHEX = VALUE.substr(2);
return stol(VALUEWITHOUTHEX, nullptr, 16);
} else if (VALUE.find("rgba(") == 0 && VALUE.find(')') == VALUE.length() - 1) {
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) {
@@ -642,7 +699,7 @@ int64_t configStringToInt(const std::string& VALUE) {
// now we need to RGBA -> ARGB. The config holds ARGB only.
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
} else if (VALUE.find("rgb(") == 0 && VALUE.find(')') == VALUE.length() - 1) {
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) {
@@ -653,11 +710,15 @@ int64_t configStringToInt(const std::string& VALUE) {
const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
return RGB + 0xFF000000; // 0xFF for opaque
} else if (VALUE.find("true") == 0 || VALUE.find("on") == 0 || VALUE.find("yes") == 0) {
} else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
return 1;
} else if (VALUE.find("false") == 0 || VALUE.find("off") == 0 || VALUE.find("no") == 0) {
} else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
return 0;
}
if (VALUE.empty() || !isNumber(VALUE))
return 0;
return std::stoll(VALUE);
}
@@ -689,9 +750,10 @@ std::string replaceInString(std::string subject, const std::string& search, cons
std::vector<SCallstackFrameInfo> getBacktrace() {
std::vector<SCallstackFrameInfo> callstack;
void* bt[1024];
size_t btSize;
char** btSymbols;
#ifdef HAS_EXECINFO
void* bt[1024];
size_t btSize;
char** btSymbols;
btSize = backtrace(bt, 1024);
btSymbols = backtrace_symbols(bt, btSize);
@@ -699,6 +761,9 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
for (size_t i = 0; i < btSize; ++i) {
callstack.emplace_back(SCallstackFrameInfo{bt[i], std::string{btSymbols[i]}});
}
#else
callstack.emplace_back(SCallstackFrameInfo{nullptr, "configuration does not support execinfo.h"});
#endif
return callstack;
}
@@ -706,4 +771,38 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
void throwError(const std::string& err) {
Debug::log(CRIT, "Critical error thrown: {}", err);
throw std::runtime_error(err);
}
uint32_t drmFormatToGL(uint32_t drm) {
switch (drm) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
#ifdef GLES2
return GL_RGB10_A2_EXT;
#else
return GL_RGB10_A2;
#endif
default: return GL_RGBA;
}
UNREACHABLE();
return GL_RGBA;
}
uint32_t glFormatToType(uint32_t gl) {
return gl != GL_RGBA ?
#ifdef GLES2
GL_UNSIGNED_INT_2_10_10_10_REV_EXT :
#else
GL_UNSIGNED_INT_2_10_10_10_REV :
#endif
GL_UNSIGNED_BYTE;
}
bool envEnabled(const std::string& env) {
const auto ENV = getenv(env.c_str());
if (!ENV)
return false;
return std::string(ENV) == "1";
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <optional>
#include <string>
#include <wayland-server.h>
#include <wlr/util/box.h>
@@ -14,23 +15,28 @@ struct SCallstackFrameInfo {
std::string absolutePath(const std::string&, const std::string&);
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
void removeWLSignal(wl_listener*);
std::string escapeJSONStrings(const std::string& str);
void scaleBox(wlr_box*, float);
std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false);
bool isDirection(const std::string&);
bool isDirection(const char&);
int getWorkspaceIDFromString(const std::string&, std::string&);
std::optional<std::string> cleanCmdForWorkspace(const std::string&, std::string);
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
void logSystemInfo();
std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid);
int64_t configStringToInt(const std::string&);
float getPlusMinusKeywordResult(std::string in, float relative);
std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
uint32_t drmFormatToGL(uint32_t drm);
uint32_t glFormatToType(uint32_t gl);
bool envEnabled(const std::string& env);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View File

@@ -8,7 +8,7 @@ int ratHandler(void* data) {
return 1;
}
CMonitor::CMonitor() {
CMonitor::CMonitor() : state(this) {
wlr_damage_ring_init(&damage);
}
@@ -40,14 +40,20 @@ void CMonitor::onConnect(bool noRule) {
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this);
hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this);
tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
if (m_bEnabled) {
wlr_output_enable(output, 1);
wlr_output_commit(output);
wlr_output_state_set_enabled(state.wlr(), true);
state.commit();
return;
}
szName = output->name;
szDescription = output->description ? output->description : "";
// remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
szDescription.erase(std::remove(szDescription.begin(), szDescription.end(), ','), szDescription.end());
if (!wlr_backend_is_drm(output->backend))
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable
@@ -57,8 +63,8 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore
if (monitorRule.disabled) {
wlr_output_set_scale(output, 1);
wlr_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL);
wlr_output_state_set_scale(state.wlr(), 1);
wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL);
auto PREFSTATE = wlr_output_preferred_mode(output);
@@ -66,9 +72,9 @@ void CMonitor::onConnect(bool noRule) {
wlr_output_mode* mode;
wl_list_for_each(mode, &output->modes, link) {
wlr_output_set_mode(output, PREFSTATE);
wlr_output_state_set_mode(state.wlr(), mode);
if (!wlr_output_test(output))
if (!wlr_output_test_state(output, state.wlr()))
continue;
PREFSTATE = mode;
@@ -77,13 +83,13 @@ void CMonitor::onConnect(bool noRule) {
}
if (PREFSTATE)
wlr_output_set_mode(output, PREFSTATE);
wlr_output_state_set_mode(state.wlr(), PREFSTATE);
else
Debug::log(WARN, "No mode found for disabled output {}", output->name);
wlr_output_enable(output, 0);
wlr_output_state_set_enabled(state.wlr(), 0);
if (!wlr_output_commit(output))
if (!state.commit())
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false;
@@ -107,29 +113,32 @@ void CMonitor::onConnect(bool noRule) {
m_bRenderingInitPassed = true;
}
if (!m_pThisWrap) {
std::shared_ptr<CMonitor>* thisWrapper = nullptr;
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
m_pThisWrap = &m;
break;
}
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
thisWrapper = &m;
break;
}
}
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) {
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap);
}
RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???");
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end())
g_pCompositor->m_vMonitors.push_back(*thisWrapper);
m_bEnabled = true;
wlr_output_enable(output, 1);
wlr_output_state_set_enabled(state.wlr(), 1);
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
@@ -150,8 +159,6 @@ void CMonitor::onConnect(bool noRule) {
if (scale < 0.1)
scale = getDefaultScale();
m_pThisWrap = nullptr;
forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering.
//
@@ -182,9 +189,11 @@ void CMonitor::onConnect(bool noRule) {
g_pCompositor->setActiveMonitor(this);
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this);
}
void CMonitor::onDisconnect() {
void CMonitor::onDisconnect(bool destroy) {
if (renderTimer) {
wl_event_source_remove(renderTimer);
@@ -219,9 +228,6 @@ void CMonitor::onDisconnect() {
g_pConfigManager->m_bWantsMonitorReload = true;
}
m_bEnabled = false;
m_bRenderingInitPassed = false;
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
@@ -243,9 +249,12 @@ void CMonitor::onDisconnect() {
if (!BACKUPMON) {
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
g_pCompositor->m_bUnsafeState = true;
g_pCompositor->enterUnsafeState();
}
m_bEnabled = false;
m_bRenderingInitPassed = false;
if (BACKUPMON) {
// snap cursor
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f,
@@ -254,7 +263,7 @@ void CMonitor::onDisconnect() {
// move workspaces
std::deque<CWorkspace*> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) {
wspToMove.push_back(w.get());
}
}
@@ -272,11 +281,13 @@ void CMonitor::onDisconnect() {
activeWorkspace = -1;
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output);
if (!destroy)
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output);
wlr_output_enable(output, false);
wlr_output_state_set_enabled(state.wlr(), false);
wlr_output_commit(output);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect");
if (g_pCompositor->m_pLastMonitor == this)
g_pCompositor->setActiveMonitor(BACKUPMON);
@@ -294,7 +305,6 @@ void CMonitor::onDisconnect() {
g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz;
}
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
}
@@ -313,14 +323,14 @@ void CMonitor::addDamage(const CRegion* rg) {
addDamage(const_cast<CRegion*>(rg)->pixman());
}
void CMonitor::addDamage(const wlr_box* box) {
void CMonitor::addDamage(const CBox* box) {
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
wlr_damage_ring_add_whole(&damage);
g_pCompositor->scheduleFrameForMonitor(this);
}
if (wlr_damage_ring_add_box(&damage, box))
if (wlr_damage_ring_add_box(&damage, const_cast<CBox*>(box)->pWlr()))
g_pCompositor->scheduleFrameForMonitor(this);
}
@@ -349,7 +359,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
findAvailableDefaultWS() :
getWorkspaceIDFromString(g_pConfigManager->getDefaultWorkspaceFor(szName), newDefaultWorkspaceName);
if (WORKSPACEID == INT_MAX || (WORKSPACEID >= SPECIAL_WORKSPACE_START && WORKSPACEID <= -2)) {
if (WORKSPACEID == WORKSPACE_INVALID || (WORKSPACEID >= SPECIAL_WORKSPACE_START && WORKSPACEID <= -2)) {
WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1;
newDefaultWorkspaceName = std::to_string(WORKSPACEID);
@@ -412,19 +422,22 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
vecPosition = RULE.offset;
// push to mvmonitors
if (!m_pThisWrap) {
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
m_pThisWrap = &m;
break;
}
std::shared_ptr<CMonitor>* thisWrapper = nullptr;
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
thisWrapper = &m;
break;
}
}
RASSERT(thisWrapper->get(), "CMonitor::setMirror: Had no wrapper???");
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) ==
g_pCompositor->m_vMonitors.end()) {
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap);
g_pCompositor->m_vMonitors.push_back(*thisWrapper);
}
setupDefaultWS(RULE);
@@ -491,7 +504,7 @@ float CMonitor::getDefaultScale() {
return 1;
}
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove) {
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove, bool noFocus) {
if (!pWorkspace)
return;
@@ -522,24 +535,24 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool
}
}
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
if (!noFocus && !g_pCompositor->m_pLastMonitor->specialWorkspaceID) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
CWindow* pWindow = pWorkspace->getLastFocusedWindow();
if (const auto PLASTWINDOW = pWorkspace->getLastFocusedWindow(); PLASTWINDOW)
g_pCompositor->focusWindow(PLASTWINDOW);
else {
CWindow* pWindow = nullptr;
if (!pWindow) {
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal());
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
}
g_pCompositor->focusWindow(pWindow);
}
if (!noMouseMove)
g_pInputManager->simulateMouseMovement();
@@ -552,6 +565,10 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool
g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pCompositor->updateSuspendedStates();
}
void CMonitor::changeWorkspace(const int& id, bool internal) {
@@ -577,6 +594,8 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
else
g_pInputManager->refocus();
g_pCompositor->updateSuspendedStates();
return;
}
@@ -604,7 +623,23 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
w->m_iMonitorID = ID;
w->updateSurfaceOutputs();
w->updateSurfaceScaleTransformDetails();
const auto MIDDLE = w->middle();
if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && w->m_iX11Type != 2) {
// if it's floating and the middle isnt on the current mon, move it to the center
const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE);
Vector2D pos = w->m_vRealPosition.goalv();
if (!VECINRECT(MIDDLE, PMONFROMMIDDLE->vecPosition.x, PMONFROMMIDDLE->vecPosition.y, PMONFROMMIDDLE->vecPosition.x + PMONFROMMIDDLE->vecSize.x,
PMONFROMMIDDLE->vecPosition.y + PMONFROMMIDDLE->vecSize.y)) {
// not on any monitor, center
pos = middle() / 2.f - w->m_vRealSize.goalv() / 2.f;
} else
pos = pos - PMONFROMMIDDLE->vecPosition + vecPosition;
w->m_vRealPosition = pos;
w->m_vPosition = pos;
}
}
}
@@ -618,6 +653,8 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateSuspendedStates();
}
void CMonitor::setSpecialWorkspace(const int& id) {
@@ -633,4 +670,42 @@ void CMonitor::moveTo(const Vector2D& pos) {
Vector2D CMonitor::middle() {
return vecPosition + vecSize / 2.f;
}
}
void CMonitor::updateMatrix() {
wlr_matrix_identity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
wlr_matrix_transform(projMatrix.data(), transform);
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
wlr_output_state_init(&m_state);
}
CMonitorState::~CMonitorState() {
wlr_output_state_finish(&m_state);
}
wlr_output_state* CMonitorState::wlr() {
return &m_state;
}
void CMonitorState::clear() {
wlr_output_state_finish(&m_state);
m_state = {0};
wlr_output_state_init(&m_state);
}
bool CMonitorState::commit() {
bool ret = wlr_output_commit_state(m_pOwner->output, &m_state);
clear();
return ret;
}
bool CMonitorState::test() {
return wlr_output_test_state(m_pOwner->output, &m_state);
}

View File

@@ -25,6 +25,25 @@ struct SMonitorRule {
std::optional<int> vrr;
};
class CMonitor;
// Class for wrapping the wlr state
class CMonitorState {
public:
CMonitorState(CMonitor* owner);
~CMonitorState();
wlr_output_state* wlr();
void clear();
// commit() will also clear()
bool commit();
bool test();
private:
wlr_output_state m_state = {0};
CMonitor* m_pOwner;
};
class CMonitor {
public:
CMonitor();
@@ -40,40 +59,47 @@ class CMonitor {
uint64_t ID = -1;
int activeWorkspace = -1;
float scale = 1;
float setScale = 1; // scale set by cfg
float scale = 1; // real scale
std::string szName = "";
std::string szName = "";
std::string szDescription = "";
Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0);
drmModeModeInfo customDrmMode = {};
CMonitorState state;
// WLR stuff
wlr_damage_ring damage;
wlr_output* output = nullptr;
float refreshRate = 60;
int framesToSkip = 0;
int forceFullFrames = 0;
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
bool gammaChanged = false;
float xwaylandScale = 1.f;
wlr_damage_ring damage;
wlr_output* output = nullptr;
float refreshRate = 60;
int framesToSkip = 0;
int forceFullFrames = 0;
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
bool gammaChanged = false;
float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0};
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false;
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false;
uint32_t drmFormat = DRM_FORMAT_INVALID;
bool isUnsafeFallback = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false;
wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false;
CTimer lastPresentationTimer;
wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false;
CTimer lastPresentationTimer;
SMonitorRule activeMonitorRule;
SMonitorRule activeMonitorRule;
// mirroring
CMonitor* pMirrorOf = nullptr;
@@ -81,6 +107,18 @@ class CMonitor {
CRegion lastFrameDamage; // stores last frame damage
// for tearing
CWindow* solitaryClient = nullptr;
struct {
bool canTear = false;
bool nextRenderTorn = false;
bool activelyTearing = false;
bool busy = false;
bool frameScheduledWhileBusy = false;
} tearingState;
// for the special workspace. 0 means not open.
int specialWorkspaceID = 0;
@@ -95,24 +133,24 @@ class CMonitor {
DYNLISTENER(monitorBind);
// methods
void onConnect(bool noRule);
void onDisconnect();
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg);
void addDamage(const wlr_box* box);
void setMirror(const std::string&);
bool isMirror();
float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false);
void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
void onConnect(bool noRule);
void onDisconnect(bool destroy = false);
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg);
void addDamage(const CBox* box);
void setMirror(const std::string&);
bool isMirror();
float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
void updateMatrix();
std::shared_ptr<CMonitor>* m_pThisWrap = nullptr;
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
// For the list lookup

View File

@@ -1,6 +1,8 @@
#include "Region.hpp"
extern "C" {
#include <wlr/util/box.h>
#include <wlr/util/region.h>
}
CRegion::CRegion() {
pixman_region32_init(&m_rRegion);
@@ -19,6 +21,10 @@ CRegion::CRegion(wlr_box* box) {
pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->width, box->height);
}
CRegion::CRegion(const CBox& box) {
pixman_region32_init_rect(&m_rRegion, box.x, box.y, box.w, box.h);
}
CRegion::CRegion(pixman_box32_t* box) {
pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1);
}
@@ -57,6 +63,11 @@ CRegion& CRegion::add(double x, double y, double w, double h) {
return *this;
}
CRegion& CRegion::add(const CBox& other) {
pixman_region32_union_rect(&m_rRegion, &m_rRegion, other.x, other.y, other.w, other.h);
return *this;
}
CRegion& CRegion::subtract(const CRegion& other) {
pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this;
@@ -77,11 +88,25 @@ CRegion& CRegion::invert(pixman_box32_t* box) {
return *this;
}
CRegion& CRegion::invert(const CBox& box) {
pixman_box32 pixmanBox = {box.x, box.y, box.w + box.x, box.h + box.y};
return this->invert(&pixmanBox);
}
CRegion& CRegion::translate(const Vector2D& vec) {
pixman_region32_translate(&m_rRegion, vec.x, vec.y);
return *this;
}
CRegion& CRegion::transform(const wl_output_transform t, double w, double h) {
wlr_region_transform(&m_rRegion, &m_rRegion, t, w, h);
return *this;
}
CRegion CRegion::copy() const {
return CRegion(*this);
}
CRegion& CRegion::scale(float scale) {
wlr_region_scale(&m_rRegion, &m_rRegion, scale);
return *this;
@@ -98,7 +123,7 @@ std::vector<pixman_box32_t> CRegion::getRects() const {
return result;
}
wlr_box CRegion::getExtents() {
CBox CRegion::getExtents() {
pixman_box32_t* box = pixman_region32_extents(&m_rRegion);
return {box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1};
}

View File

@@ -2,6 +2,7 @@
#include <pixman.h>
#include <vector>
#include "Vector2D.hpp"
#include "Box.hpp"
struct wlr_box;
@@ -15,6 +16,8 @@ class CRegion {
CRegion(double x, double y, double w, double h);
/* Create from a wlr_box */
CRegion(wlr_box* box);
/* Create from a CBox */
CRegion(const CBox& box);
/* Create from a pixman_box32_t */
CRegion(pixman_box32_t* box);
@@ -37,21 +40,25 @@ class CRegion {
CRegion& set(const CRegion& other);
CRegion& add(const CRegion& other);
CRegion& add(double x, double y, double w, double h);
CRegion& add(const CBox& other);
CRegion& subtract(const CRegion& other);
CRegion& intersect(const CRegion& other);
CRegion& intersect(double x, double y, double w, double h);
CRegion& translate(const Vector2D& vec);
CRegion& transform(const wl_output_transform t, double w, double h);
CRegion& invert(pixman_box32_t* box);
CRegion& invert(const CBox& box);
CRegion& scale(float scale);
wlr_box getExtents();
CBox getExtents();
bool containsPoint(const Vector2D& vec) const;
bool empty() const;
Vector2D closestPoint(const Vector2D& vec) const;
CRegion copy() const;
std::vector<pixman_box32_t> getRects() const;
pixman_region32_t* pixman() {
return &m_rRegion;
return &m_rRegion;
}
private:

View File

@@ -19,6 +19,21 @@ inline const std::vector<std::string> SPLASHES = {
"Compile, wait for 20 minutes, notice a new commit, compile again.",
"To rice, or not to rice, that is the question.",
"Now available on Fedora!",
"\"Hyprland is so good it starts with a capital letter\" - Hazel",
"\"please make this message a splash\" - eriedaberrie",
"\"the only wayland compositor powered by fried chicken\" - raf",
"\"This will never get into Hyprland\" - Flafy",
"\"Hyprland only gives you up on -git\" - fazzi",
"Segmentation fault (core dumped)",
"\"disabling hyprland logo is a war crime\" - vaxry",
"some basic startup code",
"\"I think I am addicted to hyprland\" - mathisbuilder",
"\"hyprland is the most important package in the arch repos\" - jacekpoz",
"Thanks Brodie!",
"Thanks fufexan!",
"Thanks raf!",
"You can't use --splash to change this message :)",
"Hyprland will overtake Gnome in popularity by [insert year]",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",

Some files were not shown because too many files have changed in this diff Show More