Compare commits

...

331 Commits

Author SHA1 Message Date
Mihai Fufezan
388bf32f2f src/config/ConfigWatcher.cpp: watch both symlinks and canonical paths 2025-01-31 15:10:32 +02:00
nyx
ef03f69116 xwayland: handle window coords correctly (#9238) 2025-01-30 22:36:42 +01:00
Tom Englund
32c0fa2f2f core: begin using CFileDescriptor from hyprutils (#9122)
* config: make fd use CFileDescriptor

make use of the new hyprutils CFileDescriptor instead of manual FD
handling.

* hyprctl: make fd use CFileDescriptor

make use of the new hyprutils CFileDescriptor instead of manual FD
handling.

* ikeyboard: make fd use CFileDescriptor

make use of the new CFileDescriptor instead of manual FD handling, also
in sendKeymap remove dead code, it already early returns if keyboard
isnt valid, and dont try to close the FD that ikeyboard owns.

* core: make SHMFile functions use CFileDescriptor

make SHMFile misc functions use CFileDescriptor and its associated usage
in dmabuf and keyboard.

* core: make explicit sync use CFileDescriptor

begin using CFileDescriptor in explicit sync and its timelines and
eglsync usage in opengl, there is still a bit left with manual handling
that requires future aquamarine change aswell.

* eventmgr: make fd and sockets use CFileDescriptor

make use of the hyprutils CFileDescriptor instead of manual FD and
socket handling and closing.

* eventloopmgr: make timerfd use CFileDescriptor

make the timerfd use CFileDescriptor instead of manual fd handling

* opengl: make gbm fd use CFileDescriptor

make the gbm rendernode fd use CFileDescriptor instead of manual fd
handling

* core: make selection source/offer use CFileDescriptor

make data selection source and offers use CFileDescriptor and its
associated use in xwm and protocols

* protocols: convert protocols fd to CFileDescriptor

make most fd handling use CFileDescriptor in protocols

* shm: make SHMPool use CfileDescriptor

make SHMPool use CFileDescriptor instead of manual fd handling.

* opengl: duplicate fd with CFileDescriptor

duplicate fenceFD with CFileDescriptor duplicate instead.

* xwayland: make sockets and fds use CFileDescriptor

instead of manual opening/closing make sockets and fds use
CFileDescriptor

* keybindmgr: make sockets and fds use CFileDescriptor

make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 11:30:12 +00:00
Honkazel
7d1c78f4a3 core,hyprctl: clang, clang-tidy, typo fixes and dtors changes (#9233)
* declare dtor once + DMABBUF typo fix

* dup include + clang moment

* linux-dmabuf: last minute nit change
2025-01-30 11:07:06 +00:00
Vaxry
d462cc7fa1 subsurface: fix invalid parent typo
fixes #9224
2025-01-29 23:16:25 +00:00
Vaxry
09ec1cca51 popup: stop refocusing at unmap
fixes #9018
2025-01-29 23:05:59 +00:00
Mihai Fufezan
6fc9c8e479 flake.lock: update 2025-01-29 22:45:38 +02:00
Vaxry
6131919715 monitor: round refresh rates in sorting modes
fixes #9209
2025-01-29 13:34:11 +00:00
vaxerski
aaa5573c73 config/hyprctl: fix keyword not updating autoreload
ref #9139
2025-01-29 10:50:39 +00:00
vaxerski
344e32d71b pass/rect: fix bounding / opaque regions
fixes #9212
2025-01-29 10:42:46 +00:00
Owen L.
d41135d07c input: change window grab cursor to closed hand (#9196) 2025-01-29 10:27:34 +01:00
nyx
b884f1f7c8 renderer: calculate UV using both pixel and monitor dimensions (#9210) 2025-01-29 09:41:56 +01:00
Vaxry
3d1dd6b5c7 presentation: log a fixme when there is a feedback leak
ref #8087
2025-01-28 23:43:31 +00:00
Tom Englund
1d3904c3e7 configmgr: properly free glob memory
globfree is only freeing internally allocated resources, so also call
free the on glob_t memory we allocated.
2025-01-28 11:19:52 +01:00
Tom Englund
529ad4eaf4 ikeyboard: free xkbSymState in clearManuallyAllocd
asan reported a leak on xkbSymState on destruction, because it wasnt
beeing unrefed, was only being unrefed on calls to updateXKBTranslationState.
2025-01-28 11:19:52 +01:00
DDoSolitary
d3042e5358 xwayland: respect window size set by configure requests (#9190) 2025-01-28 10:04:57 +00:00
Jan Beich
d2773d7a4e deps: add libinotify-kqueue on BSDs after 8dd2cd41fb (#9197)
src/config/ConfigWatcher.cpp:2:10: fatal error: 'sys/inotify.h' file not found
    2 | #include <sys/inotify.h>
      |          ^~~~~~~~~~~~~~~
2025-01-27 23:06:48 +01:00
matt1432
5fd90548dc nix: fix duplicate inputs and update flake.lock 2025-01-27 22:45:18 +02:00
Mihai Fufezan
25d5ce4833 CI/setup_base: add libspng 2025-01-27 22:25:27 +02:00
DDoSolitary
e7a72de9b5 xwayland: send synthetic configure events (#9193) 2025-01-27 17:45:15 +01:00
littleblack111
cb7ed4f62b ci: clang-format fix (#9145)
* move

* revert, comment via peter-evans/commit-comment@v3

* remove
2025-01-27 17:41:26 +01:00
Vaxry
2a478c30ca core: fix clang-format 2025-01-27 13:41:38 +00:00
Vaxry
04ac46c543 version: bump to 0.47.0 2025-01-27 11:43:43 +00:00
Vaxry
2f55806d6f renderer: fix rare case when a tiled window would be rendered over fs 2025-01-26 21:06:25 +00:00
user111111111111111111111111111111111
3b207d29bd core: update groups on movewindow (#9183) 2025-01-26 20:06:50 +01:00
Vaxry
6bd6c5512e hooksystem: avoid huge include for HANDLE 2025-01-26 18:38:35 +00:00
Vaxry
bb5b09def0 renderer: fix funky corners
oopsie
2025-01-26 16:00:29 +00:00
Vaxry
efe29a2461 shadow: avoid drawing empty shadows 2025-01-26 16:00:28 +00:00
Vaxry
e951011503 renderer/internal: stop using box pointers
in favor of const refs
2025-01-26 15:05:34 +00:00
Vaxry
16aeb24bc1 core: make persistent workspaces always follow the config
instead of just staying after open, they will now be enforced on their respective monitors

fixes #8769
2025-01-26 14:40:42 +00:00
Vaxry
74d0f34cf3 pointer: always scale the cpu cursor to the right size
fixes #9003
2025-01-26 14:25:54 +00:00
Maximilian Seidler
4abf9155ee window: only set m_iMonitorMovedFrom when the workspace is visible (#9178) 2025-01-26 14:00:52 +01:00
Vaxry
0a28e13787 desktop: move desktop types to memory-safe pointers 2025-01-26 12:54:38 +00:00
Maximilian Seidler
3cd6e3960f xwayland: fix pointer mismatches with multiple monitors (#9179) 2025-01-26 13:23:39 +01:00
Vaxry
8b1d6e3009 subsurface: damage the entire parent on size change
the previous method doesn't exactly work, idk todo fix

fixes #8784
2025-01-25 21:57:58 +00:00
Vaxry
445acec2a2 core: move sendWindowSize off of xwaylandmgr
additionally fixes that one weird x11 issue with floating windows being mis-sized on open
2025-01-25 20:36:44 +00:00
Vaxry
45c3787e75 window: revert only set m_iMonitorMovedFrom, when moving to a different monitor
This reverts commit 089fdd1ea0.

Great commit.
2025-01-25 19:37:52 +00:00
Junxuan Liao
9199a9746d input: pass touch events to lock screens (#9129)
* refactor: use weak pointers for session lock surfaces

* input: pass touch events to lock screens
2025-01-25 19:44:13 +01:00
Vaxry
bce58d9d65 dwindle: fix possible crash on null ws 2025-01-25 18:34:15 +00:00
heather7283
f3fc8d599a config/ConfigWatcher.cpp: add missing include needed for clang (#9166) 2025-01-25 19:23:53 +01:00
Maximilian Seidler
107723bdf4 config: disable borderangle by default (#9165) 2025-01-25 19:23:36 +01:00
Mihai Fufezan
065e89648b flake.lock: update 2025-01-25 15:33:27 +02:00
Vaxry
354d4594de xwayland: various window handling fixes
I hate this fucking garbage
2025-01-25 02:35:41 +00:00
Maximilian Seidler
089fdd1ea0 window: only set m_iMonitorMovedFrom, when moving to a different monitor (#9160) 2025-01-25 00:27:24 +01:00
heather7283
1815f9a2e5 compositor: fix incorrect cast, use lambda capture instead (#9161) 2025-01-25 00:26:46 +01:00
Vaxry
d8f79d7678 core: add --verify-config to verify the config with Hyprland
fixes #9135
2025-01-24 20:30:21 +00:00
Maximilian Seidler
80b2fd135b animations: fix borderangle once (#9149) 2025-01-24 19:22:05 +01:00
Vaxry
fda9790cde layout: force full damage on toggling floating mode
fixes #8849
2025-01-24 17:51:16 +00:00
heather7283
9b3f71390c layershell: check if layer is valid (#9156)
fixes compositor crash when client tried to create a layer surface with
invalid layer argument
2025-01-24 16:51:31 +01:00
Maximilian Seidler
0e5d03a557 datadevice: do the unfocus surface stuff before dndActive is true (#9157) 2025-01-24 16:50:15 +01:00
Vaxry
57a39984dd input: abord dnd op on escape pressed
ref #9154
2025-01-24 13:37:30 +00:00
Vaxry
944e36ea2e config: fix misc:disable_autoreload
fixes #9139
2025-01-24 13:23:23 +00:00
Vaxry
4a1b960cbe layout: set window size after toggling floating 2025-01-24 00:49:47 +00:00
Vaxry
5d8261aee2 xwayland: avoid sending value of real size to xwayland 2025-01-24 00:25:14 +00:00
Vaxry
d075d1cab9 pass/rect: fix bounding box 2025-01-23 22:59:42 +00:00
Vaxry
bb099e5733 pass/rect: fix box shrinking with bb
fixes #9084
2025-01-23 22:57:14 +00:00
Vaxry
dadb2e0949 opengl: use uv to avoid rendering the entire blurbox
additionally can use smoothing for rounding

fixes #9086
2025-01-23 22:41:46 +00:00
Vaxry
a8c2d5a616 layout: damage window properly on float mode changes
fixes #8849
2025-01-23 22:15:14 +00:00
outfoxxed
465cf66df1 protocols: add hyprland_surface_v1.set_visible_region implementation (#9120) 2025-01-23 22:57:33 +01:00
Vaxry
1a0a22ad03 configmgr: fix crash on very early plugin loads 2025-01-23 21:27:11 +00:00
Vaxry
0a1ae48a9f core: move all shared_ptrs from the STL to hyprutils (#9143) 2025-01-23 21:55:41 +01:00
Mihai Fufezan
ae403e6a05 flake.lock: update 2025-01-23 14:48:37 +02:00
Junxuan Liao
ae4e38d9d5 tablet: send motions on tip events (#9132)
Typically, the position of the tool tip also changes on tool tip events, so we
should forward this update to the clients.
2025-01-23 12:35:13 +00:00
littleblack111
ecae3c5e4b ci: fix "Resource not accessible by integration" for cf workflow (#9144) 2025-01-23 12:32:44 +00:00
maround95
f1bd62806e core/renderer: Add GPU hotplug support (#8980) 2025-01-23 11:08:19 +00:00
DDoSolitary
fda5626594 xwayland: fix sending large clipboard data (#9134) 2025-01-22 14:27:46 +00:00
DDoSolitary
c90dbfab6f xwayland: fix clipboard mime name and atom mismatch (#9137) 2025-01-22 14:26:53 +00:00
Vaxry
d335c8f101 fractional-scale: avoid redundant and duplicate scale events
fixes #9126
2025-01-22 10:41:04 +00:00
amnesiacsardine
c8a0443adc config/ConfigManager.cpp: add instruction to edit config (#9130) 2025-01-22 12:16:46 +02:00
Christoph Hrdinka
ce48bc5408 flake.lock: update (#9128) 2025-01-21 21:21:00 +02:00
Alexander
da6e966313 keybinds: add visible arg for cyclenext (#9045)
Co-authored-by: Крылов Александр <aleksandr.krylov@hyperus.team>
2025-01-21 14:17:07 +00:00
Zach DeCook
a661203bb6 xwayland: fix crash when trying to initialize without Xwayland installed (#9077) 2025-01-20 18:40:51 +00:00
DDoSolitary
9e8d9791c7 xwayland: support sending clipboard change notification on focus (#9111) 2025-01-20 17:53:29 +00:00
Maximilian Seidler
2d82a92324 config: fix float animation speeds < 0 (#9123) 2025-01-20 16:48:04 +00:00
Maximilian Seidler
407453166c protocols: add hyprland_lock_notify_v1 implementation (#9092) 2025-01-19 18:21:36 +00:00
vaxerski
8dd2cd41fb core: move to inotify for monitoring the config files
instead of manually polling every second which is not efficient, use inotify.

an added bonus is that inotify is much much faster
2025-01-19 16:11:12 +01:00
vaxerski
0a0e56d99c core: use readFileAsString instead of cat for os-release 2025-01-19 14:04:59 +01:00
outfoxxed
086fd7ece8 protocols: do not destroy screencopy resources before client request (#9048) 2025-01-19 12:51:42 +00:00
Tom Englund
4da9b7cc5b core: reserve vector sizes as much as we can (#9118)
avoid reallocations as much as possible with a few edge cases where the
reservation overshoots a tiny bit. but a few bytes of memory short term
is better used then the overhead of potential reallocation.
2025-01-19 10:38:42 +00:00
Charlie Root
f56153a9c1 nix/module.nix: expand nixos module for configuring hyprland
Expand the nixos module to be able to configure hyprland, just like the
current home-manager module does.
2025-01-18 12:48:38 +02:00
Mihai Fufezan
a36fa5c229 Nix: disable hyprpm
It does not work properly on Nix anyway. If you were using hyprpm in
some way before, please switch to using the `plugins` option in the
HM module (and the upcoming option in the NixOS module, soon™).
2025-01-18 11:39:44 +02:00
Mihai Fufezan
fdfcfc824e CMake, Meson: add option controlling hyprpm building 2025-01-18 11:39:44 +02:00
Mihai Fufezan
d01756c1f4 Meson: properly install 'hyprland' symlink (#9091) 2025-01-18 10:59:25 +02:00
UjinT34
078e13f463 renderer: Auto enable wide color gamut in HDR mode (#9090) 2025-01-17 20:23:57 +00:00
vaxerski
47d645d84a core: fixup includes 2025-01-17 19:14:55 +01:00
Beau
b65f8a8723 desktop/DesktopTypes.hpp: fix include (#9104) 2025-01-17 18:56:43 +01:00
vaxerski
401a3bae61 core: fix warning in shadow 2025-01-17 18:56:43 +01:00
vaxerski
76a899627e regex: log an error if regex parsing fails 2025-01-17 18:56:43 +01:00
vaxerski
b7a3c45269 core: add LIKELY and UNLIKELY macros
helps the compiler optimize
2025-01-17 18:56:39 +01:00
user111111111111111111111111111111111
2bad73354a core: cleanup header includes (#9088)
Cleanup some unneeded includes to speed up compilation
2025-01-17 15:21:35 +00:00
Vaxry
0dc7367a70 renderer: use cairo for cpu buffer rendering (#9071)
Instead of a wonky memcpy which doesn't work when anything non-standard is used (size, pos, transform), just use cairo
2025-01-16 14:42:39 +00:00
UjinT34
52b9ae592b renderer: fix fullscreen hdr check (#9076) 2025-01-16 13:13:47 +01:00
vaxerski
25add26881 renderer: unload background texture if it's disabled
ref #9031
2025-01-14 17:52:32 +01:00
outfoxxed
f16f170433 protocols: immediately copy toplevel content when ignoreDamage set (#9049)
Also sets m_ignoreDamage, as it wasn't set before.
2025-01-14 16:44:09 +00:00
outfoxxed
a6b263713a protocols: allow hyprland-toplevel-export to capture hidden windows (#9041) 2025-01-12 17:10:36 +00:00
outfoxxed
4f0f512cab protocols: do not capture cursor in toplevel without pointer focus (#9042) 2025-01-12 17:09:02 +00:00
UjinT34
a3a7499317 renderer: Do not set hdr metadata unless needed (#9014) 2025-01-12 17:02:41 +00:00
staz
b117fae3b4 keybinds: fix movefocus fallback for special workspaces (#9040) 2025-01-12 17:00:56 +00:00
Toria
2671656a75 helpers/Monitor.cpp: fix include path (#9039) 2025-01-12 18:35:10 +02:00
Maximilian Seidler
2778aff08f animations: fix XWayland cursor glitch and refactor skill issues (#9033) 2025-01-11 16:38:04 +00:00
Maximilian Seidler
9e4f90aedf animation: fixup adding animvars during ::tick (#9030) 2025-01-11 16:35:57 +00:00
Alexander
15dc024a39 keybinds: fix previous_per_monitor logic (#9010)
Co-authored-by: Крылов Александр <aleksandr.krylov@hyperus.team>
2025-01-11 16:05:53 +00:00
littleblack111
3b85690aa6 config: add exec(-onec) with rules and execr(-once) (#8953) 2025-01-11 15:58:05 +00:00
vaxerski
cef09fbfe6 animation: avoid crashes in ::tick() on mutations
mutating the active variables is valid during a tick, we can't let it crash
2025-01-10 21:55:19 +01:00
UjinT34
a8b568c6c4 core: Add render:allow_early_buffer_release to make buffer release configurable (#9019) 2025-01-10 18:42:26 +00:00
Vaxry
b5fb6110ab core: Add a periodic donation request (#8981)
Will fire once in december and july. Disableable with `ecosystem:no_donation:nag`
2025-01-10 18:09:40 +00:00
littleblack111
da9252a23e keybinds: fix nullptr deref in forcekillactive (#9021) 2025-01-10 15:16:52 +00:00
vaxerski
8475a8ef99 core: always use goal size to send to clients 2025-01-10 14:09:16 +01:00
Dardo D Kleiner
f9c37ca43b windows: honor xdg_toplevel_set_fullscreen output hint (#8965)
Co-authored-by: Dardo D Kleiner <dardo.kleiner@nrl.navy.mil>
2025-01-09 22:38:38 +00:00
vaxerski
9dc9366fc6 config: fix animations requiring all args
fixes #9009
2025-01-09 22:08:16 +01:00
littleblack111
85aba23cbe ci(clang-format): directly do the clang-format instead of error (#8955)
Will suggest a clang-format fix on every MR that violates it.
2025-01-09 20:54:25 +00:00
zakk4223
2d1ebadb9b selectors: add a tag: to for matching window tag(s) by regex (#8985) 2025-01-09 20:52:26 +00:00
Maximilian Seidler
e66eab7b6a animationmgr: don't warp based on POINTY value (#9000) 2025-01-09 11:13:47 +00:00
littleblack111
c9822b08f9 keybinds: add new window destruction dispatchers (#8962) 2025-01-08 21:16:34 +00:00
vaxerski
983bc067da opengl: fix crash on null fb stencil op 2025-01-08 12:34:43 +01:00
vaxerski
b320bc2dc6 core: use cpu-buffer hw cursors on nvidia by default 2025-01-08 12:29:24 +01:00
vaxerski
ad64726f5d opengl: only allocate offMainFB on demand
These are not used by hyprland, only potentially plugins. No need to keep the VRAM allocated all the time
2025-01-08 11:33:45 +01:00
Maximilian Seidler
5fa2594659 renderer: don't access hdrMetadata optional if it has no value (#8987) 2025-01-08 10:28:55 +00:00
staz
75727e7c17 protocols: fix compilation error (#8988) 2025-01-08 10:28:47 +00:00
vaxerski
67e1e46f9b window: fixup fade out animation on silent moves 2025-01-07 20:58:49 +01:00
vaxerski
308b1f3afb core: minor fixes for animation rewrite 2025-01-07 20:30:44 +01:00
vaxerski
c143907857 config: update animation even if disabled 2025-01-07 20:27:07 +01:00
UjinT34
830350a1f7 core: Add support for HDR and color management protocols (#8715) 2025-01-07 18:32:50 +00:00
vaxerski
95542e4488 animationmgr: fix invalid include 2025-01-07 19:13:35 +01:00
Maximilian Seidler
5642ed331d core: move parts of the animation system to hyprutils (#8868)
* core: change animation manager to use Hyprutils::Animation

* config: move animation config to hyprutils animation tree

* use g_pAnimationManager->createAnimation and the new PHLANIMVAR template

* core: use CGenericAnimatedVariabled::{enabled,setConfig,getStyle} and adapt callbacks

* core: adapt animated variable usage (dereference the shared pointer)

* misc: bump CMakeLists to hyprutils 0.3.3
2025-01-07 17:55:14 +00:00
Zetta1 Reid0
c7086f936a master: add option to show slaves on left in center orientation (#8940)
Co-authored-by: Zetta1_Reid0 <11255-Zetta1_Reid0@users.noreply.gitlab.xfce.org>
2025-01-07 17:50:34 +00:00
Pollux
b9f110ef87 shaders: fix blank windows when using corner rounding (#8969) (#8971) 2025-01-06 19:30:57 +00:00
Byso
1bf4937b02 hyprctl: fix hyprctl --batch not working with exec rules (#8952) 2025-01-06 16:52:59 +00:00
UjinT34
6a90b50545 core/compositor: fix too early buffer release (#8966) 2025-01-06 16:52:35 +00:00
Mike Will
602d6b7356 snap: don't snap to any windows if workspace has a fullscreen window (#8870) 2025-01-06 16:37:13 +00:00
vaxerski
780e3dd542 [gha] Nix: update inputs 2025-01-06 14:29:29 +00:00
vaxerski
f1a7a7497e datadevice: guard XWayland server against crashes
The server might be dead or restarting, and we'd deref null

ref #7822
2025-01-06 15:27:57 +01:00
vaxerski
f390f48a07 pass: fixup debug mode rendering of input boxes 2025-01-05 23:19:13 +01:00
vaxerski
1b06d222cf pass/surface: fixup invalid expansion by old -1 rounding param
fixes #8889
2025-01-05 20:35:24 +01:00
vaxerski
391ff29110 pass: improve pass debug mode 2025-01-05 19:49:31 +01:00
Pollux
a5c14370c1 renderer: Add supercircular window corners (#8943)
renderer: Add supercircular shadows and borders

config: Add rounding_power to default and example configs

rule: add `roundingpower` window rule
2025-01-05 18:38:49 +00:00
Sergio Miguéns
b0bae15499 master: make loop around optional when cycling (#8926) 2025-01-04 16:40:33 +00:00
vaxerski
e8317ae34d xwayland: don't define atoms on no_xwayland builds
fixes #8661
2025-01-04 17:35:11 +01:00
vaxerski
a25d228840 windows: minor initial workspace improvements
ref #8942
2025-01-04 17:21:02 +01:00
vaxerski
60f069d540 groupbar: fix missing ellipsize for text
fixes #8938
2025-01-04 00:25:01 +01:00
vaxerski
2e6e0e9278 core: guard workspace and monitor in moveWorkspaceToMonitor
ref #7822
2025-01-04 00:10:21 +01:00
davc0n
f69e72eca1 socket2: add focusedmonv2 event (#8921)
* socket2: add focusedmonv2 event

* socket2: remove workspace name from focusedmonv2
2025-01-03 22:43:48 +00:00
rszyma
b0cd9972e8 github: bug issue template improvements (#8894) 2025-01-03 12:47:34 +00:00
vaxerski
f3c49c1295 pass: ignore empty rectangles queued
ref #8935
2025-01-02 21:58:23 +01:00
vaxerski
4971725b69 config: avoid querying the fs every tick 2025-01-02 18:29:10 +01:00
vaxerski
1697171fc0 pass: allow removing all pass elements of a given type 2025-01-02 18:14:45 +01:00
vaxerski
81721b8aa8 groupbar: unify title rendering
moves the text renderer to the unified opengl impl
2025-01-02 17:50:55 +01:00
vaxerski
365caa49ff keybinds: attempt to wrap around if fallback is allowed in movefocus
fixes #8909
2025-01-02 17:36:02 +01:00
vaxerski
94381e5999 dnd/seat: avoid sending button events during a dnd op
fixes #8781
2025-01-02 15:58:28 +01:00
outfoxxed
42fd366046 protocols: fix alpha-modifier noncompliance (#8929)
Also fixes small issues with hyprland-surface opacity < 1.0 while
surface alpha = 1.0.
2025-01-02 11:53:57 +00:00
Raymond Bian
cbd2451570 renderer: fix CRendererHintsPassElement reset duplication issue (#8928) 2025-01-02 11:51:59 +00:00
outfoxxed
dde3e082c9 protocols: add hyprland_surface_v1 implementation (#8877) 2025-01-01 23:34:02 +00:00
vaxerski
9f3c9ac01a pass: mark crucial elements as undiscardable 2025-01-01 22:55:21 +01:00
vaxerski
7f177fa1cf layershell: return focus to a valid ls on close if possible 2025-01-01 17:28:04 +01:00
vaxerski
d64ac47202 pass: improve pass debugging 2025-01-01 17:26:05 +01:00
vaxerski
4e93b2def5 renderer: use a render pass for render modif in client render
fixes #8918
2025-01-01 14:11:21 +01:00
vaxerski
aff34089c4 monitor: avoid crashes on no good modes
fixes #8915
2025-01-01 13:47:16 +01:00
davc0n
1989b0049f hyprpm: add glaze dependency FetchContent fallback (#8899)
Use FetchContent to retrieve glaze dependency if not available with
find_package.
Allows to build hyprpm w/o installing glaze at system level (on some distros is
not available in official repositories).
2024-12-30 19:12:29 +00:00
vaxerski
9f933da1c5 renderer: fix oversized blur precalcs not blurring at all
fixes #8839
2024-12-30 20:00:45 +01:00
Ikalco
af301312d5 core: fix custom resolutions (#8897) 2024-12-30 18:45:42 +00:00
vaxerski
8c14c2a5f4 ctm: disable fade animation by default on nvidia
Fixes #8891, the nvidia driver dies when we spam CTM too much.
2024-12-30 11:58:35 +01:00
Ikalco
cb211d83f6 internal: few small monitor improvements (#8890)
* remove framesToSkip, its not used anymore

* only set drm format when needed after leaving direct scanout

* always set drm damage
2024-12-29 23:19:12 +00:00
khushal-solves
fde569db65 master: replace always_center_master with slave_count_for_center_master (#8871)
- Replaced boolean config parameter `always_center_master` to an integer value `slave_count_for_center_master` for more control on when to center master window.
- Also updated configuration parameter, type, description and related internal variable.

New description: **when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)**

This effects center orientation of master layout.
Now, user can control when the master window would be centered
2024-12-29 23:11:16 +00:00
Tuur Vanhoutte
5b37d53992 hyprpm: add an option to force reload all plugins (#8883) 2024-12-29 23:09:17 +00:00
vaxerski
deb077c346 ctm: add an internal fade animation to ctm transitions 2024-12-29 19:21:20 +01:00
vaxerski
3f40d6d936 pass: scale blur region in ::render 2024-12-29 12:56:58 +01:00
Mike Will
a364e80425 snap: give edge snapping precedence over corner snapping (#8873) 2024-12-29 11:18:14 +00:00
Clayton
2b01a5bcf6 xwayland: don't create an abstract unix domain socket on linux (#8874) 2024-12-28 14:07:42 +00:00
Ikalco
8d4c18d723 core: refactor/improve monitor mode selection (#8804) 2024-12-28 13:32:04 +00:00
vaxerski
c600e1aaff [gha] Nix: update inputs 2024-12-27 21:51:12 +00:00
Mike Will
cca0adf6a3 snap: revert #8659, use bounds checking instead of bit mask (#8872)
They seem like the same check, but a snap bit might not be
turned on as expected if we grab a different corner after
an edge has already been snapped.
2024-12-27 21:49:45 +00:00
vaxerski
534adad6b1 pass: scale blur regions properly 2024-12-27 17:49:05 +01:00
user111111111111111111111111111111111
775111b603 foreign-toplevel: update active on null window focus (#8860)
* update ForeignTopLevel on null window focus

* fix

* clang
2024-12-27 15:20:55 +00:00
khushal-solves
85632e7c33 internal: update window position/size after changing fullscreenstate (#8865)
This effects fullscreen group of floating windows.
2024-12-27 15:20:38 +00:00
Tuur Vanhoutte
43ca66779b hyprpm: use glaze to parse hyprctl plugin list (#8812)
* Use std::filesystem::path in hyprpm DataState to avoid concatenating strings with (folder + "/" + file)
* Added getPluginStates helper method in DataState
* Small clang-tidy improvements
2024-12-27 14:40:46 +00:00
vaxerski
e75e2cdac7 functionHooks: wait for hyprland pages before returning addr for trampo
ref #8845
2024-12-26 11:31:59 +01:00
vaxerski
2eaa4d80a0 debug: fix overlay not rendering 2024-12-26 00:16:42 +01:00
Avi Herman
dddb64dc35 internal: added reference to CTimer class in KeybindManager (#8836) 2024-12-25 19:09:45 +00:00
staz
1a3d17da91 debug: fix ISDEBUG checking (#8823) 2024-12-25 16:33:10 +01:00
vaxerski
2a24a61126 pass: improve blur region detection 2024-12-25 00:57:55 +01:00
vaxerski
2e2e2e2cad monitor: bring back old description behavior
this makes description match the beginning again. Fixes #8756
2024-12-23 21:17:38 +00:00
Mihai Fufezan
68a5842f06 Nix: fix TAG substitution 2024-12-23 20:07:12 +02:00
Mihai Fufezan
5f7ad767db flake.lock: update 2024-12-23 00:39:17 +02:00
Vaxry
a4a4fffffb renderer: allow plugins to know what window was rendered in post 2024-12-22 22:07:08 +00:00
Vaxry
1830296df3 debug: add debug:pass for debugging the render pass 2024-12-22 17:20:33 +00:00
Vaxry
e536b02248 Renderer: rewrite render scheduling (#8683)
This rewrites renderer scheduling. Occlusion is now unified in a new Pass type.
2024-12-22 17:12:09 +01:00
Vaxry
1cc1a46c2e core: fade in windows when they are brought from invisible workspaces 2024-12-22 16:04:10 +00:00
Vaxry
31422ae25d windowrules: add negative: prefix for negating a regex
fixes #8799
2024-12-21 23:07:34 +00:00
Vaxry
57921d7dbd windowrules: precompute regexes for window/layer rules 2024-12-21 19:21:50 +00:00
Aqa-Ib
8e8073c421 groups: honor group lock window rule (#8782) 2024-12-21 17:35:47 +01:00
Vaxry
52ee7a8748 data-device: fix edge case crash on null xwm
fixes #8787
2024-12-21 15:08:00 +00:00
Vaxry
71dc9f6128 pluginAPI: add register dispatcher v2 2024-12-19 20:23:32 +00:00
Aqa-Ib
fd67ee9ecd layout: apply group rules after window creation (#8779)
* apply group rules after window creation

* clang-format
2024-12-19 20:22:40 +01:00
Vaxry
bec18dc6f9 core: fix possible crash on null active workspace
fixes #7822
2024-12-19 19:11:15 +00:00
Ikalco
5b714f05f8 internal: Make static analysis more happy (#8767)
* make functions used in 1 file static

* fix invalid substr param -1

* give default initializer to borderGradier

* move RASSERT from printf to std::print
2024-12-19 02:56:01 +01:00
Aaron Blasko
7c43eed2c1 hyprpm: fix hyrpm sometimes returning 0 despite errors occuring (#8761)
* hyprpm: fix hyrpm sometimes returning 0 despite errors occuring

* there u go
2024-12-19 01:33:57 +01:00
Kamikadze
23e7d8f6a7 logging: fix Gpu info (#8764) 2024-12-19 00:08:57 +01:00
Jan Beich
df06cb4d71 core: Unbreak build on FreeBSD (#8762)
* CrashReporter: restore explicit environ(7) after 8bbeee1173

BSD systems don't declare environ(7) in any header like POSIX suggests.

src/signal-safe.cpp:12:23: error: use of undeclared identifier 'environ'
   12 |     for (char** var = environ; *var != nullptr; var++) {
      |                       ^

* fix

---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
2024-12-18 20:15:44 +01:00
Vaxry
0fec38fe72 xwayland: fix compile with no xwayland 2024-12-18 19:09:02 +00:00
Vaxry
80a4852266 xwm: fixup targets in selection requests
fixes #8719
2024-12-18 15:24:37 +00:00
Vaxry
4c4471c66d xwayland: fix dnd including xwayland
ref #8759
2024-12-18 15:14:37 +00:00
Vaxry
6378c8ed65 core: avoid activating toplevel-less surfaces
ref #8609
2024-12-18 15:01:12 +00:00
Ikalco
49e5f9c428 logging: get broader GPUINFO (#8753) 2024-12-18 15:56:53 +01:00
Khalid
5f1df55fcb hyprctl: Fix hyprctl batch JSON command (#8749) 2024-12-17 18:26:02 +01:00
Ikalco
e2c78c00e5 internal: remove dead code (#8748) 2024-12-17 17:31:27 +01:00
normaltaro
bd7092a9fe binds: cycle within group on single monitor if no window found in the argument direction. (#8714) 2024-12-17 17:12:38 +01:00
Mihai Fufezan
c7d9719910 flake.lock: update 2024-12-17 10:20:33 +02:00
Austin Horstman
7ae7920572 flake.lock: update (#8742) 2024-12-17 03:15:39 +01:00
Vaxry
4b6163aef3 windowrules: fixup duplicate rule enum tags
fixes #8746
2024-12-17 01:57:35 +00:00
Vaxry
788ae58897 version: bump to 0.46.0 2024-12-16 23:38:56 +00:00
Vaxry
bba2d9a197 versionkeeper: init version to 0.0.0 if no file is present 2024-12-16 23:37:13 +00:00
Austin Horstman
e340e9f431 nix/meson: add re2 dependency (#8738)
* nix/default: add re2 dependency

* meson: add re2
2024-12-16 20:22:36 +01:00
Vaxry
c2d14a2013 opengl: fixup missed /4 in oklab parsing 2024-12-16 18:53:21 +00:00
Vaxry
95cdedee04 windowrules: fix prop rules with boolean values 2024-12-16 18:44:19 +00:00
Vaxry
0706c1a1f7 layerrules: fix ignorezero not working
fixes #8737
2024-12-16 18:37:34 +00:00
Vaxry
aecf1abddd opengl: fixup invalid ogl uniform reads 2024-12-16 18:34:19 +00:00
Vaxry
b9f82e9968 animationmgr: fixup stack-use-after-return 2024-12-16 18:31:07 +00:00
Vaxry
e06b520427 core: Move regex from stdlib to re2 (#8736)
Moves the regex handling from stdlib to re2
2024-12-16 19:21:44 +01:00
Vaxry
dab50b3ef3 core: Optimize window/layer rule application and scanning (#8735)
Optimizes window and layer rule parsing and later usage.
2024-12-16 19:05:24 +01:00
Mihai Fufezan
eaac5c7cbd flake.lock: update 2024-12-16 18:06:00 +02:00
Vaxry
a5234f26e4 core: drop using deques in favor of vectors
No point in most of these.
2024-12-16 15:58:19 +00:00
Vaxry
de3ad245dc input: add warp_back_after_non_mouse_input
adds cursor:warp_back_after_non_mouse_input

fixes #8675
2024-12-15 23:54:14 +00:00
Vaxry
db24964877 xwayland: Support cross DnD from Wayland (#8708)
Adds support for drag-and-drop from Wayland clients to XWayland ones
2024-12-15 00:37:17 +01:00
Vaxry
9f7a96b997 core/data: Use pointer focus for DnD operations (#8707)
fixes #7737
2024-12-14 16:19:56 +01:00
Vaxry
3cba4ba44e hyprctl: avoid crash on null pwuid
fixes #8693
2024-12-13 22:36:42 +00:00
Vaxry
8237627f3a compositor: fix monitor arrangement with mixed auto directions
fixes #8518
2024-12-13 22:31:30 +00:00
Vaxry
35e134e570 hyprctl: add an inhibitingIdle field to windows
fixes #4322
2024-12-13 21:30:24 +00:00
normaltaro
452a7e6905 dispatchers: Add an option to prioritize focus change within groups with movefocus (#8601)
* modified movefocus dispatcher to prioritize focus change within groups

* pass clang-format check

* `movefocus` cycling groups set optional to config bool `movefocus_cycles_groupfirst`

* Update ConfigDescriptions.hpp
2024-12-13 21:28:57 +01:00
Mike Will
61a51bb4ef snap: bias reserved area when its size is greater than zero (#8694) 2024-12-13 17:34:04 +01:00
Vaxry
aefaeedf5e data-device: fixup wrong box expansion
oopsie 7c03e9d376 was wrong
2024-12-12 12:16:59 +00:00
Vaxry
cef5e6dd7c layersurface: use lastMonitor and not monitor from cursor for new ls
fixes #8622
2024-12-11 22:31:38 +00:00
Vaxry
7c03e9d376 core/data-device: expand damage region to fix minor px errors
fixes #7656
2024-12-11 22:30:17 +00:00
Mike Will
df956a0f6f windowrules: add rules for mouse and touchpad scroll factors (#8655) 2024-12-11 17:27:49 +00:00
rooot
33f271c29a hyprpm: target installed instead of running version (#8634)
---------

Signed-off-by: rooot <hey@rooot.gay>
2024-12-11 17:49:09 +01:00
Vaxry
e892310953 workspace: update hasFullscreenWindow in updateWindows 2024-12-11 16:03:04 +00:00
Pavel Belyavsky
4d05677e8d config: add 'force' option for 'cursor:warp_on_change_workspace' (#8681)
* config: add 'force' option for 'cursor:warp_on_change_workspace'

* manager: throw the expression into the function arguments

* config: fix description of `cursor:warp_on_change_workspace`
2024-12-10 21:55:05 +01:00
Alexander Iliev
c16044a5c8 core: Fix workspace selector parsing (#8687)
Search for the closing bracket when parsing a workspace selector.
This is needed when the `m[desc:<monitor description>]` selector
is used, as the monitor description always contains spaces.
2024-12-10 21:54:51 +01:00
littleblack111
d94d8b4ab2 windows: allow replacing existing fullscreen (#8566)
* feat: `binds:allow_replace_fullscreen`

this allows replacement of fullscreen window(i.e. fullscreening another
window when one is already fullscreened on the workspace)

this is used when a floating window that is spawned on top of a
fullscreen wanted to be fullscreened

* clang-format

* remove: config for `allow_replace_fullscreen`
2024-12-10 21:09:47 +01:00
Vaxry
bb5c3f2702 core/output: don't send enter too aggresively
sometimes this might be like 1px and send enter to the wrong output

fixes #8654
2024-12-10 01:40:35 +00:00
Vaxry
0a27af8cd1 crashreporter: avoid clang warning 2024-12-08 18:52:39 +00:00
Mihai Fufezan
c106f454c1 CI/Nix: temporarily disable cross build 2024-12-07 22:01:32 +02:00
Vaxry
875b598a33 github: mention PR guidelines in the template 2024-12-07 18:01:19 +00:00
Vaxry
8bbeee1173 core: Add clang-tidy (#8664)
This adds a .clang-tidy file for us.

It's not a strict requirement to be compliant, but I tuned it to be alright.
2024-12-07 18:51:18 +01:00
Vaxry
b1e5cc66bd core: Add support for hyprqtutils' update screen (#8651)
* Nix: add hyprland-qtutils to PATH

* flake.lock: update

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-12-06 15:45:02 +01:00
Mike Will
5ff02902ee snap: use the bit mask to check if snapping occurred (#8659) 2024-12-06 15:25:14 +01:00
Vaxry
cccca7c02e renderer: drop requesting OUT_FENCE_PTR 2024-12-06 14:15:46 +00:00
Mihai Fufezan
10a4365f7d Nix: create TAG info from version 2024-12-06 10:16:18 +02:00
Vaxry
a7a6eedc21 core: move version include to hyprctl 2024-12-05 19:35:50 +00:00
Vaxry
888bdf4e23 hyprctl: add directScanout to hyprctl monitors 2024-12-05 19:33:54 +00:00
Vaxry
ceef4fb3a5 core: feeling a bit quirky today. 2024-12-05 03:36:50 +00:00
Vaxry
22f7d6f142 core: add a few festive splashes
adds two new 'special' splash types for xmas and new years. Activated based on local time.
2024-12-05 01:59:36 +00:00
Mike Will
f9e4998a6d snap: check which corner is being grabbed for monitor snapping (#8637) 2024-12-04 19:12:04 +01:00
Vaxry
3c617ce33c internal: fixup some missed updateColorsOk() calls 2024-12-03 22:58:30 +00:00
vaxerski
f6ac755cf7 cleanup: Revert use doLater instead of adding idle event handlers (#8624)
This reverts commit 6d7544458d.
2024-12-03 21:15:25 +00:00
Vaxry
320144ae72 core: move colorspace handling to oklab (#8635)
* Meson: add hyprgraphics

* Nix: add hyprgraphics

* CI/setup_base: get hyprgraphics-git

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-12-03 18:58:24 +00:00
Vaxry
92186898c0 version: add link versions for other utils (#8619)
---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-12-02 16:31:22 +00:00
UjinT34
10a9fec7fc master: make center ignore reserved areas (#8625) 2024-12-02 15:32:32 +00:00
Ikalco
6d7544458d cleanup: use doLater instead of adding idle event handlers (#8624) 2024-12-01 17:14:35 +00:00
Vaxry
d26439a0fe nix: update flake 2024-11-30 17:42:40 +00:00
littleblack111
ef6b0c81c9 cleanup: remove leftover var in ThreadManager.cpp (#8611) 2024-11-30 16:40:23 +00:00
Vaxry
8f83d29f00 renderer: restore discard mode after IME render pass
ref #8555
2024-11-28 23:51:59 +00:00
Daringcuteseal
22bf2853e6 hyprpm: fix incomplete unmet dependencies message 2024-11-28 17:48:46 +02:00
nyx
5963970be5 descriptions: change allow_pin_fullscreen value to false (#8592) 2024-11-28 15:25:24 +00:00
Vaxry
8b51eeb7ae core: fix compilation outside stdlibc++
fixes #8603
2024-11-28 14:31:43 +00:00
Andre Toerien
5329298b52 sessionLock: don't send motion events on every surface commit (#8584) 2024-11-27 19:59:00 +00:00
Agent00Ming
e9a7fb8f91 renderer: fix incorrect early return (#8590)
Co-authored-by: Agent_00Ming <agent00ming9366@gmail.com>
2024-11-27 14:17:45 +00:00
Ikalco
1fb720b62a seat: fix double scrolling in some applications (#8583) 2024-11-26 13:52:43 +00:00
Vaxry
2687788236 hyprctl: verify runtime dir exists in instances()
ref #8579
2024-11-25 14:42:11 +00:00
Vladimir-csp
0ddb952d7a hyprland-uwsm.desktop: Just reference plain entry (#8553) 2024-11-25 16:18:50 +02:00
Nabil Otsmane
1930a95000 shm: fix shm fd size check before creating or resizing shm_pool (#8572)
* protocols: fix shm fd size check before creating or resizing shm_pool

* added static to function
2024-11-25 00:50:35 +00:00
Vaxry
cc38e7e18f config: don't overwrite errors in gradients 2024-11-24 14:53:36 +00:00
Vaxry
55ec8bd512 config: throw an error explicitly when parsing colors in gradients
ref #8552
2024-11-24 02:46:28 +00:00
Mike Will
54f57797e9 snap: account for position of multiple monitors (#8543) 2024-11-23 14:36:28 +00:00
littleblack111
65f66dcf0d binds: add option to allow fullscreening a pinned window (#8526) 2024-11-23 14:32:13 +00:00
Ryan
451d7a41fc renderer: add option to blur IME popups (#8521) 2024-11-23 14:29:29 +00:00
Vaxry
00d6261cc0 hyprpm: move temp files to XDG_RUNTIME_DIR
avoid /tmp, it's cringe
2024-11-23 14:18:13 +00:00
Vaxry
745a82ce8a core: workspace-related function cleanup / refactor
CCompositor is massive, and has a lot of functions that could be better optimized if in CWorkspace
2024-11-22 16:01:02 +00:00
Vaxry
a847bc67b1 monitor: fix default focus when switching to a fs workspace 2024-11-22 15:22:35 +00:00
may
b100344595 keybinds: actually suppress internal keybinds instead of passing them along (#8538) 2024-11-22 02:31:42 +00:00
Tom Englund
943b3d467b bezier: optimize setup of bezier curves (#8528)
avoid reallocations by resizing and copy the pVec into the resized
m_dPoints, reduce the amount of calculations in baking to only do it
once per iteration instead of twice. precompute in getYforT and getXforT
return early in getYForPoint if x is equal or below 0. and use const
references where we can.

these changes we are now down to an average of "time to bake: 2.50µs."
on my machine compared to before average of "time to bake: 11.15µs"
2024-11-22 01:47:51 +00:00
vaxerski
940f7aa990 renderer: fixup blur optimization considitons
fixes #8531
2024-11-20 11:02:21 +00:00
Vaxry
e5fa017172 internal: fix some misused configStringToInt conversions
fixes #8523
2024-11-20 10:32:50 +00:00
Vaxry
c4eda46d0e xdg-shell: even more robust layout min/max size
although I don't think any apps use this, but better safe than sorry
2024-11-19 22:07:25 +00:00
Vaxry
aa067a4cf1 xdg-shell: don't report invalid min/max sizes on unset
fixes #8522
2024-11-19 21:40:16 +00:00
Vaxry
67cee43006 internal: minor cleanups for color results 2024-11-19 01:16:11 +00:00
Vaxry
47a1650c48 miscfunctions: move configStringToInt to std::expected 2024-11-18 23:53:38 +00:00
Vaxry
936dfedbad keybinds: move to managed pointers 2024-11-18 19:56:26 +00:00
MightyPlaza
6113f4da7f keybinds: allow repeating multiple binds (#8290) 2024-11-18 19:48:13 +00:00
Maximilian Seidler
cc923ad031 config: update the configStringToInt implementation (#8476)
Copied from hyprlang and removed std::expected.
2024-11-18 19:45:22 +00:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко)
df9ff44899 Fix example config name in auto-generated cfg header (#8509)
Previously, it was suggesting to find `hypr.conf` in the `examples/` folder which doesn't exist. This patch fixed that to point to the existing file.

Additionally, the change updates `HYPR` to `HYPRLAND` in the same header.
2024-11-18 17:54:18 +02:00
Aqa-Ib
97493511f9 internal: fix changeWindowZOrder reordering incorrectly (#8494) 2024-11-18 14:44:15 +00:00
Vaxry
505c1f8f4a miscfunctions: fix cross build 2024-11-18 14:35:09 +00:00
johannes hanika
6744bb57c6 constraints: don't warp pointer position on release (#8491)
this was annoying for nuklear properties/ui slider elements that grab
the pointer via GLFW_CURSOR_DISABLE to allow more range and finer control.
upon mouse release, the pointer is reset to the middle of the window
without this patch, making long mouse movements necessary to go back
to the original position for readjustments. fwiw the new behaviour
is consistent with x11 and weston.
2024-11-18 14:02:34 +00:00
Vaxry
2259a88551 miscfunctions: add missing include 2024-11-18 13:59:57 +00:00
Vaxry
1ba050d603 shell: propagate new machanism from hyprctl to miscfunctions 2024-11-17 21:58:00 +00:00
Vaxry
e8717a4fce shell: don't use fgrep, prefer grep -F 2024-11-17 21:57:04 +00:00
Alessio Molinari
9b03307653 hooks: add pre connected/disconnected monitor events (#8503) 2024-11-17 19:34:03 +00:00
Vaxry
b735295d2b windows/xdg: minor cleanup of min/max size calculations
fixes #8495
2024-11-17 19:31:54 +00:00
Vaxry
8d5cdedbd3 hyprpm: fix format crash
ref #8487
2024-11-17 16:46:49 +00:00
Vaxry
4f591e807a renderer: simplify blur enabling logic 2024-11-17 16:42:30 +00:00
Vaxry
fb91c2550f renderer: don't render unmapped popups
fixes #8485
2024-11-17 16:16:54 +00:00
Alexandre Acebedo
0ddbd1c3a4 renderer: add lockdead_screen_delay (#8467) 2024-11-17 15:58:18 +00:00
Ruslan
af83c82513 hyprctl: add json output on hyprctl -j plugins list (#8480)
---------

Co-authored-by: Руслан Новокшонов <r.novokshonov@vk.team>
2024-11-16 23:18:30 +00:00
staz
9d37b1b073 workspacerules: Do not check 'on-created-empty' if using a workspace windowrule (#8486) 2024-11-16 23:07:33 +00:00
vaxerski
cf18eca86d [gha] Nix: update inputs 2024-11-16 23:06:35 +00:00
Vaxry
ec1e6be003 core: guard pmonitor in focuswindow
may be null

fixes #8483
2024-11-16 23:05:02 +00:00
sslater11
83be2480c4 workspace: fix missing name via focusworkspaceoncurrentmonitor (#8484) 2024-11-16 16:39:58 +00:00
littleblack111
7affc34ab4 bind: new long press option (#8302)
---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
2024-11-15 23:21:59 +00:00
Vaxry
098e491a43 protocols: mark primarySelection as not privileged
fixes #8479
2024-11-15 00:47:34 +00:00
UjinT34
967fe76a60 drm: enable explit out fence in AQ (#8431) 2024-11-15 00:45:13 +00:00
Tom Englund
940ed3d525 xcursors: store themes in a std:set to order it (#8474)
using a unordered_set means its store based on a hash_value meaning
currently it can end up loading inherited themes before the actual theme
itself depending on the hash of the theme name used, reason for using
set at all over vector is to keep unique members and not foreverever
looping broken inherit themeing.
2024-11-14 20:38:16 +00:00
Vaxry
6f7280a690 descriptions: add use_cpu_buffer 2024-11-14 20:20:51 +00:00
Vaxry
20031cea92 pointer: add drm dumb buffers for cursors (#8399)
---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-11-14 20:15:51 +00:00
Mihai Fufezan
3fb47372b7 flake.lock: update 2024-11-13 21:34:52 +02:00
Vaxry
bb160cfe37 makefile: add stub to discourage direct make 2024-11-12 15:26:25 +00:00
SoSeDiK
a29cfa7843 logging: Add some context to config error logs (#8326) 2024-11-12 00:53:55 +00:00
nnra
f5fa84554f config: Changed the default value of decoration:blur:ignore_opacity to true (#8418)
This change is made in order to deliver the blur look majority of people
expect by default.
2024-11-11 15:49:35 +00:00
Mihai Fufezan
b88e4a1a9a Nix: disable uwsm desktop file installation
Will be enabled in the NixOS module.
2024-11-11 16:52:42 +02:00
Mihai Fufezan
ff411658e8 Lock uwsm desktop file behind feature flag
The file in the repo cannot be used in NixOS due to missing full paths,
and the fact that `uwsm` does not have access to `PATH` to find the
listed binaries. Might be useful in other situations as well.
2024-11-11 16:52:42 +02:00
Mihai Fufezan
943c7d18cc meson: autodetect systemd 2024-11-11 16:52:42 +02:00
Izmyname
ccfae82ad1 rename hyprland-systemd.desktop and remove hyprland-session.service 2024-11-11 16:52:42 +02:00
izmyname
430b5c302a systemd: hyprland-systemd.desktop -> hyprland-uwsm.desktop
Remove hyprland-session.service.
2024-11-11 16:52:42 +02:00
Vaxry
8fa4cfb7df keybinds: don't animate fullscreen size/pos changes coming in
when fullscreen, don't animate the pos/size when switching to another fullscreen window, as they can look weird and distracting.

Ideally we would do it differently but it's not really possible to do well without reading minds
2024-11-11 13:55:37 +00:00
dawsers
a551f85b91 renderer: scaled surfaces could have zero area (#8423) 2024-11-11 13:48:50 +00:00
JManch
07052a515b pointer: map devices across all outputs by default (#8352) 2024-11-11 13:45:33 +00:00
WavyEbuilder
1fa0cd7a75 debug: clean up fetching of the contents of /proc/device-tree (#8413) 2024-11-11 13:44:41 +00:00
Vaxry
c10739e6e3 core: fixup execAndGet
fixes #8410
2024-11-10 22:53:11 +00:00
WavyEbuilder
9e628067fc debug: clean up opening of files in HyprCtl (#8401)
`std::ifstream` is more suited than `execAndGet` here.
2024-11-10 15:54:15 +00:00
Vaxry
99b01c5d12 hyprpm: fix format 2024-11-10 15:54:00 +00:00
Vaxry
a8ff3a452c core: move to os/Process from hyprutils
nix bump too
2024-11-09 17:14:25 +00:00
Vaxry
dca75db127 defaultConfig: fixup smart gaps rules 2024-11-09 16:56:43 +00:00
358 changed files with 15405 additions and 8144 deletions

101
.clang-tidy Normal file
View File

@@ -0,0 +1,101 @@
WarningsAsErrors: '*'
HeaderFilterRegex: '.*\.hpp'
FormatStyle: file
Checks: >
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-forward-declararion-namespace,
-bugprone-forward-declararion-namespace,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-branch-clone,
-bugprone-assignment-in-if-condition,
concurrency-*,
-concurrency-mt-unsafe,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-special-member-functions,
-cppcoreguidelines-explicit-virtual-functions,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-reinterpret-cast,
google-global-names-in-headers,
-google-readability-casting,
google-runtime-operator,
misc-*,
-misc-unused-parameters,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-include-cleaner,
-misc-use-anonymous-namespace,
-misc-const-correctness,
modernize-*,
-modernize-return-braced-init-list,
-modernize-use-trailing-return-type,
-modernize-use-using,
-modernize-use-override,
-modernize-avoid-c-arrays,
-modernize-macro-to-enum,
-modernize-loop-convert,
-modernize-use-nodiscard,
-modernize-pass-by-value,
-modernize-use-auto,
performance-*,
-performance-avoid-endl,
-performance-unnecessary-value-param,
portability-std-allocator-const,
readability-*,
-readability-function-cognitive-complexity,
-readability-function-size,
-readability-identifier-length,
-readability-magic-numbers,
-readability-uppercase-literal-suffix,
-readability-braces-around-statements,
-readability-redundant-access-specifiers,
-readability-else-after-return,
-readability-container-data-pointer,
-readability-implicit-bool-conversion,
-readability-avoid-nested-conditional-operator,
-readability-redundant-member-init,
-readability-redundant-string-init,
-readability-avoid-const-params-in-decls,
-readability-named-parameter,
-readability-convert-member-functions-to-static,
-readability-qualified-auto,
-readability-make-member-function-const,
-readability-isolate-declaration,
-readability-inconsistent-declaration-parameter-name,
-clang-diagnostic-error,
CheckOptions:
performance-for-range-copy.WarnOnAllAutoCopies: true
performance-inefficient-string-concatenation.StrictMode: true
readability-braces-around-statements.ShortStatementLines: 0
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.ClassIgnoredRegexp: I.*
readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.EnumPrefix: e
readability-identifier-naming.EnumConstantCase: UPPER_CASE
readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.NamespaceCase: CamelCase
readability-identifier-naming.NamespacePrefix: N
readability-identifier-naming.StructPrefix: S
readability-identifier-naming.StructCase: CamelCase

View File

@@ -5,10 +5,10 @@ body:
- type: checkboxes
attributes:
label: Already reported ? *
description: Before opening a new bug report, please take a moment to search through the current open and closed issues to check if it already exists.
description: Before opening a new bug report, please take a moment to search through the current open issues. If the same bug is already reported, don't open new issue - instead go upvote/comment on an existing one.
options:
- label: I have searched the existing open and closed issues.
required: true
- label: I have searched the existing open and closed issues.
required: true
- type: dropdown
id: type
@@ -19,27 +19,28 @@ body:
**BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue
multiple: true
options:
- "Yes"
- "No"
- "Definitely a regression - something broke after update (requires bisect)"
- "Probably not a regression / I don't remember it happening before"
- "Not a regression - it's bug regarding new feature"
- "Not a regression - it's an old bug"
- "I don't know, I started using Hyprland only recently"
validations:
required: true
- type: textarea
id: ver
attributes:
label: System Info and Version
label: System Info and Hyprland Version
description: |
Paste the output of `hyprctl systeminfo -c` here. If you can't
Paste the output of `hyprctl systeminfo` here. If you can't
launch Hyprland, paste the output of `Hyprland --systeminfo`.
If `Hyprland --systeminfo` errors out (added in 0.44.0), find
and paste the Hyprland version manually.
value: "<details>
<summary>System/Version info</summary>
```sh
<Paste the output of the command here>
```
<Paste the output of the command here, without removing any formatting around this>
```
@@ -61,15 +62,56 @@ body:
attributes:
label: How to reproduce
description: "How can someone else reproduce the issue?"
placeholder: |
1. ...
2. ...
3. ...
validations:
required: true
- type: markdown
attributes:
value: |
## Additional info section
In the section below you will be asked to upload some files.
When including text files (such as logs or config), please **always ATTACH** them, and not paste them directly.
This is important to avoid clutter, spam, and make the issues more readable.
Thanks for your understanding.
# The main reason to disallow pasting directly or in a dropdown, is to not clutter
# the issue with unnecessary keywords, making the github issue search useless.
- type: checkboxes
attributes:
label: Attach not paste
options:
- label: I understand that all text files must be *attached*, and not pasted directly. If not respected, this issue will likely get closed as spam
required: true
- type: markdown
attributes:
value: >-
Please be sure to upload the following files below if they are relevant to the issue:
- Logs can be found in $XDG_RUNTIME_DIR/hypr (sort by date to grab the latest)
- Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
- Hyprland config files - `hyprctl systeminfo -c > /tmp/hyprland_config_dump.txt` use this command to dump full configuration to a single file.
- type: checkboxes
attributes:
label: Checklist of files to include below
options:
- label: Hyprland config - `hyprctl systeminfo -c` (always include)
- label: Crash report (always include in case of crash)
- label: Video (always include in case of a visual bug)
- label: Logs (might contain useful info such as errors)
- type: textarea
id: logs
attributes:
label: Crash reports, logs, images, videos
label: Additional info & File uploads
description: |
Anything that can help. Please always ATTACH and not paste them.
Logs can be found in $XDG_RUNTIME_DIR/hypr
Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
Tip: You can attach files by clicking this area to highlight it and then dragging files in.

View File

@@ -33,7 +33,10 @@ runs:
libfontenc \
libglvnd \
libinput \
libjxl \
libliftoff \
libspng \
libwebp \
libxcursor \
libxcvt \
libxfont2 \
@@ -58,7 +61,17 @@ runs:
xcb-util \
xcb-util-image \
libzip \
librsvg
librsvg \
re2
- name: Get glaze
shell: bash
run: |
git clone https://github.com/stephenberry/glaze.git
cd glaze
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build
- name: Get hyprwayland-scanner-git
shell: bash
@@ -69,6 +82,11 @@ runs:
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build
- name: Get hyprgraphics-git
shell: bash
run: |
git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build
- name: Get hyprutils-git
shell: bash
run: |

View File

@@ -1,3 +1,9 @@
<!--
BEFORE you submit your PR, please check out the PR guidelines
on our wiki: https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/
-->
#### Describe your PR, what does it fix/add?

View File

@@ -107,6 +107,7 @@ jobs:
run: make release
clang-format:
permissions: read-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Code Style (Arch)"
runs-on: ubuntu-latest

48
.github/workflows/clang-format.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: clang-format
on: pull_request_target
jobs:
clang-format:
permissions: write-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Code Style (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: meson setup build -Ddefault_library=static
- name: clang-format check
run: ninja -C build clang-format-check
- name: clang-format apply
if: ${{ failure() && github.event_name == 'pull_request' }}
run: ninja -C build clang-format
- name: Create patch
if: ${{ failure() && github.event_name == 'pull_request' }}
run: |
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch
echo '<details>' >> clang-format.patch
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
echo >> clang-format.patch
echo '```diff' >> clang-format.patch
git diff >> clang-format.patch
echo '```' >> clang-format.patch
echo >> clang-format.patch
echo '</details>' >> clang-format.patch
- name: Comment patch
if: ${{ failure() && github.event_name == 'pull_request' }}
uses: mshick/add-pr-comment@v2
with:
message-path: |
clang-format.patch

View File

@@ -12,7 +12,8 @@ jobs:
matrix:
package:
- hyprland
- hyprland-cross
# - hyprland-cross # cross compiling fails due to qt
# failure chain: hyprland-qtutils -> qt6.qtsvg -> qt6.qtbase -> psqlodbc & qt6.qttranslations
- xdg-desktop-portal-hyprland
runs-on: ubuntu-latest

View File

@@ -101,11 +101,17 @@ else()
endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.1)
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.2)
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.4.0)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
add_compile_definitions(HYPRGRAPHICS_VERSION="${hyprgraphics_dep_VERSION}")
pkg_check_modules(
deps
@@ -124,9 +130,7 @@ pkg_check_modules(
libinput
gbm
gio-2.0
hyprlang>=0.3.2
hyprcursor>=0.1.7
hyprutils>=0.2.3)
re2)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@@ -193,6 +197,12 @@ if(NOT HAS_TIMERFD AND epoll_FOUND)
target_link_libraries(Hyprland PkgConfig::epoll)
endif()
check_include_file("sys/inotify.h" HAS_INOTIFY)
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
if(NOT HAS_INOTIFY AND inotify_FOUND)
target_link_libraries(Hyprland PkgConfig::inotify)
endif()
if(LEGACY_RENDERER)
message(STATUS "Using the legacy GLES2 renderer!")
add_compile_definitions(LEGACY_RENDERER)
@@ -222,16 +232,15 @@ if(NO_SYSTEMD)
else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
add_compile_definitions(USES_SYSTEMD)
configure_file(systemd/hyprland-session.service.in
systemd/hyprland-session.service @ONLY)
# session file -systemd
install(FILES ${CMAKE_SOURCE_DIR}/systemd/hyprland-systemd.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
# install systemd service
install(FILES ${CMAKE_BINARY_DIR}/systemd/hyprland-session.service
DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/user)
# session file -uwsm
if(NO_UWSM)
message(STATUS "UWSM support is disabled...")
else()
message(STATUS "UWSM support is enabled (NO_UWSM not defined)...")
install(FILES ${CMAKE_SOURCE_DIR}/systemd/hyprland-uwsm.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
endif()
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
@@ -245,7 +254,15 @@ target_precompile_headers(Hyprland PRIVATE
message(STATUS "Setting link libraries")
target_link_libraries(Hyprland rt PkgConfig::aquamarine_dep PkgConfig::deps)
target_link_libraries(
Hyprland
rt
PkgConfig::aquamarine_dep
PkgConfig::hyprlang_dep
PkgConfig::hyprutils_dep
PkgConfig::hyprcursor_dep
PkgConfig::hyprgraphics_dep
PkgConfig::deps)
if(udis_dep_FOUND)
target_link_libraries(Hyprland PkgConfig::udis_dep)
else()
@@ -287,7 +304,7 @@ endfunction()
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.0)
if(hyprland_protocols_dep_FOUND)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
@@ -313,8 +330,12 @@ protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolnew("protocols" "xx-color-management-v4" true)
protocolnew("protocols" "frog-color-management-v1" true)
protocolnew("protocols" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
@@ -352,7 +373,13 @@ protocolwayland()
# tools
add_subdirectory(hyprctl)
add_subdirectory(hyprpm)
if(NO_HYPRPM)
message(STATUS "hyprpm is disabled")
else()
add_subdirectory(hyprpm)
message(STATUS "hyprpm is enabled (NO_HYPRPM not defined)")
endif()
# binary and symlink
install(TARGETS Hyprland)
@@ -363,7 +390,6 @@ install(
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
)")
# session file
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)

View File

@@ -1,5 +1,8 @@
PREFIX = /usr/local
stub:
@echo "Do not run $(MAKE) directly without any arguments. Please refer to the wiki on how to compile Hyprland."
legacyrenderer:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`

View File

@@ -1 +1 @@
0.45.0
0.47.0

View File

@@ -81,6 +81,7 @@ general {
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
rounding = 10
rounding_power = 2
# Change transparency of focused and unfocused windows
active_opacity = 1.0
@@ -136,13 +137,10 @@ animations {
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[t1], gapsout:0, gapsin:0
# workspace = w[tg1], gapsout:0, gapsin:0
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[t1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[t1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tg1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tg1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]

View File

@@ -22,5 +22,6 @@
}
]
},
]
}

162
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1730968822,
"narHash": "sha256-NocDjINsh6ismkhb0Xr6xPRksmhuB2WGf8ZmXMhxu7Y=",
"lastModified": 1738183445,
"narHash": "sha256-C1He3N1SA8D2u+TSlldbA9wiYwDvXI4GxX3zKaeD7qU=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "a49bc3583ff223f426cb3526fdaa4bcaa247ec14",
"rev": "48a000cf35dd10bfeb231152735aebbe875f4b74",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
]
},
"locked": {
"lastModified": 1728669738,
"narHash": "sha256-EDNAU9AYcx8OupUzbTbWE1d3HYdeG0wO6Msg3iL1muk=",
"lastModified": 1738178255,
"narHash": "sha256-+D6Nu2ewXbMTFzx/Q4jDOo+LAOUPr0cxQJg5k33daIE=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "0264e698149fcb857a66a53018157b41f8d97bb0",
"rev": "dcadd3398abe146d60c67e0d9ee6e27b301cae82",
"type": "github"
},
"original": {
@@ -92,6 +92,32 @@
"type": "github"
}
},
"hyprgraphics": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1738018829,
"narHash": "sha256-5Ol5iahMlELx3lWuChyZsqqLk6sP6aqaJCJFw92OZGo=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "12cd7034e441a5ebfdef1a090c0788413b4a635b",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprgraphics",
"type": "github"
}
},
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
@@ -102,11 +128,11 @@
]
},
"locked": {
"lastModified": 1728345020,
"narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
"lastModified": 1737556638,
"narHash": "sha256-laKgI3mr2qz6tas/q3tuGPxMdsGhBi/w+HO+hO2f1AY=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "a7c183800e74f337753de186522b9017a07a8cee",
"rev": "4c75dd5c015c8a0e5a34c6d02a018a650f57feb5",
"type": "github"
},
"original": {
@@ -115,6 +141,67 @@
"type": "github"
}
},
"hyprland-qt-support": {
"inputs": {
"hyprlang": [
"hyprland-qtutils",
"hyprlang"
],
"nixpkgs": [
"hyprland-qtutils",
"nixpkgs"
],
"systems": [
"hyprland-qtutils",
"systems"
]
},
"locked": {
"lastModified": 1737634706,
"narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=",
"owner": "hyprwm",
"repo": "hyprland-qt-support",
"rev": "8810df502cdee755993cb803eba7b23f189db795",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-qt-support",
"type": "github"
}
},
"hyprland-qtutils": {
"inputs": {
"hyprland-qt-support": "hyprland-qt-support",
"hyprlang": [
"hyprlang"
],
"hyprutils": [
"hyprland-qtutils",
"hyprlang",
"hyprutils"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1737981711,
"narHash": "sha256-lh6cL5D8nPplB3WovCQjLUZ7k7MViiBrMlpkfm4R7/c=",
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"rev": "96bf0677fa9cd13508294e3d4559dfbbc8beff73",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"type": "github"
}
},
"hyprlang": {
"inputs": {
"hyprutils": [
@@ -128,11 +215,11 @@
]
},
"locked": {
"lastModified": 1728168612,
"narHash": "sha256-AnB1KfiXINmuiW7BALYrKqcjCnsLZPifhb/7BsfPbns=",
"lastModified": 1737634606,
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "f054f2e44d6a0b74607a6bc0f52dba337a3db38e",
"rev": "f41271d35cc0f370d300413d756c2677f386af9d",
"type": "github"
},
"original": {
@@ -151,11 +238,11 @@
]
},
"locked": {
"lastModified": 1730968903,
"narHash": "sha256-zFvzLXcSm0Ia4XI1SE4FQ9KE63hlGrRWhLtwMolWuR8=",
"lastModified": 1737978343,
"narHash": "sha256-TfFS0HCEJh63Kahrkp1h9hVDMdLU8a37Zz+IFucxyfA=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "3ce0cde8709cdacbfba471f8e828433b58a561e9",
"rev": "6a8bc9d2a4451df12f5179dc0b1d2d46518a90ab",
"type": "github"
},
"original": {
@@ -174,11 +261,11 @@
]
},
"locked": {
"lastModified": 1726874836,
"narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
"lastModified": 1735493474,
"narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
"rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b",
"type": "github"
},
"original": {
@@ -189,11 +276,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1730785428,
"narHash": "sha256-Zwl8YgTVJTEum+L+0zVAWvXAGbWAuXHax3KzuejaDyo=",
"lastModified": 1737885589,
"narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4aa36568d413aca0ea84a1684d2d46f55dbabad7",
"rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8",
"type": "github"
},
"original": {
@@ -203,37 +290,20 @@
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
]
},
"locked": {
"lastModified": 1730814269,
"narHash": "sha256-fWPHyhYE6xvMI1eGY3pwBTq85wcy1YXqdzTZF+06nOg=",
"lastModified": 1737465171,
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "d70155fdc00df4628446352fc58adc640cd705c2",
"rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17",
"type": "github"
},
"original": {
@@ -246,7 +316,9 @@
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols",
"hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
@@ -293,11 +365,11 @@
]
},
"locked": {
"lastModified": 1730743262,
"narHash": "sha256-iTLqj3lU8kFehPm5tXpctzkD274t/k1nwSSq3qCWXeg=",
"lastModified": 1737634991,
"narHash": "sha256-dBAnb7Kbnier30cA7AgxVSxxARmxKZ1vHZT33THSIr8=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "09b23cef06fe248e61cec8862c04b9bcb62f4b6d",
"rev": "e09dfe2726c8008f983e45a0aa1a3b7416aaeb8a",
"type": "github"
},
"original": {

View File

@@ -22,12 +22,26 @@
inputs.hyprlang.follows = "hyprlang";
};
hyprgraphics = {
url = "github:hyprwm/hyprgraphics";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprutils.follows = "hyprutils";
};
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprland-qtutils = {
url = "github:hyprwm/hyprland-qtutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprlang.follows = "hyprlang";
};
hyprlang = {
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
@@ -116,13 +130,11 @@
inherit
(pkgsFor.${system})
# hyprland-packages
hyprland
hyprland-debug
hyprland-legacy-renderer
hyprland-unwrapped
# hyprland-extras
xdg-desktop-portal-hyprland
;
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;

View File

@@ -5,7 +5,7 @@ project(
DESCRIPTION "Control utility for Hyprland"
)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.1.1)
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2)
add_executable(hyprctl "main.cpp")

View File

@@ -1,3 +1,5 @@
#include <re2/re2.h>
#include <cctype>
#include <netdb.h>
#include <netinet/in.h>
@@ -19,15 +21,10 @@
#include <string>
#include <print>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <filesystem>
#include <cstdarg>
#include <regex>
#include <sys/socket.h>
#include <hyprutils/string/String.hpp>
#include <cstring>
using namespace Hyprutils::String;
#include "Strings.hpp"
@@ -52,11 +49,17 @@ void log(const std::string& str) {
std::println("{}", str);
}
static int getUID() {
const auto UID = getuid();
const auto PWUID = getpwuid(UID);
return PWUID ? PWUID->pw_uid : UID;
}
std::string getRuntimeDir() {
const auto XDG = getenv("XDG_RUNTIME_DIR");
if (!XDG) {
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
const std::string USERID = std::to_string(getUID());
return "/run/user/" + USERID + "/hypr";
}
@@ -66,6 +69,11 @@ std::string getRuntimeDir() {
std::vector<SInstanceData> instances() {
std::vector<SInstanceData> result;
try {
if (!std::filesystem::exists(getRuntimeDir()))
return {};
} catch (std::exception& e) { return {}; }
for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
continue;
@@ -163,7 +171,7 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
return 2;
}
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
const std::string USERID = std::to_string(getUID());
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
@@ -233,7 +241,7 @@ int requestHyprpaper(std::string arg) {
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
const std::string USERID = std::to_string(getUID());
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.hyprpaper.sock";
@@ -271,10 +279,11 @@ int requestHyprpaper(std::string arg) {
}
void batchRequest(std::string arg, bool json) {
std::string commands = arg.substr(arg.find_first_of(" ") + 1);
std::string commands = arg.substr(arg.find_first_of(' ') + 1);
if (json) {
commands = "j/" + std::regex_replace(commands, std::regex(";\\s*"), ";j/");
RE2::GlobalReplace(&commands, ";\\s*", ";j/");
commands.insert(0, "j/");
}
std::string rq = "[[BATCH]]" + commands;
@@ -311,11 +320,11 @@ void instancesRequest(bool json) {
log(result + "\n");
}
std::deque<std::string> splitArgs(int argc, char** argv) {
std::deque<std::string> result;
std::vector<std::string> splitArgs(int argc, char** argv) {
std::vector<std::string> result;
for (auto i = 1 /* skip the executable */; i < argc; ++i)
result.push_back(std::string(argv[i]));
result.emplace_back(argv[i]);
return result;
}

View File

@@ -3,6 +3,7 @@ executable(
'main.cpp',
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
dependency('re2', required: true)
],
install: true,
)

View File

@@ -9,11 +9,25 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.1.1)
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.2.4)
find_package(glaze QUIET)
if (NOT glaze_FOUND)
set(GLAZE_VERSION v4.2.3)
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
include(FetchContent)
FetchContent_Declare(
glaze
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
GIT_TAG ${GLAZE_VERSION}
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(glaze)
endif()
add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::deps)
target_link_libraries(hyprpm PUBLIC PkgConfig::hyprpm_deps glaze::glaze)
# binary
install(TARGETS hyprpm)

View File

@@ -14,7 +14,7 @@ hyprpm [<FLAGS>]... <ARGUMENT>
| (list) "List all installed plugins"
| (enable <PLUGINS>) "Load a plugin"
| (disable <PLUGINS>) "Unload a plugin"
| (reload) "Reload all plugins"
| (reload) "Reload plugins to match the enabled/disabled state. Use -f to force reload."
;
<PLUGINS> ::= {{{ hyprpm list | awk '/Plugin/{print $4}' }}};

View File

@@ -1,11 +1,10 @@
#include "DataState.hpp"
#include <toml++/toml.hpp>
#include <print>
#include <filesystem>
#include <fstream>
#include "PluginManager.hpp"
std::string DataState::getDataStatePath() {
std::filesystem::path DataState::getDataStatePath() {
const auto HOME = getenv("HOME");
if (!HOME) {
std::println(stderr, "DataState: no $HOME");
@@ -16,12 +15,29 @@ std::string DataState::getDataStatePath() {
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";
return std::filesystem::path{XDG_DATA_HOME} / "hyprpm";
return std::filesystem::path{HOME} / ".local/share/hyprpm";
}
std::string DataState::getHeadersPath() {
return getDataStatePath() + "/headersRoot";
return getDataStatePath() / "headersRoot";
}
std::vector<std::filesystem::path> DataState::getPluginStates() {
ensureStateStoreExists();
std::vector<std::filesystem::path> states;
for (const auto& entry : std::filesystem::directory_iterator(getDataStatePath())) {
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
const auto stateFile = entry.path() / "state.toml";
if (!std::filesystem::exists(stateFile))
continue;
states.emplace_back(stateFile);
}
return states;
}
void DataState::ensureStateStoreExists() {
@@ -37,7 +53,7 @@ void DataState::ensureStateStoreExists() {
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
ensureStateStoreExists();
const auto PATH = getDataStatePath() + "/" + repo.name;
const auto PATH = getDataStatePath() / repo.name;
std::filesystem::create_directories(PATH);
// clang-format off
@@ -50,19 +66,21 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
}}
};
for (auto const& p : repo.plugins) {
const auto filename = p.name + ".so";
// copy .so to the good place
if (std::filesystem::exists(p.filename))
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");
std::filesystem::copy_file(p.filename, PATH / filename);
DATA.emplace(p.name, toml::table{
{"filename", p.name + ".so"},
{"filename", filename},
{"enabled", p.enabled},
{"failed", p.failed}
});
}
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
@@ -72,17 +90,10 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName)
return true;
@@ -96,29 +107,22 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName) {
// unload the plugins!!
for (const auto& file : std::filesystem::directory_iterator(entry.path())) {
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
if (!file.path().string().ends_with(".so"))
continue;
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
}
std::filesystem::remove_all(entry.path());
std::filesystem::remove_all(stateFile.parent_path());
return;
}
}
@@ -139,7 +143,7 @@ void DataState::updateGlobalState(const SGlobalState& state) {
};
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
@@ -147,12 +151,12 @@ void DataState::updateGlobalState(const SGlobalState& state) {
SGlobalState DataState::getGlobalState() {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
const auto stateFile = getDataStatePath() / "state.toml";
if (!std::filesystem::exists(PATH + "/state.toml"))
if (!std::filesystem::exists(stateFile))
return SGlobalState{};
auto DATA = toml::parse_file(PATH + "/state.toml");
auto DATA = toml::parse_file(stateFile.c_str());
SGlobalState state;
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
@@ -167,15 +171,8 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
const auto PATH = getDataStatePath();
std::vector<SPluginRepository> repos;
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
@@ -210,15 +207,8 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
for (const auto& [key, val] : STATE) {
if (key == "repository")
continue;
@@ -231,10 +221,11 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
if (FAILED)
return false;
(*STATE[key].as_table()).insert_or_assign("enabled", enabled);
auto modifiedState = STATE;
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc);
state << STATE;
std::ofstream state(stateFile, std::ios::trunc);
state << modifiedState;
state.close();
return true;

View File

@@ -1,4 +1,5 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include "Plugin.hpp"
@@ -9,14 +10,15 @@ struct SGlobalState {
};
namespace DataState {
std::string getDataStatePath();
std::string getHeadersPath();
void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo);
void removePluginRepo(const std::string& urlOrName);
bool pluginRepoExists(const std::string& urlOrName);
void updateGlobalState(const SGlobalState& state);
SGlobalState getGlobalState();
bool setPluginEnabled(const std::string& name, bool enabled);
std::vector<SPluginRepository> getAllRepositories();
std::filesystem::path getDataStatePath();
std::string getHeadersPath();
std::vector<std::filesystem::path> getPluginStates();
void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo);
void removePluginRepo(const std::string& urlOrName);
bool pluginRepoExists(const std::string& urlOrName);
void updateGlobalState(const SGlobalState& state);
SGlobalState getGlobalState();
bool setPluginEnabled(const std::string& name, bool enabled);
std::vector<SPluginRepository> getAllRepositories();
};

View File

@@ -7,10 +7,8 @@
#include <cstdio>
#include <iostream>
#include <array>
#include <filesystem>
#include <print>
#include <thread>
#include <fstream>
#include <algorithm>
#include <format>
@@ -21,37 +19,56 @@
#include <unistd.h>
#include <toml++/toml.hpp>
#include <glaze/glaze.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/Process.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
static std::string execAndGet(std::string cmd) {
cmd += " 2>&1";
std::array<char, 128> buffer;
std::string result;
using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd.c_str(), "r"), static_cast<PcloseType>(pclose));
if (!pipe)
return "";
result.reserve(buffer.size());
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
CProcess proc("/bin/sh", {"-c", cmd});
if (!proc.runSync())
return "error";
return proc.stdOut();
}
SHyprlandVersion CPluginManager::getHyprlandVersion() {
static SHyprlandVersion ver;
static bool once = false;
static std::string getTempRoot() {
static auto ENV = getenv("XDG_RUNTIME_DIR");
if (!ENV) {
std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n";
exit(1);
}
if (once)
return ver;
const auto STR = ENV + std::string{"/hyprpm/"};
once = true;
const auto HLVERCALL = execAndGet("hyprctl version");
return STR;
}
SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
static bool onceRunning = false;
static bool onceInstalled = false;
static SHyprlandVersion verRunning;
static SHyprlandVersion verInstalled;
if (onceRunning && running)
return verRunning;
if (onceInstalled && !running)
return verInstalled;
if (running)
onceRunning = true;
else
onceInstalled = true;
const auto HLVERCALL = running ? execAndGet("hyprctl version") : execAndGet("Hyprland --version");
if (m_bVerbose)
std::println("{}", verboseString("version returned: {}", HLVERCALL));
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
if (!HLVERCALL.contains("Tag:")) {
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
@@ -65,13 +82,13 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
hldate = hldate.substr(0, hldate.find("\n"));
hldate = hldate.substr(0, hldate.find('\n'));
std::string hlcommits;
if (HLVERCALL.contains("commits:")) {
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
hlcommits = hlcommits.substr(0, hlcommits.find(" "));
hlcommits = hlcommits.substr(0, hlcommits.find(' '));
}
int commits = 0;
@@ -82,12 +99,18 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
if (m_bVerbose)
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
if (running)
verRunning = ver;
else
verInstalled = ver;
return ver;
}
bool CPluginManager::createSafeDirectory(const std::string& path) {
if (path.empty() || !path.starts_with("/tmp"))
if (path.empty() || !path.starts_with(getTempRoot()))
return false;
if (std::filesystem::exists(path))
@@ -106,7 +129,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HLVER = getHyprlandVersion();
if (!hasDeps()) {
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
return false;
}
@@ -145,17 +168,17 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
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);
} else if (!std::filesystem::is_directory("/tmp/hyprpm")) {
if (!std::filesystem::exists(getTempRoot())) {
std::filesystem::create_directory(getTempRoot());
std::filesystem::permissions(getTempRoot(), std::filesystem::perms::owner_all, std::filesystem::perm_options::replace);
} else if (!std::filesystem::is_directory(getTempRoot())) {
std::println(stderr, "\n{}", failureString("Could not prepare working dir for hyprpm"));
return false;
}
const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
m_szWorkingPluginDirectory = getTempRoot() + USERNAME;
if (!createSafeDirectory(m_szWorkingPluginDirectory)) {
std::println(stderr, "\n{}", failureString("Could not prepare working dir for repo"));
@@ -164,7 +187,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(infoString("Cloning {}", url));
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " " + USERNAME);
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
@@ -275,7 +298,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
}
if (m_bVerbose)
std::println("{}", verboseString("shell returned: " + out));
std::println("{}", verboseString("shell returned: {}", out));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
progress.printMessageAbove(failureString("Plugin {} failed to build.\n"
@@ -347,14 +370,14 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
}
eHeadersErrors CPluginManager::headersValid() {
const auto HLVER = getHyprlandVersion();
const auto HLVER = getHyprlandVersion(false);
if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc"))
return HEADERS_MISSING;
// find headers commit
const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str());
auto headers = execAndGet(cmd);
if (!headers.contains("-I/"))
return HEADERS_MISSING;
@@ -409,16 +432,16 @@ bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion();
const auto HLVER = getHyprlandVersion(false);
if (!hasDeps()) {
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
return false;
}
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(getTempRoot())) {
std::filesystem::create_directory(getTempRoot());
std::filesystem::permissions(getTempRoot(), std::filesystem::perms::owner_all, std::filesystem::perm_options::replace);
}
if (!force && headersValid() == HEADERS_OK) {
@@ -433,7 +456,7 @@ bool CPluginManager::updateHeaders(bool force) {
progress.print();
const std::string USERNAME = getpwuid(getuid())->pw_name;
const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME;
const auto WORKINGDIR = getTempRoot() + "hyprland-" + USERNAME;
if (!createSafeDirectory(WORKINGDIR)) {
std::println("\n{}", failureString("Could not prepare working dir for hl"));
@@ -451,12 +474,12 @@ bool CPluginManager::updateHeaders(bool force) {
if (m_bVerbose && bShallow)
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
std::string ret =
execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""));
std::string ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-{}{}", getTempRoot(), USERNAME,
(bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
if (!std::filesystem::exists(WORKINGDIR)) {
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME);
ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/hyprland hyprland-{}", getTempRoot(), USERNAME));
}
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
@@ -571,7 +594,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
return true;
}
const auto HLVER = getHyprlandVersion();
const auto HLVER = getHyprlandVersion(false);
CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
@@ -580,7 +603,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.print();
const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
m_szWorkingPluginDirectory = getTempRoot() + USERNAME;
for (auto const& repo : REPOS) {
bool update = forceUpdateAll;
@@ -595,7 +618,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(infoString("Cloning {}", repo.url));
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " " + USERNAME);
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
@@ -757,7 +780,7 @@ bool CPluginManager::disablePlugin(const std::string& name) {
return ret;
}
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload) {
if (headersValid() != HEADERS_OK) {
std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
return LOADSTATE_HEADERS_OUTDATED;
@@ -766,35 +789,28 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) {
std::println(stderr, "PluginManager: no $HOME or HIS");
std::println(stderr, "PluginManager: no $HOME or $HYPRLAND_INSTANCE_SIGNATURE");
return LOADSTATE_FAIL;
}
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
const auto HYPRPMPATH = DataState::getDataStatePath();
auto pluginLines = execAndGet("hyprctl plugins list | grep Plugin");
const auto json = glz::read_json<glz::json_t::array_t>(execAndGet("hyprctl plugins list -j"));
if (!json) {
std::println(stderr, "PluginManager: couldn't parse hyprctl output");
return LOADSTATE_FAIL;
}
std::vector<std::string> loadedPlugins;
for (const auto& plugin : json.value()) {
if (!plugin.is_object() || !plugin.contains("name")) {
std::println(stderr, "PluginManager: couldn't parse plugin object");
return LOADSTATE_FAIL;
}
loadedPlugins.emplace_back(plugin["name"].get<std::string>());
}
std::println("{}", successString("Ensuring plugin load state"));
// iterate line by line
while (!pluginLines.empty()) {
auto plLine = pluginLines.substr(0, pluginLines.find('\n'));
if (pluginLines.find('\n') != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
else
pluginLines = "";
if (plLine.back() != ':')
continue;
plLine = plLine.substr(7);
plLine = plLine.substr(0, plLine.find(" by "));
loadedPlugins.push_back(plLine);
}
// get state
const auto REPOS = DataState::getAllRepositories();
@@ -820,12 +836,20 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
return "";
};
// unload disabled plugins
// if any of the loadUnloadPlugin calls return false, this is true
// bcs that means the header version doesn't match the running version
// (and Hyprland needs to restart)
bool hyprlandVersionMismatch = false;
// unload disabled plugins (or all if forceReload is true)
for (auto const& p : loadedPlugins) {
if (!enabled(p)) {
if (forceReload || !enabled(p)) {
// unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::println("{}", successString("Unloaded {}", p));
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p) / (p + ".so"), false)) {
std::println("{}", infoString("{} will be unloaded after restarting Hyprland", p));
hyprlandVersionMismatch = true;
} else
std::println("{}", successString("Unloaded {}", p));
}
}
@@ -835,20 +859,31 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (!p.enabled)
continue;
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
if (!forceReload && std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
continue;
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
std::println("{}", successString("Loaded {}", p.name));
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) {
std::println("{}", infoString("{} will be loaded after restarting Hyprland", p.name));
hyprlandVersionMismatch = true;
} else
std::println("{}", successString("Loaded {}", p.name));
}
}
std::println("{}", successString("Plugin load state ensured"));
return LOADSTATE_OK;
return hyprlandVersionMismatch ? LOADSTATE_HYPRLAND_UPDATED : LOADSTATE_OK;
}
bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
auto state = DataState::getGlobalState();
auto HLVER = getHyprlandVersion(true);
if (state.headersHashCompiled != HLVER.hash) {
std::println("{}", infoString("Running Hyprland version differs from plugin state, please restart Hyprland."));
return false;
}
if (load)
execAndGet("hyprctl plugin load " + path);
else

View File

@@ -27,7 +27,8 @@ enum ePluginLoadStateReturn {
LOADSTATE_OK = 0,
LOADSTATE_FAIL,
LOADSTATE_PARTIAL_FAIL,
LOADSTATE_HEADERS_OUTDATED
LOADSTATE_HEADERS_OUTDATED,
LOADSTATE_HYPRLAND_UPDATED
};
struct SHyprlandVersion {
@@ -50,10 +51,10 @@ class CPluginManager {
bool enablePlugin(const std::string& name);
bool disablePlugin(const std::string& name);
ePluginLoadStateReturn ensurePluginsLoadState();
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
bool loadUnloadPlugin(const std::string& path, bool load);
SHyprlandVersion getHyprlandVersion();
SHyprlandVersion getHyprlandVersion(bool running = true);
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);

View File

@@ -73,7 +73,7 @@ int main(int argc, char** argv, char** envp) {
if (command.empty()) {
std::println(stderr, "{}", HELP);
return 0;
return 1;
}
g_pPluginManager = std::make_unique<CPluginManager>();
@@ -103,7 +103,7 @@ int main(int argc, char** argv, char** envp) {
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
bool headers = g_pPluginManager->updateHeaders(force);
if (headers) {
const auto HLVER = g_pPluginManager->getHyprlandVersion();
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
auto GLOBALSTATE = DataState::getGlobalState();
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
@@ -114,6 +114,9 @@ int main(int argc, char** argv, char** envp) {
auto ret2 = g_pPluginManager->ensurePluginsLoadState();
if (ret2 == LOADSTATE_HYPRLAND_UPDATED)
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Updated plugins, but Hyprland was updated. Please restart Hyprland.");
if (ret2 != LOADSTATE_OK)
return 1;
} else if (notify)
@@ -130,6 +133,10 @@ int main(int argc, char** argv, char** envp) {
}
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret == LOADSTATE_HYPRLAND_UPDATED)
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland.");
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "disable") {
@@ -147,7 +154,7 @@ int main(int argc, char** argv, char** envp) {
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState();
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK && notify) {
switch (ret) {
@@ -158,6 +165,7 @@ int main(int argc, char** argv, char** envp) {
break;
default: break;
}
return 1;
} else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}

View File

@@ -8,6 +8,7 @@ executable(
dependency('hyprutils', version: '>= 0.1.1'),
dependency('threads'),
dependency('tomlplusplus'),
dependency('glaze', method: 'cmake'),
],
install: true,
)

View File

@@ -31,8 +31,16 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
aquamarine = dependency('aquamarine', version: '>=0.4.2')
aquamarine = dependency('aquamarine', version: '>=0.4.5')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp')
add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp')
add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp')
add_project_arguments(['-DHYPRUTILS_VERSION="@0@"'.format(hyprutils.version())], language: 'cpp')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
@@ -50,10 +58,17 @@ endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
inotify_dep = dependency('libinotify', required: false) # inotify on BSDs
re2 = dependency('re2', required: true)
# Handle options
if get_option('systemd').enabled()
systemd = dependency('systemd')
systemd_option = get_option('systemd')
systemd = dependency('systemd', required: systemd_option)
systemd_option.enable_auto_if(systemd.found())
if (systemd_option.enabled())
message('Enabling systemd integration')
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
subdir('systemd')
endif
@@ -87,11 +102,14 @@ endif
subdir('protocols')
subdir('src')
subdir('hyprctl')
subdir('hyprpm/src')
subdir('assets')
subdir('example')
subdir('docs')
if get_option('hyprpm').enabled()
subdir('hyprpm/src')
endif
# Generate hyprland.pc
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')

View File

@@ -1,4 +1,6 @@
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
option('uwsm', type: 'feature', value: 'enabled', description: 'Enable uwsm integration (only if systemd is enabled)')
option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')
option('hyprpm', type: 'feature', value: 'enabled', description: 'Enable hyprpm')
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')

View File

@@ -5,14 +5,18 @@
pkg-config,
pkgconf,
makeWrapper,
cmake,
meson,
ninja,
aquamarine,
binutils,
cairo,
git,
glaze,
hyprcursor,
hyprgraphics,
hyprland-protocols,
hyprland-qtutils,
hyprlang,
hyprutils,
hyprwayland-scanner,
@@ -25,6 +29,7 @@
mesa,
pango,
pciutils,
re2,
systemd,
tomlplusplus,
udis86-hyprland,
@@ -47,12 +52,12 @@
nvidiaPatches ? false,
hidpiXWayland ? false,
}: let
inherit (builtins) baseNameOf foldl';
inherit (builtins) baseNameOf foldl' readFile;
inherit (lib.asserts) assertMsg;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) flatten concatLists optional optionals;
inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable;
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable trim;
adapters = flatten [
stdenvAdapters.useMoldLinker
@@ -64,7 +69,7 @@ in
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
customStdenv.mkDerivation {
customStdenv.mkDerivation (finalAttrs: {
pname = "hyprland${optionalString debug "-debug"}";
inherit version;
@@ -88,6 +93,7 @@ in
DATE = date;
DIRTY = optionalString (commit == "") "dirty";
HASH = commit;
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
depsBuildBuild = [
pkg-config
@@ -98,6 +104,7 @@ in
makeWrapper
meson
ninja
cmake # needed for glaze
pkg-config
];
@@ -112,7 +119,9 @@ in
aquamarine
cairo
git
glaze
hyprcursor
hyprgraphics
hyprland-protocols
hyprlang
hyprutils
@@ -124,6 +133,7 @@ in
mesa
pango
pciutils
re2
tomlplusplus
udis86-hyprland
wayland
@@ -152,7 +162,8 @@ in
(mapAttrsToList mesonEnable {
"xwayland" = enableXWayland;
"legacy_renderer" = legacyRenderer;
"systemd" = withSystemd;
"uwsm" = false;
"hyprpm" = false;
})
(mapAttrsToList mesonBool {
"b_pch" = false;
@@ -165,6 +176,7 @@ in
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${makeBinPath [
binutils
hyprland-qtutils
pciutils
pkgconf
]}
@@ -180,4 +192,4 @@ in
platforms = lib.platforms.linux;
mainProgram = "Hyprland";
};
}
})

View File

@@ -7,15 +7,206 @@ inputs: {
inherit (pkgs.stdenv.hostPlatform) system;
cfg = config.programs.hyprland;
package = inputs.self.packages.${system}.hyprland;
portalPackage = inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
hyprland = cfg.finalPackage;
};
# basically 1:1 taken from https://github.com/nix-community/home-manager/blob/master/modules/services/window-managers/hyprland.nix
toHyprconf = {
attrs,
indentLevel ? 0,
importantPrefixes ? ["$"],
}: let
inherit
(lib)
all
concatMapStringsSep
concatStrings
concatStringsSep
filterAttrs
foldl
generators
hasPrefix
isAttrs
isList
mapAttrsToList
replicate
;
initialIndent = concatStrings (replicate indentLevel " ");
toHyprconf' = indent: attrs: let
sections =
filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs;
mkSection = n: attrs:
if lib.isList attrs
then (concatMapStringsSep "\n" (a: mkSection n a) attrs)
else ''
${indent}${n} {
${toHyprconf' " ${indent}" attrs}${indent}}
'';
mkFields = generators.toKeyValue {
listsAsDuplicateKeys = true;
inherit indent;
};
allFields =
filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v)))
attrs;
isImportantField = n: _:
foldl (acc: prev:
if hasPrefix prev n
then true
else acc)
false
importantPrefixes;
importantFields = filterAttrs isImportantField allFields;
fields =
builtins.removeAttrs allFields
(mapAttrsToList (n: _: n) importantFields);
in
mkFields importantFields
+ concatStringsSep "\n" (mapAttrsToList mkSection sections)
+ mkFields fields;
in
toHyprconf' initialIndent attrs;
in {
config = {
options = {
programs.hyprland = {
package = lib.mkDefault package;
portalPackage = lib.mkDefault portalPackage;
plugins = lib.mkOption {
type = with lib.types; listOf (either package path);
default = [];
description = ''
List of Hyprland plugins to use. Can either be packages or
absolute plugin paths.
'';
};
settings = lib.mkOption {
type = with lib.types; let
valueType =
nullOr (oneOf [
bool
int
float
str
path
(attrsOf valueType)
(listOf valueType)
])
// {
description = "Hyprland configuration value";
};
in
valueType;
default = {};
description = ''
Hyprland configuration written in Nix. Entries with the same key
should be written as lists. Variables' and colors' names should be
quoted. See <https://wiki.hyprland.org> for more examples.
::: {.note}
Use the [](#programs.hyprland.plugins) option to
declare plugins.
:::
'';
example = lib.literalExpression ''
{
decoration = {
shadow_offset = "0 5";
"col.shadow" = "rgba(00000099)";
};
"$mod" = "SUPER";
bindm = [
# mouse movements
"$mod, mouse:272, movewindow"
"$mod, mouse:273, resizewindow"
"$mod ALT, mouse:272, resizewindow"
];
}
'';
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
example = ''
# window resize
bind = $mod, S, submap, resize
submap = resize
binde = , right, resizeactive, 10 0
binde = , left, resizeactive, -10 0
binde = , up, resizeactive, 0 -10
binde = , down, resizeactive, 0 10
bind = , escape, submap, reset
submap = reset
'';
description = ''
Extra configuration lines to add to `/etc/xdg/hypr/hyprland.conf`.
'';
};
sourceFirst =
lib.mkEnableOption ''
putting source entries at the top of the configuration
''
// {
default = true;
};
importantPrefixes = lib.mkOption {
type = with lib.types; listOf str;
default = ["$" "bezier" "name"] ++ lib.optionals cfg.sourceFirst ["source"];
example = ["$" "bezier"];
description = ''
List of prefix of attributes to source at the top of the config.
'';
};
};
};
config = lib.mkMerge [
{
programs.hyprland = {
package = lib.mkDefault inputs.self.packages.${system}.hyprland;
portalPackage = lib.mkDefault (inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
hyprland = cfg.finalPackage;
});
};
}
(lib.mkIf cfg.enable {
environment.etc."xdg/hypr/hyprland.conf" = let
shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != [];
pluginsToHyprconf = plugins:
toHyprconf {
attrs = {
plugin = let
mkEntry = entry:
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in
map mkEntry cfg.plugins;
};
inherit (cfg) importantPrefixes;
};
in
lib.mkIf shouldGenerate {
text =
lib.optionalString (cfg.plugins != [])
(pluginsToHyprconf cfg.plugins)
+ lib.optionalString (cfg.settings != {})
(toHyprconf {
attrs = cfg.settings;
inherit (cfg) importantPrefixes;
})
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
};
})
];
}

View File

@@ -22,7 +22,9 @@ in {
# Dependencies
inputs.aquamarine.overlays.default
inputs.hyprcursor.overlays.default
inputs.hyprgraphics.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprland-qtutils.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default

View File

@@ -0,0 +1,366 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="frog_color_management_v1">
<copyright>
Copyright © 2023 Joshua Ashton for Valve Software
Copyright © 2023 Xaver Hugl
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="experimental color management protocol">
The aim of this color management extension is to get HDR games working quickly,
and have an easy way to test implementations in the wild before the upstream
protocol is ready to be merged.
For that purpose it's intentionally limited and cut down and does not serve
all uses cases.
</description>
<interface name="frog_color_management_factory_v1" version="1">
<description summary="color management factory">
The color management factory singleton creates color managed surface objects.
</description>
<request name="destroy" type="destructor"></request>
<request name="get_color_managed_surface">
<description summary="create color management interface for surface">
</description>
<arg name="surface" type="object" interface="wl_surface"
summary="target surface" />
<arg name="callback" type="new_id" interface="frog_color_managed_surface"
summary="new color managed surface object" />
</request>
</interface>
<interface name="frog_color_managed_surface" version="1">
<description summary="color managed surface">
Interface for changing surface color management and HDR state.
An implementation must: support every part of the version
of the frog_color_managed_surface interface it exposes.
Including all known enums associated with a given version.
</description>
<request name="destroy" type="destructor">
<description summary="destroy color managed surface">
Destroying the color managed surface resets all known color
state for the surface back to 'undefined' implementation-specific
values.
</description>
</request>
<enum name="transfer_function">
<description summary="known transfer functions">
Extended information on the transfer functions described
here can be found in the Khronos Data Format specification:
https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
</description>
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling of the surface's transfer function." />
<entry name="srgb" value="1"
summary="specifies the sRGB non-linear EOTF. An implementation may: display this as Gamma 2.2 for the purposes of being consistent with content rendering across displays, rendering_intent and user expectations." />
<entry name="gamma_22" value="2" summary="specifies gamma 2.2 power curve as the EOTF" />
<entry name="st2084_pq" value="3"
summary="specifies the SMPTE ST2084 Perceptual Quantizer (PQ) EOTF" />
<entry name="scrgb_linear" value="4"
summary="specifies the scRGB (extended sRGB) linear EOTF. Note: Primaries outside the gamut triangle specified can be expressed with negative values for this transfer function." />
</enum>
<request name="set_known_transfer_function">
<description summary="sets a known transfer function for a surface" />
<arg name="transfer_function" type="uint" enum="transfer_function"
summary="transfer function for the surface" />
</request>
<enum name="primaries">
<description summary="known primaries" />
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling" />
<entry name="rec709" value="1" summary="specifies Rec.709/sRGB primaries with D65 white point" />
<entry name="rec2020" value="2"
summary="specifies Rec.2020/HDR10 primaries with D65 white point" />
</enum>
<request name="set_known_container_color_volume">
<description summary="sets the container color volume (primaries) for a surface" />
<arg name="primaries" type="uint" enum="primaries" summary="primaries for the surface" />
</request>
<enum name="render_intent">
<description summary="known render intents">
Extended information on render intents described
here can be found in ICC.1:2022:
https://www.color.org/specification/ICC.1-2022-05.pdf
</description>
<entry name="perceptual" value="0" summary="perceptual" />
</enum>
<request name="set_render_intent">
<description summary="sets the render intent for a surface">
NOTE: On a surface with "perceptual" (default) render intent, handling of the container's
color volume
is implementation-specific, and may differ between different transfer functions it is paired
with:
ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's
gamut
to be be more pleasing for the viewer.
Compared to scRGB Linear + 709 being treated faithfully as 709
(including utilizing negatives out of the 709 gamut triangle)
</description>
<arg name="render_intent" type="uint" enum="render_intent"
summary="render intent for the surface" />
</request>
<request name="set_hdr_metadata">
<description summary="set HDR metadata for a surface">
Forwards HDR metadata from the client to the compositor.
HDR Metadata Infoframe as per CTA 861.G spec.
Usage of this HDR metadata is implementation specific and
outside of the scope of this protocol.
</description>
<arg name="mastering_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Mastering Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Mastering Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Mastering Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Mastering Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Mastering Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Mastering Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_x" type="uint">
<description summary="white point x coordinate">
Mastering White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_y" type="uint">
<description summary="white point y coordinate">
Mastering White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_display_mastering_luminance" type="uint">
<description summary="max display mastering luminance">
Max Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_display_mastering_luminance" type="uint">
<description summary="min display mastering luminance">
Min Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_cll" type="uint">
<description summary="max content light level">
Max Content Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="max_fall" type="uint">
<description summary="max frame average light level">
Max Frame Average Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</request>
<event name="preferred_metadata">
<description summary="preferred metadata for a surface">
Current preferred metadata for a surface.
The application should use this information to tone-map its buffers
to this target before committing.
This metadata does not necessarily correspond to any physical output, but
rather what the compositor thinks would be best for a given surface.
</description>
<arg name="transfer_function" type="uint" enum="transfer_function">
<description summary="output's current transfer function">
Specifies a known transfer function that corresponds to the
output the surface is targeting.
</description>
</arg>
<arg name="output_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Output Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Output Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Output Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Output Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Output Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Output Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_x" type="uint">
<description summary="white point x coordinate">
Output White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_y" type="uint">
<description summary="white point y coordinate">
Output White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_luminance" type="uint">
<description summary="maximum luminance">
Max Output Luminance
The max luminance in nits that the output is capable of rendering in small areas.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_luminance" type="uint">
<description summary="minimum luminance">
Min Output Luminance
The min luminance that the output is capable of rendering.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_full_frame_luminance" type="uint">
<description summary="maximum full frame luminance">
Max Full Frame Luminance
The max luminance in nits that the output is capable of rendering for the
full frame sustained.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</event>
</interface>
</protocol>

View File

@@ -7,7 +7,7 @@ wayland_protos = dependency(
hyprland_protos = dependency(
'hyprland-protocols',
version: '>=0.4',
version: '>=0.6',
fallback: 'hyprland-protocols',
)
@@ -33,10 +33,14 @@ protocols = [
'wayland-drm.xml',
'wlr-data-control-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
'xx-color-management-v4.xml',
'frog-color-management-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-lock-notify-v1.xml',
wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,23 @@
#pragma once
#include <memory>
#include <deque>
#include <list>
#include <sys/resource.h>
#include "defines.hpp"
#include "debug/Log.hpp"
#include "events/Events.hpp"
#include "config/ConfigManager.hpp"
#include "managers/ThreadManager.hpp"
#include "managers/XWaylandManager.hpp"
#include "managers/input/InputManager.hpp"
#include "managers/LayoutManager.hpp"
#include "managers/KeybindManager.hpp"
#include "managers/AnimationManager.hpp"
#include "managers/EventManager.hpp"
#include "managers/ProtocolManager.hpp"
#include "managers/SessionLockManager.hpp"
#include "managers/HookSystemManager.hpp"
#include "debug/HyprDebugOverlay.hpp"
#include "debug/HyprNotificationOverlay.hpp"
#include "helpers/Monitor.hpp"
#include "desktop/Workspace.hpp"
#include "desktop/Window.hpp"
#include "render/Renderer.hpp"
#include "render/OpenGL.hpp"
#include "hyprerror/HyprError.hpp"
#include "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp"
#include "protocols/types/ColorManagement.hpp"
#include "helpers/memory/Memory.hpp"
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
class CWLSurfaceResource;
struct SWorkspaceRule;
enum eManagersInitStage {
enum eManagersInitStage : uint8_t {
STAGE_PRIORITY = 0,
STAGE_BASICINIT,
STAGE_LATE
@@ -43,7 +25,7 @@ enum eManagersInitStage {
class CCompositor {
public:
CCompositor();
CCompositor(bool onlyConfig = false);
~CCompositor();
wl_display* m_sWLDisplay;
@@ -90,10 +72,11 @@ class CCompositor {
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
PHLMONITORREF m_pUnsafeOutput; // fallback output for the unsafe state
bool m_bIsShuttingDown = false;
bool m_bFinalRequests = false;
bool m_bDesktopEnvSet = false;
bool m_bEnableXwayland = true;
bool m_bIsShuttingDown = false;
bool m_bFinalRequests = false;
bool m_bDesktopEnvSet = false;
bool m_bWantsXwayland = true;
bool m_bOnlyConfigVerification = false;
// ------------------------------------------------- //
@@ -115,34 +98,24 @@ class CCompositor {
PHLMONITOR getRealMonitorFromOutput(SP<Aquamarine::IOutput>);
PHLWINDOW getWindowFromSurface(SP<CWLSurfaceResource>);
PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE);
bool isWorkspaceVisibleNotCovered(PHLWORKSPACE);
PHLWORKSPACE getWorkspaceByID(const WORKSPACEID&);
PHLWORKSPACE getWorkspaceByName(const std::string&);
PHLWORKSPACE getWorkspaceByString(const std::string&);
void sanityCheckWorkspaces();
void updateWorkspaceWindowDecos(const WORKSPACEID&);
void updateWorkspaceWindowData(const WORKSPACEID&);
int getWindowsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroupsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
PHLWINDOW getUrgentWindow();
bool hasUrgentWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFirstWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getTopLeftWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFullscreenWindowOnWorkspace(const WORKSPACEID&);
bool isWindowActive(PHLWINDOW);
void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const MONITORID& monid);
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}, bool visible = false);
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}, bool visible = false);
WORKSPACEID getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
PHLMONITOR getMonitorInDirection(const char&);
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const WORKSPACEID& id);
void updateWindowAnimatedDecorationValues(PHLWINDOW);
MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
@@ -151,8 +124,7 @@ class CCompositor {
bool workspaceIDOutOfBounds(const WORKSPACEID&);
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state);
void changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state);
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW);
@@ -165,10 +137,8 @@ class CCompositor {
PHLLS getLayerSurfaceFromSurface(SP<CWLSurfaceResource>);
void closeWindow(PHLWINDOW);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const WORKSPACEID&);
PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "",
bool isEmpty = true); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const WORKSPACEID&, const std::string& name = "");
void setActiveMonitor(PHLMONITOR);
bool isWorkspaceSpecial(const WORKSPACEID&);
WORKSPACEID getNewSpecialID();
@@ -181,8 +151,11 @@ class CCompositor {
void setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, double scale);
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
void updateSuspendedStates();
PHLWINDOW windowForCPointer(CWindow*);
void onNewMonitor(SP<Aquamarine::IOutput> output);
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules);
SImageDescription getPreferredImageDescription();
bool shouldChangePreferredImageDescription();
std::string explicitConfigPath;
@@ -193,10 +166,11 @@ class CCompositor {
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
bool isWindowAvailableForCycle(PHLWINDOW pWindow, PHLWINDOW w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false);
uint64_t m_iHyprlandPID = 0;
wl_event_source* m_critSigSource = nullptr;
rlimit m_sOriginalNofile = {0};
};
inline std::unique_ptr<CCompositor> g_pCompositor;
inline UP<CCompositor> g_pCompositor;

View File

@@ -3,11 +3,11 @@
#include "helpers/math/Math.hpp"
#include <functional>
#include <any>
#include <string>
#include <algorithm>
#include <hyprutils/math/Box.hpp>
using namespace Hyprutils::Math;
enum eIcons {
enum eIcons : uint8_t {
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
@@ -17,7 +17,7 @@ enum eIcons {
ICON_NONE
};
enum eRenderStage {
enum eRenderStage : uint8_t {
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 */
@@ -29,7 +29,7 @@ enum eRenderStage {
RENDER_POST_WINDOW, /* After rendering a window (any pass) */
};
enum eInputType {
enum eInputType : uint8_t {
INPUT_TYPE_AXIS = 0,
INPUT_TYPE_BUTTON,
INPUT_TYPE_DRAG_START,
@@ -41,7 +41,7 @@ struct SCallbackInfo {
bool cancelled = false; /* on cancellable events, will cancel the event. */
};
enum eHyprCtlOutputFormat {
enum eHyprCtlOutputFormat : uint8_t {
FORMAT_NORMAL = 0,
FORMAT_JSON
};
@@ -52,6 +52,12 @@ struct SHyprCtlCommand {
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
};
struct SDispatchResult {
bool passEvent = false;
bool success = true;
std::string error;
};
typedef int64_t WINDOWID;
typedef int64_t MONITORID;
typedef int64_t WORKSPACEID;

View File

@@ -3,7 +3,7 @@
#include "../helpers/varlist/VarList.hpp"
#include <vector>
enum eConfigValueDataTypes {
enum eConfigValueDataTypes : int8_t {
CVD_TYPE_INVALID = -1,
CVD_TYPE_GRADIENT = 0,
CVD_TYPE_CSS_VALUE = 1
@@ -11,7 +11,7 @@ enum eConfigValueDataTypes {
class ICustomConfigValueData {
public:
virtual ~ICustomConfigValueData() = 0;
virtual ~ICustomConfigValueData() = default;
virtual eConfigValueDataTypes getDataType() = 0;
@@ -20,24 +20,40 @@ class ICustomConfigValueData {
class CGradientValueData : public ICustomConfigValueData {
public:
CGradientValueData() {};
CGradientValueData(CColor col) {
CGradientValueData() = default;
CGradientValueData(CHyprColor col) {
m_vColors.push_back(col);
updateColorsOk();
};
virtual ~CGradientValueData() {};
virtual ~CGradientValueData() = default;
virtual eConfigValueDataTypes getDataType() {
return CVD_TYPE_GRADIENT;
}
void reset(CColor col) {
void reset(CHyprColor col) {
m_vColors.clear();
m_vColors.emplace_back(col);
m_fAngle = 0;
updateColorsOk();
}
void updateColorsOk() {
m_vColorsOkLabA.clear();
for (auto& c : m_vColors) {
const auto OKLAB = c.asOkLab();
m_vColorsOkLabA.emplace_back(OKLAB.l);
m_vColorsOkLabA.emplace_back(OKLAB.a);
m_vColorsOkLabA.emplace_back(OKLAB.b);
m_vColorsOkLabA.emplace_back(c.a);
}
}
/* Vector containing the colors */
std::vector<CColor> m_vColors;
std::vector<CHyprColor> m_vColors;
/* Vector containing pure colors for shoving into opengl */
std::vector<float> m_vColorsOkLabA;
/* Float corresponding to the angle (rad) */
float m_fAngle = 0;

View File

@@ -139,6 +139,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{0, 0, 20},
},
SConfigOptionDescription{
.value = "decoration:rounding_power",
.description = "rouding power of corners (2 is a circle)",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{2, 2, 10},
},
SConfigOptionDescription{
.value = "decoration:active_opacity",
.description = "opacity of active windows. [0.0 - 1.0]",
@@ -268,7 +274,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.value = "blur:ignore_opacity",
.description = "make the blur layer ignore the opacity of the window",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "blur:new_optimizations",
@@ -331,6 +337,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
},
SConfigOptionDescription{
.value = "blur:input_methods",
.description = "whether to blur input methods (e.g. fcitx5)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "blur:input_methods_ignorealpha",
.description = "works like ignorealpha in layer rules. If pixel opacity is below set value, will not blur. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
},
/*
* animations:
@@ -620,16 +638,22 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "input:tablet:output",
.description = "the monitor to bind tablets. Empty means unbound..",
.description = "the monitor to bind tablets. Can be current or a monitor name. Leave empty to map across all monitors.",
.type = CONFIG_OPTION_STRING_SHORT,
.data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET?
},
SConfigOptionDescription{
.value = "input:tablet:region_position",
.description = "position of the mapped region in monitor layout.",
.description = "position of the mapped region in monitor layout relative to the top left corner of the bound monitor or all monitors.",
.type = CONFIG_OPTION_VECTOR,
.data = SConfigOptionDescription::SVectorData{{}, {-20000, -20000}, {20000, 20000}},
},
SConfigOptionDescription{
.value = "input:tablet:absolute_region_position",
.description = "whether to treat the region_position as an absolute position in monitor layout. Only applies when output is empty.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "input:tablet:region_size",
.description = "size of the mapped region. When this variable is set, tablet input will be mapped to the region. [0, 0] or invalid size means unset.",
@@ -1115,6 +1139,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "misc:disable_hyprland_qtutils_check",
.description = "disable the warning if hyprland-qtutils is missing",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "misc:lockdead_screen_delay",
.description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1000, 0, 5000},
},
/*
* binds:
@@ -1170,6 +1206,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "binds:movefocus_cycles_groupfirst",
.description = "If enabled, when in a grouped window, movefocus will cycle windows in the groups first, then at each ends of tabs, it'll move on to other windows/groups",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "binds:disable_keybind_grabbing",
.description = "If enabled, apps that request keybinds to be disabled (e.g. VMs) will not be able to do so.",
@@ -1182,6 +1224,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "binds:allow_pin_fullscreen",
.description = "Allows fullscreen to pinned windows, and restore their pinned status afterwards",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* xwayland:
@@ -1247,6 +1295,30 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "render:expand_undersized_textures",
.description = "Whether to expand textures that have not yet resized to be larger, or to just stretch them instead.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "render:xp_mode",
.description = "Disable back buffer and bottom layer rendering.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "render:ctm_animation",
.description = "Whether to enable a fade animation for CTM changes (hyprsunset). 2 means 'auto' (Yes on everything but Nvidia).",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
},
SConfigOptionDescription{
.value = "render:allow_early_buffer_release",
.description = "Allow early buffer release event. Fixes stuttering and missing frames for some apps. May cause graphical glitches and memory leaks in others",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
/*
* cursor:
@@ -1303,9 +1375,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "cursor:warp_on_change_workspace",
.description = "If true, move the cursor to the last focused window after changing the workspace.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
.description = "Move the cursor to the last focused window after changing the workspace. Options: 0 (Disabled), 1 (Enabled), 2 (Force - ignores cursor:no_warps option)",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
},
SConfigOptionDescription{
.value = "cursor:default_monitor",
@@ -1344,8 +1416,8 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "cursor:allow_dumb_copy",
.description = "Makes HW cursors work on Nvidia, at the cost of a possible hitch whenever the image changes",
.value = "cursor:use_cpu_buffer",
.description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. Experimental",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
@@ -1561,8 +1633,20 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "master:always_center_master",
.description = "when using orientation=center, keep the master window centered, even when it is the only window in the workspace.",
.value = "master:slave_count_for_center_master",
.description = "when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 10}, //##TODO RANGE?
},
SConfigOptionDescription{
.value = "master:center_master_slaves_on_right",
.description = "set if the slaves should appear on right of master when slave_count_for_center_master > 2",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "master:center_ignores_reserved",
.description = "centers the master window on monitor ignoring reserved areas",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
@@ -1580,4 +1664,22 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "experimental:wide_color_gamut",
.description = "force wide color gamut for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:hdr",
.description = "force static hdr for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:xx_color_management_v4",
.description = "enable color management protocol",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,30 @@
#pragma once
#include <hyprutils/animation/AnimationConfig.hpp>
#define CONFIG_MANAGER_H
#include <map>
#include "../debug/Log.hpp"
#include <unordered_map>
#include "../defines.hpp"
#include <variant>
#include <vector>
#include <deque>
#include <algorithm>
#include <regex>
#include <optional>
#include <functional>
#include <xf86drmMode.h>
#include "../helpers/WLClasses.hpp"
#include "../helpers/Monitor.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "../desktop/Window.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../desktop/LayerRule.hpp"
#include "defaultConfig.hpp"
#include "ConfigDataValues.hpp"
#include "../SharedDefs.hpp"
#include "../helpers/Color.hpp"
#include "../desktop/DesktopTypes.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../desktop/WindowRule.hpp"
#include "../managers/XWaylandManager.hpp"
#include <hyprlang.hpp>
#define INITANIMCFG(name) animationConfig[name] = {}
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]}
#define HANDLE void*
struct SWorkspaceRule {
@@ -56,26 +53,14 @@ struct SMonitorAdditionalReservedArea {
int right = 0;
};
struct SAnimationPropertyConfig {
bool overridden = true;
std::string internalBezier = "";
std::string internalStyle = "";
float internalSpeed = 0.f;
int internalEnabled = -1;
SAnimationPropertyConfig* pValues = nullptr;
SAnimationPropertyConfig* pParentAnimation = nullptr;
};
struct SPluginKeyword {
HANDLE handle = 0;
HANDLE handle = nullptr;
std::string name = "";
Hyprlang::PCONFIGHANDLERFUNC fn = nullptr;
};
struct SPluginVariable {
HANDLE handle = 0;
HANDLE handle = nullptr;
std::string name = "";
};
@@ -84,7 +69,7 @@ struct SExecRequestedRule {
uint64_t iPid = 0;
};
enum eConfigOptionType : uint16_t {
enum eConfigOptionType : uint8_t {
CONFIG_OPTION_BOOL = 0,
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
CONFIG_OPTION_FLOAT = 2,
@@ -96,7 +81,7 @@ enum eConfigOptionType : uint16_t {
CONFIG_OPTION_VECTOR = 8,
};
enum eConfigOptionFlags : uint32_t {
enum eConfigOptionFlags : uint8_t {
CONFIG_OPTION_FLAG_PERCENTAGE = (1 << 0),
};
@@ -119,7 +104,7 @@ struct SConfigOptionDescription {
};
struct SColorData {
CColor color;
CHyprColor color;
};
struct SChoiceData {
@@ -148,12 +133,18 @@ struct SConfigOptionDescription {
std::variant<SBoolData, SRangeData, SFloatData, SStringData, SColorData, SChoiceData, SGradientData, SVectorData> data;
};
struct SFirstExecRequest {
std::string exec = "";
bool withRules = false;
};
class CConfigManager {
public:
CConfigManager();
void tick();
void init();
void reload();
std::string verify();
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
@@ -165,9 +156,8 @@ class CConfigManager {
void* const* getConfigValuePtr(const std::string&);
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
void onPluginLoadUnload(const std::string& name, bool load);
static std::string getMainConfigPath();
const std::string getConfigString();
std::string getMainConfigPath();
std::string getConfigString();
SMonitorRule getMonitorRuleFor(const PHLMONITOR);
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
@@ -175,139 +165,146 @@ class CConfigManager {
PHLMONITOR getBoundMonitorForWS(const std::string&);
std::string getBoundMonitorStringForWS(const std::string&);
const std::deque<SWorkspaceRule>& getAllWorkspaceRules();
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
std::vector<SWindowRule> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
std::vector<SLayerRule> getMatchingRules(PHLLS);
std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
void ensurePersistentWorkspacesPresent();
const std::vector<SConfigOptionDescription>& getAllDescriptions();
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
const std::unordered_map<std::string, SP<Hyprutils::Animation::SAnimationPropertyConfig>>& getAnimationConfig();
void addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value);
void addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value);
void addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fun, Hyprlang::SHandlerOptions opts = {});
void removePluginConfig(HANDLE handle);
// no-op when done.
void dispatchExecOnce();
void dispatchExecShutdown();
void dispatchExecOnce();
void dispatchExecShutdown();
void performMonitorReload();
void appendMonitorRule(const SMonitorRule&);
bool replaceMonitorRule(const SMonitorRule&);
void ensureMonitorStatus();
void ensureVRR(PHLMONITOR pMonitor = nullptr);
void performMonitorReload();
void ensureMonitorStatus();
void ensureVRR(PHLMONITOR pMonitor = nullptr);
bool shouldUseSoftwareCursors();
bool shouldUseSoftwareCursors();
void updateWatcher();
std::string parseKeyword(const std::string&, const std::string&);
std::string parseKeyword(const std::string&, const std::string&);
void addParseError(const std::string&);
void addParseError(const std::string&);
SAnimationPropertyConfig* getAnimationPropertyConfig(const std::string&);
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
void addExecRule(const SExecRequestedRule&);
void addExecRule(const SExecRequestedRule&);
void handlePluginLoads();
std::string getErrors();
void handlePluginLoads();
std::string getErrors();
// keywords
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecShutdown(const std::string&, const std::string&);
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWindowRuleV2(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
std::optional<std::string> handleExec(const std::string&, const std::string&);
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecRawOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecShutdown(const std::string&, const std::string&);
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWindowRuleV2(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::string configCurrentPath;
std::string configCurrentPath;
std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(PHLWINDOW)>> mbWindowProperties = {
{"allowsinput", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.allowsInput; }},
{"dimaround", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.dimAround; }},
{"decorate", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.decorate; }},
{"focusonactivate", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }},
{"keepaspectratio", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }},
{"nearestneighbor", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }},
{"noanim", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noAnim; }},
{"noblur", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noBlur; }},
{"noborder", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noBorder; }},
{"nodim", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noDim; }},
{"nofocus", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noFocus; }},
{"nomaxsize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
{"norounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noRounding; }},
{"noshadow", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShadow; }},
{"noshortcutsinhibit", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
{"opaque", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.opaque; }},
{"forcergbx", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.RGBX; }},
{"syncfullscreen", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
{"immediate", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.tearing; }},
{"xray", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.xray; }},
std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> mbWindowProperties = {
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.allowsInput; }},
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.dimAround; }},
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.decorate; }},
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }},
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }},
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }},
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noAnim; }},
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBlur; }},
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBorder; }},
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noDim; }},
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noFocus; }},
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noRounding; }},
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShadow; }},
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.opaque; }},
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.RGBX; }},
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.tearing; }},
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.xray; }},
};
std::unordered_map<std::string, std::function<CWindowOverridableVar<int>*(PHLWINDOW)>> miWindowProperties = {
{"rounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.rounding; }}, {"bordersize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.borderSize; }}};
std::unordered_map<std::string, std::function<CWindowOverridableVar<int>*(const PHLWINDOW&)>> miWindowProperties = {
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.rounding; }},
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.borderSize; }},
};
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
std::unordered_map<std::string, std::function<CWindowOverridableVar<float>*(PHLWINDOW)>> mfWindowProperties = {
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.roundingPower; }},
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
bool m_bWantsMonitorReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
bool m_bLastConfigVerificationWasSuccessful = true;
private:
std::unique_ptr<Hyprlang::CConfig> m_pConfig;
UP<Hyprlang::CConfig> m_pConfig;
std::deque<std::string> configPaths; // stores all the config paths
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
std::vector<std::string> m_configPaths;
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; // stores all the animations with their set values
Hyprutils::Animation::CAnimationConfigTree m_AnimationTree;
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<std::string> m_vDeclaredPlugins;
std::vector<SPluginKeyword> pluginKeywords;
std::vector<SPluginVariable> pluginVariables;
std::vector<std::string> m_vDeclaredPlugins;
std::vector<SPluginKeyword> pluginKeywords;
std::vector<SPluginVariable> pluginVariables;
bool isFirstLaunch = true; // For exec-once
bool isFirstLaunch = true; // For exec-once
std::deque<SMonitorRule> m_dMonitorRules;
std::deque<SWorkspaceRule> m_dWorkspaceRules;
std::deque<SWindowRule> m_dWindowRules;
std::deque<SLayerRule> m_dLayerRules;
std::deque<std::string> m_dBlurLSNamespaces;
std::vector<SMonitorRule> m_vMonitorRules;
std::vector<SWorkspaceRule> m_vWorkspaceRules;
std::vector<SP<CWindowRule>> m_vWindowRules;
std::vector<SP<CLayerRule>> m_vLayerRules;
std::vector<std::string> m_dBlurLSNamespaces;
bool firstExecDispatched = false;
bool m_bManualCrashInitiated = false;
std::deque<std::string> firstExecRequests;
std::deque<std::string> finalExecRequests;
bool firstExecDispatched = false;
bool m_bManualCrashInitiated = false;
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
std::string m_szConfigErrors = "";
std::vector<SFirstExecRequest> firstExecRequests; // bool is for if with rules
std::vector<std::string> finalExecRequests;
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
std::string m_szConfigErrors = "";
// internal methods
void setAnimForChildren(SAnimationPropertyConfig* const);
void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
static std::optional<std::string> generateConfig(std::string configPath);
static std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
void reload();
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
};
inline std::unique_ptr<CConfigManager> g_pConfigManager;
inline UP<CConfigManager> g_pConfigManager;

View File

@@ -3,7 +3,6 @@
#include <string>
#include <typeindex>
#include <hyprlang.hpp>
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include "ConfigManager.hpp"
@@ -13,6 +12,7 @@ class CConfigValue {
CConfigValue(const std::string& val) {
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
// NOLINTNEXTLINE
p_ = PVHYPRLANG->getDataStaticPtr();
#ifdef HYPRLAND_DEBUG
@@ -70,4 +70,4 @@ template <>
inline Hyprlang::CUSTOMTYPE CConfigValue<Hyprlang::CUSTOMTYPE>::operator*() const {
RASSERT(false, "Impossible to implement operator* of CConfigValue<Hyprlang::CUSTOMTYPE>, use ptr()");
return *ptr();
}
}

View File

@@ -0,0 +1,81 @@
#include "ConfigWatcher.hpp"
#include <sys/inotify.h>
#include "../debug/Log.hpp"
#include <ranges>
#include <fcntl.h>
#include <unistd.h>
#include <filesystem>
using namespace Hyprutils::OS;
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
if (!m_inotifyFd.isValid()) {
Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
return;
}
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
m_inotifyFd.reset();
return;
}
}
CFileDescriptor& CConfigWatcher::getInotifyFD() {
return m_inotifyFd;
}
void CConfigWatcher::setWatchList(const std::vector<std::string>& paths) {
// we clear all watches first, because whichever fired is now invalid
// or that is at least what it seems to be.
// since we don't know which fired,
// plus it doesn't matter that much, these ops are done rarely and fast anyways.
// cleanup old paths
for (auto& watch : m_watches) {
inotify_rm_watch(m_inotifyFd.get(), watch.wd);
}
m_watches.clear();
// add new paths
for (const auto& path : paths) {
m_watches.emplace_back(SInotifyWatch{
.wd = inotify_add_watch(m_inotifyFd.get(), path.c_str(), IN_MODIFY | IN_DONT_FOLLOW),
.file = path,
});
std::error_code ec, ec2;
const auto CANONICAL = std::filesystem::canonical(path, ec);
const auto IS_SYMLINK = std::filesystem::is_symlink(path, ec2);
if (!ec && !ec2 && IS_SYMLINK) {
m_watches.emplace_back(SInotifyWatch{
.wd = inotify_add_watch(m_inotifyFd.get(), CANONICAL.c_str(), IN_MODIFY),
.file = path,
});
}
}
}
void CConfigWatcher::setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn) {
m_watchCallback = fn;
}
void CConfigWatcher::onInotifyEvent() {
inotify_event ev;
while (read(m_inotifyFd.get(), &ev, sizeof(ev)) > 0) {
const auto WD = std::ranges::find_if(m_watches.begin(), m_watches.end(), [wd = ev.wd](const auto& e) { return e.wd == wd; });
if (WD == m_watches.end()) {
Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev.wd);
return;
}
m_watchCallback(SConfigWatchEvent{
.file = WD->file,
});
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include "../helpers/memory/Memory.hpp"
#include <vector>
#include <string>
#include <functional>
#include <hyprutils/os/FileDescriptor.hpp>
class CConfigWatcher {
public:
CConfigWatcher();
~CConfigWatcher() = default;
struct SConfigWatchEvent {
std::string file;
};
Hyprutils::OS::CFileDescriptor& getInotifyFD();
void setWatchList(const std::vector<std::string>& paths);
void setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn);
void onInotifyEvent();
private:
struct SInotifyWatch {
int wd = -1;
std::string file;
};
std::function<void(const SConfigWatchEvent&)> m_watchCallback;
std::vector<SInotifyWatch> m_watches;
Hyprutils::OS::CFileDescriptor m_inotifyFd;
};
inline UP<CConfigWatcher> g_pConfigWatcher = makeUnique<CConfigWatcher>();

View File

@@ -4,8 +4,8 @@
inline const std::string AUTOCONFIG = R"#(
# #######################################################################################
# AUTOGENERATED HYPR CONFIG.
# PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hypr.conf AND EDIT IT,
# AUTOGENERATED HYPRLAND CONFIG.
# PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hyprland.conf AND EDIT IT,
# OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS.
# #######################################################################################
@@ -94,6 +94,7 @@ general {
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
rounding = 10
rounding_power = 2
# Change transparency of focused and unfocused windows
active_opacity = 1.0
@@ -149,13 +150,10 @@ animations {
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[t1], gapsout:0, gapsin:0
# workspace = w[tg1], gapsout:0, gapsin:0
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[t1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[t1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tg1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tg1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]

View File

@@ -2,10 +2,11 @@
#include <fcntl.h>
#include <sys/utsname.h>
#include <link.h>
#include <time.h>
#include <errno.h>
#include <ctime>
#include <cerrno>
#include <sys/stat.h>
#include <filesystem>
#include "../helpers/MiscFunctions.hpp"
#include "../plugins/PluginSystem.hpp"
#include "../signal-safe.hpp"
@@ -31,10 +32,10 @@ static char const* const MESSAGES[] = {"Sorry, didn't mean to...",
// <random> is not async-signal-safe, fake it with time(NULL) instead
char const* getRandomMessage() {
return MESSAGES[time(NULL) % (sizeof(MESSAGES) / sizeof(MESSAGES[0]))];
return MESSAGES[time(nullptr) % (sizeof(MESSAGES) / sizeof(MESSAGES[0]))];
}
[[noreturn]] inline void exit_with_error(char const* err) {
[[noreturn]] inline void exitWithError(char const* err) {
write(STDERR_FILENO, err, strlen(err));
// perror() is not signal-safe, but we use it here
// because if the crash-handler already crashed, it can't get any worse.
@@ -42,17 +43,17 @@ char const* getRandomMessage() {
abort();
}
void CrashReporter::createAndSaveCrash(int sig) {
int reportFd;
void NCrashReporter::createAndSaveCrash(int sig) {
int reportFd = -1;
// We're in the signal handler, so we *only* have stack memory.
// To save as much stack memory as possible,
// destroy things as soon as possible.
{
MaxLengthCString<255> reportPath;
CMaxLengthCString<255> reportPath;
const auto HOME = sig_getenv("HOME");
const auto CACHE_HOME = sig_getenv("XDG_CACHE_HOME");
const auto HOME = sigGetenv("HOME");
const auto CACHE_HOME = sigGetenv("XDG_CACHE_HOME");
if (CACHE_HOME && CACHE_HOME[0] != '\0') {
reportPath += CACHE_HOME;
@@ -61,24 +62,24 @@ void CrashReporter::createAndSaveCrash(int sig) {
reportPath += HOME;
reportPath += "/.cache/hyprland";
} else {
exit_with_error("$CACHE_HOME and $HOME not set, nowhere to report crash\n");
exitWithError("$CACHE_HOME and $HOME not set, nowhere to report crash\n");
return;
}
int ret = mkdir(reportPath.get_str(), S_IRWXU);
int ret = mkdir(reportPath.getStr(), S_IRWXU);
//__asm__("int $3");
if (ret < 0 && errno != EEXIST) {
exit_with_error("failed to mkdir() crash report directory\n");
exitWithError("failed to mkdir() crash report directory\n");
}
reportPath += "/hyprlandCrashReport";
reportPath.write_num(getpid());
reportPath.writeNum(getpid());
reportPath += ".txt";
{
BufFileWriter<64> stderr(2);
CBufFileWriter<64> stderr(2);
stderr += "Hyprland has crashed :( Consult the crash report at ";
if (!reportPath.boundsExceeded()) {
stderr += reportPath.get_str();
stderr += reportPath.getStr();
} else {
stderr += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]";
}
@@ -86,12 +87,12 @@ void CrashReporter::createAndSaveCrash(int sig) {
stderr.flush();
}
reportFd = open(reportPath.get_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
reportFd = open(reportPath.getStr(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (reportFd < 0) {
exit_with_error("Failed to open crash report path for writing");
exitWithError("Failed to open crash report path for writing");
}
}
BufFileWriter<512> finalCrashReport(reportFd);
CBufFileWriter<512> finalCrashReport(reportFd);
finalCrashReport += "--------------------------------------------\n Hyprland Crash Report\n--------------------------------------------\n";
finalCrashReport += getRandomMessage();
@@ -100,7 +101,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "Hyprland received signal ";
finalCrashReport.writeNum(sig);
finalCrashReport += '(';
finalCrashReport += sig_strsignal(sig);
finalCrashReport += sigStrsignal(sig);
finalCrashReport += ")\nVersion: ";
finalCrashReport += GIT_COMMIT_HASH;
finalCrashReport += "\nTag: ";
@@ -111,7 +112,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
#ifdef LEGACY_RENDERER
finalCrashReport += "legacyrenderer\n";
#endif
#ifndef ISDEBUG
#if ISDEBUG
finalCrashReport += "debug\n";
#endif
#ifdef NO_XWAYLAND
@@ -122,9 +123,9 @@ void CrashReporter::createAndSaveCrash(int sig) {
if (g_pPluginSystem && g_pPluginSystem->pluginCount() > 0) {
finalCrashReport += "Hyprland seems to be running with plugins. This crash might not be Hyprland's fault.\nPlugins:\n";
size_t count = g_pPluginSystem->pluginCount();
CPlugin* plugins[count];
g_pPluginSystem->sig_getPlugins(plugins, count);
const size_t count = g_pPluginSystem->pluginCount();
std::vector<CPlugin*> plugins(count);
g_pPluginSystem->sigGetPlugins(plugins.data(), count);
for (size_t i = 0; i < count; i++) {
auto p = plugins[i];
@@ -159,9 +160,9 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "GPU:\n\t";
#if defined(__DragonFly__) || defined(__FreeBSD__)
finalCrashReport.writeCmdOutput("pciconf -lv | fgrep -A4 vga");
finalCrashReport.writeCmdOutput("pciconf -lv | grep -F -A4 vga");
#else
finalCrashReport.writeCmdOutput("lspci -vnn | grep VGA");
finalCrashReport.writeCmdOutput("lspci -vnn | grep -E '(VGA|Display|3D)'");
#endif
finalCrashReport += "\n\nos-release:\n";
@@ -241,5 +242,5 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find("\n") + 1);
finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find('\n') + 1);
}

View File

@@ -2,6 +2,6 @@
#include "../defines.hpp"
namespace CrashReporter {
namespace NCrashReporter {
void createAndSaveCrash(int sig);
};

View File

@@ -1,10 +1,13 @@
#include "HyprCtl.hpp"
#include <algorithm>
#include <format>
#include <fstream>
#include <iterator>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -22,6 +25,7 @@
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
#include <aquamarine/input/Input.hpp>
#include "../config/ConfigDataValues.hpp"
@@ -36,6 +40,17 @@ using namespace Hyprutils::String;
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../version.h"
#include "../Compositor.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../plugins/PluginSystem.hpp"
#include "../managers/AnimationManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
#include "../render/Renderer.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@@ -105,6 +120,7 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"vrr": {},
"solitary": "{:x}",
"activelyTearing": {},
"directScanoutTo": "{:x}",
"disabled": {},
"currentFormat": "{}",
"mirrorOf": "{}",
@@ -117,26 +133,27 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (uint64_t)m->solitaryClient.get(),
(m->tearingState.activelyTearing ? "true" : "false"), (m->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat),
m->pMirrorOf ? std::format("{}", m->pMirrorOf->ID) : "none", availableModesForOutput(m, format));
(m->tearingState.activelyTearing ? "true" : "false"), (uint64_t)m->lastScanout.get(), (m->m_bEnabled ? "false" : "true"),
formatToString(m->output->state->state().drmFormat), m->pMirrorOf ? std::format("{}", m->pMirrorOf->ID) : "none", availableModesForOutput(m, format));
} else {
result += std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tmirrorOf: {}\n\tavailableModes: {}\n\n",
"dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tactivelyTearing: {}\n\tdirectScanoutTo: {:x}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tmirrorOf: "
"{}\n\tavailableModes: {}\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->szShortDescription,
m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, (uint64_t)m->solitaryClient.get(),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->output->state->state().drmFormat),
m->tearingState.activelyTearing, (uint64_t)m->lastScanout.get(), !m->m_bEnabled, formatToString(m->output->state->state().drmFormat),
m->pMirrorOf ? std::format("{}", m->pMirrorOf->ID) : "none", availableModesForOutput(m, format));
}
return result;
}
std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
auto allMonitors = false;
@@ -239,30 +256,32 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
"grouped": [{}],
"tags": [{}],
"swallowing": "0x{:x}",
"focusHistoryID": {}
"focusHistoryID": {},
"inhibitingIdle": {}
}},)#",
(uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y,
(int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID,
(uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y,
(int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID,
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"),
(int64_t)w->monitorID(), escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass),
escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
(uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
(uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y,
(int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName),
(int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->monitorID(), w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(),
(int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\n",
(uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y,
(int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID,
(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->monitorID(), w->m_szClass, w->m_szTitle,
w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal,
(uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w),
(int)g_pInputManager->isWindowInhibiting(w, false));
}
}
std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
@@ -303,12 +322,12 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
"lastwindowtitle": "{}"
}})#",
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.get(), PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), w->getWindows(), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"),
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
} else {
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.get(), PLASTW ? PLASTW->m_szTitle : "");
w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", w->getWindows(), (int)w->m_bHasFullscreenWindow,
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_szTitle : "");
}
}
@@ -361,7 +380,8 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF
return result;
}
}
std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
if (!g_pCompositor->m_pLastMonitor)
return "unsafe state";
@@ -374,7 +394,7 @@ std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string requ
return CHyprCtl::getWorkspaceData(w, format);
}
std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@@ -395,7 +415,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
return result;
}
std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
@@ -415,7 +435,7 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
return result;
}
std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!validMapped(PWINDOW))
@@ -429,7 +449,7 @@ std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request
return result;
}
std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@@ -503,7 +523,7 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
return result;
}
std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
@@ -525,13 +545,13 @@ std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
return result;
}
std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
std::string currErrors = g_pConfigManager->getErrors();
CVarList errLines(currErrors, 0, '\n');
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto line : errLines) {
for (const auto& line : errLines) {
result += std::format(
R"#(
"{}",)#",
@@ -541,14 +561,14 @@ std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request
trimTrailingComma(result);
result += "\n]\n";
} else {
for (auto line : errLines) {
for (const auto& line : errLines) {
result += std::format("{}\n", line);
}
}
return result;
}
std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
auto getModState = [](SP<IKeyboard> keyboard, const char* xkbModName) -> bool {
@@ -718,14 +738,14 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
return result;
}
std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
ret += "animations:\n";
for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second.overridden,
ac.second.internalBezier, ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle);
ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second->overridden,
ac.second->internalBezier, ac.second->internalEnabled, ac.second->internalSpeed, ac.second->internalStyle);
}
ret += "beziers:\n";
@@ -747,8 +767,8 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
"speed": {:.2f},
"style": "{}"
}},)#",
ac.first, ac.second.overridden ? "true" : "false", escapeJSONStrings(ac.second.internalBezier), ac.second.internalEnabled ? "true" : "false",
ac.second.internalSpeed, escapeJSONStrings(ac.second.internalStyle));
ac.first, ac.second->overridden ? "true" : "false", escapeJSONStrings(ac.second->internalBezier), ac.second->internalEnabled ? "true" : "false",
ac.second->internalSpeed, escapeJSONStrings(ac.second->internalStyle));
}
ret[ret.length() - 1] = ']';
@@ -771,7 +791,7 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
return ret;
}
std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@@ -785,7 +805,7 @@ std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request)
return result;
}
std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
@@ -808,31 +828,31 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
return ret;
}
std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
for (auto const& kb : g_pKeybindManager->m_vKeybinds) {
ret += "bind";
if (kb.locked)
if (kb->locked)
ret += "l";
if (kb.mouse)
if (kb->mouse)
ret += "m";
if (kb.release)
if (kb->release)
ret += "r";
if (kb.repeat)
if (kb->repeat)
ret += "e";
if (kb.nonConsuming)
if (kb->nonConsuming)
ret += "n";
if (kb.hasDescription)
if (kb->hasDescription)
ret += "d";
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap,
kb.key, kb.keycode, kb.catchAll, kb.description, kb.handler, kb.arg);
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb->modmask,
kb->submap, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg);
}
} else {
// json
ret += "[";
for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
for (auto const& kb : g_pKeybindManager->m_vKeybinds) {
ret += std::format(
R"#(
{{
@@ -840,6 +860,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"mouse": {},
"release": {},
"repeat": {},
"longPress": {},
"non_consuming": {},
"has_description": {},
"modmask": {},
@@ -851,9 +872,9 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"dispatcher": "{}",
"arg": "{}"
}},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.nonConsuming ? "true" : "false",
kb.hasDescription ? "true" : "false", kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, kb.catchAll ? "true" : "false",
escapeJSONStrings(kb.description), escapeJSONStrings(kb.handler), escapeJSONStrings(kb.arg));
kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap), escapeJSONStrings(kb->key), kb->keycode,
kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg));
}
trimTrailingComma(ret);
ret += "]";
@@ -871,17 +892,18 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
"Date: {}\n"
"Tag: {}, commits: {}\n"
"built against aquamarine {}\n\n\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION);
"built against:\n aquamarine {}\n hyprlang {}\n hyprutils {}\n hyprcursor {}\n hyprgraphics {}\n\n\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION,
HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
#if (!defined(LEGACY_RENDERER) && !defined(ISDEBUG) && !defined(NO_XWAYLAND))
#if (!defined(LEGACY_RENDERER) && !ISDEBUG && !defined(NO_XWAYLAND))
result += "no flags were set\n";
#else
result += "flags set:\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
#endif
#ifdef ISDEBUG
#if ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
@@ -901,14 +923,18 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"buildHyprlang": "{}",
"buildHyprutils": "{}",
"buildHyprcursor": "{}",
"buildHyprgraphics": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION);
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
#endif
#ifdef ISDEBUG
#if ISDEBUG
result += "\"debug\",";
#endif
#ifdef NO_XWAYLAND
@@ -942,18 +968,51 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request)
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
const std::string GPUINFO = execAndGet("pciconf -lv | grep -F -A4 vga");
#elif defined(__arm__) || defined(__aarch64__)
const std::string GPUINFO = execAndGet("cat /proc/device-tree/soc*/gpu*/compatible");
std::string GPUINFO;
const std::filesystem::path dev_tree = "/proc/device-tree";
try {
if (std::filesystem::exists(dev_tree) && std::filesystem::is_directory(dev_tree)) {
std::for_each(std::filesystem::directory_iterator(dev_tree), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& entry) {
if (std::filesystem::is_directory(entry) && entry.path().filename().string().starts_with("soc")) {
std::for_each(std::filesystem::directory_iterator(entry.path()), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& sub_entry) {
if (std::filesystem::is_directory(sub_entry) && sub_entry.path().filename().string().starts_with("gpu")) {
std::filesystem::path file_path = sub_entry.path() / "compatible";
std::ifstream file(file_path);
if (file)
GPUINFO.append(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
}
});
}
});
}
} catch (...) { GPUINFO = "error"; }
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
#endif
result += "GPU information: \n" + GPUINFO;
if (GPUINFO.contains("NVIDIA") && std::filesystem::exists("/proc/driver/nvidia/version"))
result += execAndGet("cat /proc/driver/nvidia/version | grep NVRM");
if (GPUINFO.contains("NVIDIA") && std::filesystem::exists("/proc/driver/nvidia/version")) {
std::ifstream file("/proc/driver/nvidia/version");
std::string line;
if (file.is_open()) {
while (std::getline(file, line)) {
if (!line.contains("NVRM"))
continue;
result += line;
result += "\n";
}
} else
result += "error";
}
result += "\n\n";
result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n";
if (std::ifstream file("/etc/os-release"); file.is_open()) {
std::stringstream buffer;
buffer << file.rdbuf();
result += "os-release: " + buffer.str() + "\n\n";
} else
result += "os-release: error\n\n";
result += "plugins:\n";
if (g_pPluginSystem) {
@@ -972,7 +1031,7 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request)
return result;
}
std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
// get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1);
@@ -993,7 +1052,7 @@ std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
return res.success ? "ok" : res.error;
}
std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// Find the first space to strip the keyword keyword
auto const firstSpacePos = in.find_first_of(' ');
if (firstSpacePos == std::string::npos) // Handle the case where there's no space found (invalid input)
@@ -1042,6 +1101,9 @@ std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
}
}
if (COMMAND.contains("misc:disable_autoreload"))
g_pConfigManager->updateWatcher();
// decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
COMMAND.starts_with("windowrule")) {
@@ -1059,32 +1121,29 @@ std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
return retval;
}
std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {
const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
g_pConfigManager->m_bForceReload = true;
if (REQMODE == "config-only") {
if (REQMODE == "config-only")
g_pConfigManager->m_bNoMonitorReload = true;
}
g_pConfigManager->tick();
g_pConfigManager->reload();
return "ok";
}
std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
g_pInputManager->setClickMode(CLICKMODE_KILL);
return "ok";
}
std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
return g_pCompositor->m_szCurrentSplash;
}
std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
@@ -1102,41 +1161,33 @@ std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
return "error";
}
std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
// split by ;
request = request.substr(9);
std::string curitem = "";
std::string reply = "";
auto nextItem = [&]() {
auto idx = request.find_first_of(';');
if (idx != std::string::npos) {
curitem = request.substr(0, idx);
request = request.substr(idx + 1);
} else {
curitem = request;
request = "";
}
curitem = trim(curitem);
};
nextItem();
static std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
// split by ; ignores ; inside [] and adds ; on last command
request = request.substr(9);
std::string reply = "";
const std::string DELIMITER = "\n\n\n";
int bracket = 0;
size_t idx = 0;
while (curitem != "" || request != "") {
reply += g_pHyprCtl->getReply(curitem) + DELIMITER;
nextItem();
for (size_t i = 0; i <= request.size(); ++i) {
char ch = (i < request.size()) ? request[i] : ';';
if (ch == '[')
++bracket;
else if (ch == ']')
--bracket;
else if (ch == ';' && bracket == 0) {
if (idx < i)
reply += g_pHyprCtl->getReply(trim(request.substr(idx, i - idx))).append(DELIMITER);
idx = i + 1;
continue;
}
}
return reply.substr(0, std::max(static_cast<int>(reply.size() - DELIMITER.size()), 0));
}
std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto SIZESTR = vars[vars.size() - 1];
@@ -1160,7 +1211,7 @@ std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request)
return "ok";
}
std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto KB = vars[1];
@@ -1235,7 +1286,7 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
return "ok";
}
std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
std::string errorMessage = "";
@@ -1249,7 +1300,7 @@ std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
return "ok";
}
const CColor COLOR = configStringToInt(vars[1]);
const CHyprColor COLOR = configStringToInt(vars[1]).value_or(0);
for (size_t i = 2; i < vars.size(); ++i)
errorMessage += vars[i] + ' ';
@@ -1264,12 +1315,12 @@ std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
return "ok";
}
std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
auto result = g_pKeybindManager->m_mDispatchers["setprop"](request.substr(request.find_first_of(' ') + 1, -1));
static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
auto result = g_pKeybindManager->m_mDispatchers["setprop"](request.substr(request.find_first_of(' ') + 1));
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
}
std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = "";
auto nextItem = [&]() {
@@ -1310,23 +1361,22 @@ std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request)
return std::format("custom type: {}\nset: {}", ((ICustomConfigValueData*)std::any_cast<void*>(VAL))->toString(), VAR->m_bSetByUser);
} else {
if (TYPE == typeid(Hyprlang::INT))
return std::format("{{\"option\": \"{}\", \"int\": {}, \"set\": {} }}", curitem, std::any_cast<Hyprlang::INT>(VAL), VAR->m_bSetByUser);
return std::format(R"({{"option": "{}", "int": {}, "set": {} }})", curitem, std::any_cast<Hyprlang::INT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::FLOAT))
return std::format("{{\"option\": \"{}\", \"float\": {:2f}, \"set\": {} }}", curitem, std::any_cast<Hyprlang::FLOAT>(VAL), VAR->m_bSetByUser);
return std::format(R"({{"option": "{}", "float": {:2f}, "set": {} }})", curitem, std::any_cast<Hyprlang::FLOAT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::VEC2))
return std::format("{{\"option\": \"{}\", \"vec2\": [{},{}], \"set\": {} }}", curitem, std::any_cast<Hyprlang::VEC2>(VAL).x, std::any_cast<Hyprlang::VEC2>(VAL).y,
return std::format(R"({{"option": "{}", "vec2": [{},{}], "set": {} }})", curitem, std::any_cast<Hyprlang::VEC2>(VAL).x, std::any_cast<Hyprlang::VEC2>(VAL).y,
VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::STRING))
return std::format("{{\"option\": \"{}\", \"str\": \"{}\", \"set\": {} }}", curitem, escapeJSONStrings(std::any_cast<Hyprlang::STRING>(VAL)), VAR->m_bSetByUser);
return std::format(R"({{"option": "{}", "str": "{}", "set": {} }})", curitem, escapeJSONStrings(std::any_cast<Hyprlang::STRING>(VAL)), VAR->m_bSetByUser);
else if (TYPE == typeid(void*))
return std::format("{{\"option\": \"{}\", \"custom\": \"{}\", \"set\": {} }}", curitem, ((ICustomConfigValueData*)std::any_cast<void*>(VAL))->toString(),
VAR->m_bSetByUser);
return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, ((ICustomConfigValueData*)std::any_cast<void*>(VAL))->toString(), VAR->m_bSetByUser);
}
return "invalid type (internal error)";
}
std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
@@ -1352,7 +1402,7 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
return result;
}
std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
@@ -1407,7 +1457,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
return "ok";
}
std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
@@ -1435,17 +1485,40 @@ std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
g_pPluginSystem->unloadPlugin(PLUGIN);
} else if (OPERATION == "list") {
const auto PLUGINS = g_pPluginSystem->getAllPlugins();
const auto PLUGINS = g_pPluginSystem->getAllPlugins();
std::string result = "";
if (PLUGINS.size() == 0)
return "no plugins loaded";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
std::string list = "";
for (auto const& p : PLUGINS) {
list += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description);
if (PLUGINS.size() == 0)
return "[]";
for (auto const& p : PLUGINS) {
result += std::format(
R"#(
{{
"name": "{}",
"author": "{}",
"handle": "{:x}",
"version": "{}",
"description": "{}"
}},)#",
escapeJSONStrings(p->name), escapeJSONStrings(p->author), (uintptr_t)p->m_pHandle, escapeJSONStrings(p->version), escapeJSONStrings(p->description));
}
trimTrailingComma(result);
result += "]";
} else {
if (PLUGINS.size() == 0)
return "no plugins loaded";
for (auto const& p : PLUGINS) {
result +=
std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description);
}
}
return list;
return result;
} else {
return "unknown opt";
}
@@ -1453,7 +1526,7 @@ std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
return "ok";
}
std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 5)
@@ -1469,9 +1542,8 @@ std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
icon = std::stoi(ICON);
} catch (std::exception& e) { return "invalid arg 1"; }
if (icon > ICON_NONE || icon < 0) {
if (icon > ICON_NONE || icon < 0)
icon = ICON_NONE;
}
const auto TIME = vars[2];
int time = 0;
@@ -1479,10 +1551,13 @@ std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
time = std::stoi(TIME);
} catch (std::exception& e) { return "invalid arg 2"; }
CColor color = configStringToInt(vars[3]);
const auto COLOR_RESULT = configStringToInt(vars[3]);
if (!COLOR_RESULT)
return "invalid arg 3";
CHyprColor color = *COLOR_RESULT;
size_t msgidx = 4;
float fontsize = 13.f;
size_t msgidx = 4;
float fontsize = 13.f;
if (vars[msgidx].length() > 9 && vars[msgidx].compare(0, 9, "fontsize:") == 0) {
const auto FONTSIZE = vars[msgidx].substr(9);
@@ -1506,7 +1581,7 @@ std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
return "ok";
}
std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string request) {
static std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
int amount = -1;
@@ -1526,7 +1601,7 @@ std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string reque
return "ok";
}
std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
static std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
std::string lockedStr = g_pSessionLockManager->isSessionLocked() ? "true" : "false";
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
lockedStr = std::format(R"#(
@@ -1538,7 +1613,7 @@ std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
return lockedStr;
}
std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
static std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
std::string json = "{";
const auto& DESCS = g_pConfigManager->getAllDescriptions();
@@ -1553,7 +1628,7 @@ std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
return json;
}
std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
static std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
std::string submap = g_pKeybindManager->getCurrentSubmap();
if (submap.empty())
submap = "default";
@@ -1606,8 +1681,6 @@ CHyprCtl::CHyprCtl() {
CHyprCtl::~CHyprCtl() {
if (m_eventSource)
wl_event_source_remove(m_eventSource);
if (m_iSocketFD >= 0)
close(m_iSocketFD);
if (!m_socketPath.empty())
unlink(m_socketPath.c_str());
}
@@ -1702,7 +1775,7 @@ std::string CHyprCtl::getReply(std::string request) {
}
for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || !g_pCompositor->isWorkspaceVisible(w->m_pWorkspace))
if (!w->m_bIsMapped || !w->m_pWorkspace || !w->m_pWorkspace->isVisible())
continue;
w->updateDynamicRules();
@@ -1722,7 +1795,7 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input);
}
bool successWrite(int fd, const std::string& data, bool needLog = true) {
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
if (write(fd, data.c_str(), data.length()) > 0)
return true;
@@ -1735,18 +1808,18 @@ bool successWrite(int fd, const std::string& data, bool needLog = true) {
return false;
}
void runWritingDebugLogThread(const int conn) {
static void runWritingDebugLogThread(const int conn) {
using namespace std::chrono_literals;
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
//will be finished, when reading side close connection
std::thread([conn]() {
while (Debug::RollingLogFollow::Get().IsRunning()) {
if (Debug::RollingLogFollow::Get().isEmpty(conn)) {
while (Debug::SRollingLogFollow::get().isRunning()) {
if (Debug::SRollingLogFollow::get().isEmpty(conn)) {
std::this_thread::sleep_for(1000ms);
continue;
}
auto line = Debug::RollingLogFollow::Get().GetLog(conn);
auto line = Debug::SRollingLogFollow::get().getLog(conn);
if (!successWrite(conn, line))
// We cannot write, when connection is closed. So thread will successfully exit by itself
break;
@@ -1754,22 +1827,25 @@ void runWritingDebugLogThread(const int conn) {
std::this_thread::sleep_for(100ms);
}
close(conn);
Debug::RollingLogFollow::Get().StopFor(conn);
Debug::SRollingLogFollow::get().stopFor(conn);
}).detach();
}
bool isFollowUpRollingLogRequest(const std::string& request) {
static bool isFollowUpRollingLogRequest(const std::string& request) {
return request.contains("rollinglog") && request.contains("f");
}
int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0;
if (!g_pHyprCtl->m_iSocketFD.isValid())
return 0;
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
std::array<char, 1024> readBuffer;
@@ -1813,9 +1889,9 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (isFollowUpRollingLogRequest(request)) {
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
Debug::RollingLogFollow::Get().StartFor(ACCEPTEDCONNECTION);
Debug::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
runWritingDebugLogThread(ACCEPTEDCONNECTION);
Debug::log(LOG, Debug::RollingLogFollow::Get().DebugInfo());
Debug::log(LOG, Debug::SRollingLogFollow::get().debugInfo());
} else
close(ACCEPTEDCONNECTION);
@@ -1826,9 +1902,9 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
}
void CHyprCtl::startHyprCtlSocket() {
m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
m_iSocketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
if (m_iSocketFD < 0) {
if (!m_iSocketFD.isValid()) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return;
}
@@ -1839,15 +1915,15 @@ void CHyprCtl::startHyprCtlSocket() {
strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str());
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
if (bind(m_iSocketFD.get(), (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(m_iSocketFD, 10);
listen(m_iSocketFD.get(), 10);
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View File

@@ -1,9 +1,10 @@
#pragma once
#include "../Compositor.hpp"
#include <fstream>
#include "../helpers/MiscFunctions.hpp"
#include "../desktop/Window.hpp"
#include <functional>
#include <hyprutils/os/FileDescriptor.hpp>
// exposed for main.cpp
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request);
@@ -14,12 +15,12 @@ class CHyprCtl {
CHyprCtl();
~CHyprCtl();
std::string makeDynamicCall(const std::string& input);
SP<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
void unregisterCommand(const SP<SHyprCtlCommand>& cmd);
std::string getReply(std::string);
std::string makeDynamicCall(const std::string& input);
SP<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
void unregisterCommand(const SP<SHyprCtlCommand>& cmd);
std::string getReply(std::string);
int m_iSocketFD = -1;
Hyprutils::OS::CFileDescriptor m_iSocketFD;
struct {
bool all = false;
@@ -38,4 +39,4 @@ class CHyprCtl {
std::string m_socketPath;
};
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;
inline UP<CHyprCtl> g_pHyprCtl;

View File

@@ -2,13 +2,21 @@
#include "HyprDebugOverlay.hpp"
#include "config/ConfigValue.hpp"
#include "../Compositor.hpp"
#include "../render/pass/TexPassElement.hpp"
#include "../render/Renderer.hpp"
#include "../managers/AnimationManager.hpp"
CHyprDebugOverlay::CHyprDebugOverlay() {
m_pTexture = makeShared<CTexture>();
}
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
m_dLastRenderTimes.push_back(durationUs / 1000.f);
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_dLastRenderTimes.emplace_back(durationUs / 1000.f);
if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate)
m_dLastRenderTimes.pop_front();
@@ -18,7 +26,12 @@ void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs)
}
void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
m_dLastRenderTimesNoOverlay.push_back(durationUs / 1000.f);
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_dLastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
if (m_dLastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->refreshRate)
m_dLastRenderTimesNoOverlay.pop_front();
@@ -28,7 +41,12 @@ void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float du
}
void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
m_dLastFrametimes.push_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f);
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_dLastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f);
if (m_dLastFrametimes.size() > (long unsigned int)pMonitor->refreshRate)
m_dLastFrametimes.pop_front();
@@ -180,23 +198,38 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
double posX = 0, posY = 0;
cairo_get_current_point(cr, &posX, &posY);
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
g_pHyprRenderer->damageBox(m_wbLastDrawnBox);
m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset + MARGIN_TOP - 1,
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
g_pHyprRenderer->damageBox(m_wbLastDrawnBox);
return posY - offset;
}
void CHyprDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_mMonitorOverlays[pMonitor].renderData(pMonitor, durationUs);
}
void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_mMonitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
}
void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_mMonitorOverlays[pMonitor].frameData(pMonitor);
}
@@ -238,6 +271,8 @@ void CHyprDebugOverlay::draw() {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
CBox pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f);
CTexPassElement::SRenderData data;
data.tex = m_pTexture;
data.box = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
}

View File

@@ -1,11 +1,10 @@
#pragma once
#include "../defines.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Texture.hpp"
#include <deque>
#include <cairo/cairo.h>
#include <map>
#include <deque>
class CHyprRenderer;
@@ -49,4 +48,4 @@ class CHyprDebugOverlay {
friend class CHyprRenderer;
};
inline std::unique_ptr<CHyprDebugOverlay> g_pDebugOverlay;
inline UP<CHyprDebugOverlay> g_pDebugOverlay;

View File

@@ -3,8 +3,13 @@
#include "HyprNotificationOverlay.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../render/pass/TexPassElement.hpp"
inline auto iconBackendFromLayout(PangoLayout* layout) {
#include "../managers/AnimationManager.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../render/Renderer.hpp"
static inline auto iconBackendFromLayout(PangoLayout* layout) {
// preference: Nerd > FontAwesome > text
auto eIconBackendChecks = std::array<eIconBackend, 2>{ICONS_BACKEND_NF, ICONS_BACKEND_FA};
for (auto iconID : eIconBackendChecks) {
@@ -18,10 +23,10 @@ inline auto iconBackendFromLayout(PangoLayout* layout) {
CHyprNotificationOverlay::CHyprNotificationOverlay() {
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (m_dNotifications.size() == 0)
if (m_vNotifications.size() == 0)
return;
g_pHyprRenderer->damageBox(&m_bLastDamage);
g_pHyprRenderer->damageBox(m_bLastDamage);
});
m_pTexture = makeShared<CTexture>();
@@ -34,11 +39,11 @@ CHyprNotificationOverlay::~CHyprNotificationOverlay() {
cairo_surface_destroy(m_pCairoSurface);
}
void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon, const float fontSize) {
const auto PNOTIF = m_dNotifications.emplace_back(std::make_unique<SNotification>()).get();
void CHyprNotificationOverlay::addNotification(const std::string& text, const CHyprColor& color, const float timeMs, const eIcons icon, const float fontSize) {
const auto PNOTIF = m_vNotifications.emplace_back(makeUnique<SNotification>()).get();
PNOTIF->text = icon != eIcons::ICON_NONE ? " " + text /* tiny bit of padding otherwise icon touches text */ : text;
PNOTIF->color = color == CColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->color = color == CHyprColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->started.reset();
PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon;
@@ -51,12 +56,12 @@ void CHyprNotificationOverlay::addNotification(const std::string& text, const CC
void CHyprNotificationOverlay::dismissNotifications(const int amount) {
if (amount == -1)
m_dNotifications.clear();
m_vNotifications.clear();
else {
const int AMT = std::min(amount, static_cast<int>(m_dNotifications.size()));
const int AMT = std::min(amount, static_cast<int>(m_vNotifications.size()));
for (int i = 0; i < AMT; ++i) {
m_dNotifications.pop_front();
m_vNotifications.erase(m_vNotifications.begin());
}
}
}
@@ -87,7 +92,7 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
const auto iconBackendID = iconBackendFromLayout(layout);
const auto PBEZIER = g_pAnimationManager->getBezier("default");
for (auto const& notif : m_dNotifications) {
for (auto const& notif : m_vNotifications) {
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
@@ -182,7 +187,7 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
g_object_unref(layout);
// cleanup notifs
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
std::erase_if(m_vNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
return CBox{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
}
@@ -205,7 +210,7 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
}
// Draw the notifications
if (m_dNotifications.size() == 0)
if (m_vNotifications.size() == 0)
return;
// Render to the monitor
@@ -220,8 +225,8 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
CBox damage = drawNotifications(pMonitor);
g_pHyprRenderer->damageBox(&damage);
g_pHyprRenderer->damageBox(&m_bLastDamage);
g_pHyprRenderer->damageBox(damage);
g_pHyprRenderer->damageBox(m_bLastDamage);
g_pCompositor->scheduleFrameForMonitor(pMonitor);
@@ -241,10 +246,14 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
CBox pMonBox = {0, 0, MONSIZE.x, MONSIZE.y};
g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f);
CTexPassElement::SRenderData data;
data.tex = m_pTexture;
data.box = {0, 0, MONSIZE.x, MONSIZE.y};
data.a = 1.F;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
}
bool CHyprNotificationOverlay::hasAny() {
return !m_dNotifications.empty();
return !m_vNotifications.empty();
}

View File

@@ -2,15 +2,14 @@
#include "../defines.hpp"
#include "../helpers/Timer.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Texture.hpp"
#include "../SharedDefs.hpp"
#include <deque>
#include <vector>
#include <cairo/cairo.h>
enum eIconBackend {
enum eIconBackend : uint8_t {
ICONS_BACKEND_NONE = 0,
ICONS_BACKEND_NF,
ICONS_BACKEND_FA
@@ -19,17 +18,17 @@ enum eIconBackend {
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>{"", "", "", "", "", ""}};
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},
CColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0},
CColor{255 / 255.0, 204 / 255.0, 153 / 255.0, 1.0},
CColor{128 / 255.0, 255 / 255.0, 128 / 255.0, 1.0},
CColor{0, 0, 0, 1.0}};
static const std::array<CHyprColor, ICON_NONE + 1> ICONS_COLORS = {CHyprColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0},
CHyprColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
CHyprColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
CHyprColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0},
CHyprColor{255 / 255.0, 204 / 255.0, 153 / 255.0, 1.0},
CHyprColor{128 / 255.0, 255 / 255.0, 128 / 255.0, 1.0},
CHyprColor{0, 0, 0, 1.0}};
struct SNotification {
std::string text = "";
CColor color;
CHyprColor color;
CTimer started;
float timeMs = 0;
eIcons icon = ICON_NONE;
@@ -42,23 +41,23 @@ class CHyprNotificationOverlay {
~CHyprNotificationOverlay();
void draw(PHLMONITOR pMonitor);
void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE, const float fontSize = 13.f);
void addNotification(const std::string& text, const CHyprColor& color, const float timeMs, const eIcons icon = ICON_NONE, const float fontSize = 13.f);
void dismissNotifications(const int amount);
bool hasAny();
private:
CBox drawNotifications(PHLMONITOR pMonitor);
CBox m_bLastDamage;
CBox drawNotifications(PHLMONITOR pMonitor);
CBox m_bLastDamage;
std::deque<std::unique_ptr<SNotification>> m_dNotifications;
std::vector<UP<SNotification>> m_vNotifications;
cairo_surface_t* m_pCairoSurface = nullptr;
cairo_t* m_pCairo = nullptr;
cairo_surface_t* m_pCairoSurface = nullptr;
cairo_t* m_pCairo = nullptr;
PHLMONITORREF m_pLastMonitor;
Vector2D m_vecLastSize = Vector2D(-1, -1);
PHLMONITORREF m_pLastMonitor;
Vector2D m_vecLastSize = Vector2D(-1, -1);
SP<CTexture> m_pTexture;
SP<CTexture> m_pTexture;
};
inline std::unique_ptr<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
inline UP<CHyprNotificationOverlay> g_pHyprNotificationOverlay;

View File

@@ -1,6 +1,5 @@
#include "Log.hpp"
#include "../defines.hpp"
#include "../Compositor.hpp"
#include "RollingLogFollow.hpp"
#include <fstream>
@@ -18,7 +17,7 @@ void Debug::close() {
logOfs.close();
}
void Debug::log(LogLevel level, std::string str) {
void Debug::log(eLogLevel level, std::string str) {
if (level == TRACE && !trace)
return;
@@ -26,6 +25,7 @@ void Debug::log(LogLevel level, std::string str) {
return;
std::string coloredStr = str;
//NOLINTBEGIN
switch (level) {
case LOG:
str = "[LOG] " + str;
@@ -53,13 +53,14 @@ void Debug::log(LogLevel level, std::string str) {
break;
default: break;
}
//NOLINTEND
rollingLog += str + "\n";
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
if (RollingLogFollow::Get().IsRunning())
RollingLogFollow::Get().AddLog(str);
if (SRollingLogFollow::get().isRunning())
SRollingLogFollow::get().addLog(str);
if (!disableLogs || !**disableLogs) {
// log to a file

View File

@@ -5,13 +5,11 @@
#include <fstream>
#include <chrono>
#include <mutex>
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp"
#define LOGMESSAGESIZE 1024
#define ROLLING_LOG_SIZE 4096
enum LogLevel {
enum eLogLevel : int8_t {
NONE = -1,
LOG = 0,
WARN,
@@ -21,6 +19,7 @@ enum LogLevel {
TRACE
};
// NOLINTNEXTLINE(readability-identifier-naming)
namespace Debug {
inline std::string logFile;
inline std::ofstream logOfs;
@@ -38,10 +37,11 @@ namespace Debug {
void close();
//
void log(LogLevel level, std::string str);
void log(eLogLevel level, std::string str);
template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
//NOLINTNEXTLINE
void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {
std::lock_guard<std::mutex> guard(logMutex);
if (level == TRACE && !trace)

View File

@@ -2,8 +2,9 @@
#include <shared_mutex>
// NOLINTNEXTLINE(readability-identifier-naming)
namespace Debug {
struct RollingLogFollow {
struct SRollingLogFollow {
std::unordered_map<int, std::string> socketToRollingLogFollowQueue;
std::shared_mutex m;
bool running = false;
@@ -15,12 +16,12 @@ namespace Debug {
return socketToRollingLogFollowQueue[socket].empty();
}
std::string DebugInfo() {
std::string debugInfo() {
std::shared_lock<std::shared_mutex> r(m);
return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size());
}
std::string GetLog(int socket) {
std::string getLog(int socket) {
std::unique_lock<std::shared_mutex> w(m);
const std::string ret = socketToRollingLogFollowQueue[socket];
@@ -29,7 +30,7 @@ namespace Debug {
return ret;
};
void AddLog(std::string log) {
void addLog(const std::string& log) {
std::unique_lock<std::shared_mutex> w(m);
running = true;
std::vector<int> to_erase;
@@ -37,26 +38,26 @@ namespace Debug {
socketToRollingLogFollowQueue[p.first] += log + "\n";
}
bool IsRunning() {
bool isRunning() {
std::shared_lock<std::shared_mutex> r(m);
return running;
}
void StopFor(int socket) {
void stopFor(int socket) {
std::unique_lock<std::shared_mutex> w(m);
socketToRollingLogFollowQueue.erase(socket);
if (socketToRollingLogFollowQueue.empty())
running = false;
}
void StartFor(int socket) {
void startFor(int socket) {
std::unique_lock<std::shared_mutex> w(m);
socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
running = true;
}
static RollingLogFollow& Get() {
static RollingLogFollow instance;
static SRollingLogFollow& get() {
static SRollingLogFollow instance;
static std::mutex gm;
std::lock_guard<std::mutex> lock(gm);
return instance;

View File

@@ -1,3 +1,5 @@
#pragma once
#include "includes.hpp"
#include "debug/Log.hpp"
#include "helpers/Color.hpp"

View File

@@ -1,5 +1,5 @@
#pragma once
#include "../macros.hpp"
#include "../helpers/memory/Memory.hpp"
class CWorkspace;
class CWindow;
class CLayerSurface;

38
src/desktop/LayerRule.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include <re2/re2.h>
#include "LayerRule.hpp"
#include <unordered_set>
#include <algorithm>
#include "../debug/Log.hpp"
static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order"};
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : targetNamespace(ns_), rule(rule_) {
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
if (!VALID)
return;
if (rule == "noanim")
ruleType = RULE_NOANIM;
else if (rule == "blur")
ruleType = RULE_BLUR;
else if (rule == "blurpopups")
ruleType = RULE_BLURPOPUPS;
else if (rule == "dimaround")
ruleType = RULE_DIMAROUND;
else if (rule.starts_with("ignorealpha"))
ruleType = RULE_IGNOREALPHA;
else if (rule.starts_with("ignorezero"))
ruleType = RULE_IGNOREZERO;
else if (rule.starts_with("xray"))
ruleType = RULE_XRAY;
else if (rule.starts_with("animation"))
ruleType = RULE_ANIMATION;
else if (rule.starts_with("order"))
ruleType = RULE_ORDER;
else {
Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
ruleType = RULE_INVALID;
}
}

31
src/desktop/LayerRule.hpp Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <cstdint>
#include "Rule.hpp"
class CLayerRule {
public:
CLayerRule(const std::string& rule, const std::string& targetNS);
enum eRuleType : uint8_t {
RULE_INVALID = 0,
RULE_NOANIM,
RULE_BLUR,
RULE_BLURPOPUPS,
RULE_DIMAROUND,
RULE_IGNOREALPHA,
RULE_IGNOREZERO,
RULE_XRAY,
RULE_ANIMATION,
RULE_ORDER,
RULE_ZUMBA,
};
eRuleType ruleType = RULE_INVALID;
const std::string targetNamespace;
const std::string rule;
CRuleRegexContainer targetNamespaceRegex;
};

View File

@@ -4,11 +4,18 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../managers/AnimationManager.hpp"
#include "../render/Renderer.hpp"
#include "../config/ConfigManager.hpp"
#include "../helpers/Monitor.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/EventManager.hpp"
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
auto pMonitor = resource->monitor.empty() ? g_pCompositor->getMonitorFromCursor() : g_pCompositor->getMonitorFromName(resource->monitor);
auto pMonitor = resource->monitor.empty() ? g_pCompositor->m_pLastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->monitor);
pLS->surface->assign(resource->surface.lock(), pLS);
@@ -24,23 +31,21 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
pLS->szNamespace = resource->layerNamespace;
pLS->layer = resource->current.layer;
pLS->popupHead = std::make_unique<CPopup>(pLS);
pLS->monitor = pMonitor;
pLS->layer = resource->current.layer;
pLS->popupHead = makeUnique<CPopup>(pLS);
pLS->popupHead->m_pSelf = pLS->popupHead;
pLS->monitor = pMonitor;
pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS);
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
pLS->alpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->realPosition.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->realSize.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->alpha.registerVar();
pLS->realPosition.registerVar();
pLS->realSize.registerVar();
g_pAnimationManager->createAnimation(0.f, pLS->alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->registerCallbacks();
pLS->alpha.setValueAndWarp(0.f);
pLS->alpha->setValueAndWarp(0.f);
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->layerNamespace, (int)pLS->layer, pMonitor->szName);
@@ -48,7 +53,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
}
void CLayerSurface::registerCallbacks() {
alpha.setUpdateCallback([this](void*) {
alpha->setUpdateCallback([this](auto) {
if (dimAround)
g_pHyprRenderer->damageMonitor(monitor.lock());
});
@@ -93,7 +98,7 @@ void CLayerSurface::onDestroy() {
onUnmap();
} else {
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
alpha.setValueAndWarp(0.f);
alpha->setValueAndWarp(0.f);
fadingOut = true;
g_pCompositor->addToFadingOutSafe(self.lock());
}
@@ -110,7 +115,7 @@ void CLayerSurface::onDestroy() {
// and damage
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pHyprRenderer->damageBox(geomFixed);
}
readyToDelete = true;
@@ -174,7 +179,7 @@ void CLayerSurface::onMap() {
position = Vector2D(geometry.x, geometry.y);
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pHyprRenderer->damageBox(geomFixed);
const bool FULLSCREEN = PMONITOR->activeWorkspace && PMONITOR->activeWorkspace->m_bHasFullscreenWindow && PMONITOR->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
@@ -210,7 +215,7 @@ void CLayerSurface::onUnmap() {
}
// make a snapshot and start fade
g_pHyprOpenGL->makeLayerSnapshot(self.lock());
g_pHyprRenderer->makeLayerSnapshot(self.lock());
startAnimation(false);
@@ -235,13 +240,13 @@ void CLayerSurface::onUnmap() {
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pHyprRenderer->damageBox(geomFixed);
geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.size.x,
(int)layerSurface->surface->current.size.y};
g_pHyprRenderer->damageBox(&geomFixed);
g_pHyprRenderer->damageBox(geomFixed);
g_pInputManager->sendMotionEventsToFocused();
g_pInputManager->simulateMouseMovement();
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
}
@@ -270,7 +275,7 @@ void CLayerSurface::onCommit() {
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
CBox geomFixed = {geometry.x, geometry.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pHyprRenderer->damageBox(geomFixed);
if (layerSurface->current.committed != 0) {
if (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
@@ -307,17 +312,17 @@ void CLayerSurface::onCommit() {
}
}
if (realPosition.goal() != geometry.pos()) {
if (realPosition.isBeingAnimated())
realPosition = geometry.pos();
if (realPosition->goal() != geometry.pos()) {
if (realPosition->isBeingAnimated())
*realPosition = geometry.pos();
else
realPosition.setValueAndWarp(geometry.pos());
realPosition->setValueAndWarp(geometry.pos());
}
if (realSize.goal() != geometry.size()) {
if (realSize.isBeingAnimated())
realSize = geometry.size();
if (realSize->goal() != geometry.size()) {
if (realSize->isBeingAnimated())
*realSize = geometry.size();
else
realSize.setValueAndWarp(geometry.size());
realSize->setValueAndWarp(geometry.size());
}
if (mapped && (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
@@ -327,7 +332,7 @@ void CLayerSurface::onCommit() {
nullptr);
if (!WASLASTFOCUS && popupHead) {
popupHead->breadthfirst(
[&WASLASTFOCUS](CPopup* popup, void* data) {
[&WASLASTFOCUS](WP<CPopup> popup, void* data) {
WASLASTFOCUS = WASLASTFOCUS || (popup->m_pWLSurface && g_pSeatManager->state.keyboardFocus == popup->m_pWLSurface->resource());
},
nullptr);
@@ -378,54 +383,73 @@ void CLayerSurface::applyRules() {
animationStyle.reset();
for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) {
if (rule.rule == "noanim")
noAnimations = true;
else if (rule.rule == "blur")
forceBlur = true;
else if (rule.rule == "blurpopups")
forceBlurPopups = true;
else if (rule.rule.starts_with("ignorealpha") || rule.rule.starts_with("ignorezero")) {
const auto FIRST_SPACE_POS = rule.rule.find_first_of(' ');
std::string alphaValue = "";
if (FIRST_SPACE_POS != std::string::npos)
alphaValue = rule.rule.substr(FIRST_SPACE_POS + 1);
switch (rule->ruleType) {
case CLayerRule::RULE_NOANIM: {
noAnimations = true;
break;
}
case CLayerRule::RULE_BLUR: {
forceBlur = true;
break;
}
case CLayerRule::RULE_BLURPOPUPS: {
forceBlurPopups = true;
break;
}
case CLayerRule::RULE_IGNOREALPHA:
case CLayerRule::RULE_IGNOREZERO: {
const auto FIRST_SPACE_POS = rule->rule.find_first_of(' ');
std::string alphaValue = "";
if (FIRST_SPACE_POS != std::string::npos)
alphaValue = rule->rule.substr(FIRST_SPACE_POS + 1);
try {
ignoreAlpha = true;
if (!alphaValue.empty())
ignoreAlphaValue = std::stof(alphaValue);
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
} else if (rule.rule == "dimaround") {
dimAround = true;
} else if (rule.rule.starts_with("xray")) {
CVarList vars{rule.rule, 0, ' '};
try {
xray = configStringToInt(vars[1]);
} catch (...) {}
} else if (rule.rule.starts_with("animation")) {
CVarList vars{rule.rule, 2, 's'};
animationStyle = vars[1];
} else if (rule.rule.starts_with("order")) {
CVarList vars{rule.rule, 2, 's'};
try {
order = std::stoi(vars[1]);
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
try {
ignoreAlpha = true;
if (!alphaValue.empty())
ignoreAlphaValue = std::stof(alphaValue);
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
break;
}
case CLayerRule::RULE_DIMAROUND: {
dimAround = true;
break;
}
case CLayerRule::RULE_XRAY: {
CVarList vars{rule->rule, 0, ' '};
try {
xray = configStringToInt(vars[1]).value_or(false);
} catch (...) {}
break;
}
case CLayerRule::RULE_ANIMATION: {
CVarList vars{rule->rule, 2, 's'};
animationStyle = vars[1];
break;
}
case CLayerRule::RULE_ORDER: {
CVarList vars{rule->rule, 2, 's'};
try {
order = std::stoi(vars[1]);
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
break;
}
default: break;
}
}
}
void CLayerSurface::startAnimation(bool in, bool instant) {
const auto ANIMSTYLE = animationStyle.value_or(realPosition.m_pConfig->pValues->internalStyle);
if (in) {
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn");
realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"));
} else {
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut");
realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
}
const auto ANIMSTYLE = animationStyle.value_or(realPosition->getStyle());
if (ANIMSTYLE.starts_with("slide")) {
// get closest edge
const auto MIDDLE = geometry.middle();
@@ -466,9 +490,9 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
}
}
realSize.setValueAndWarp(geometry.size());
alpha.setValueAndWarp(in ? 0.f : 1.f);
alpha = in ? 1.f : 0.f;
realSize->setValueAndWarp(geometry.size());
alpha->setValueAndWarp(in ? 0.f : 1.f);
*alpha = in ? 1.f : 0.f;
Vector2D prePos;
@@ -493,11 +517,11 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
}
if (in) {
realPosition.setValueAndWarp(prePos);
realPosition = geometry.pos();
realPosition->setValueAndWarp(prePos);
*realPosition = geometry.pos();
} else {
realPosition.setValueAndWarp(geometry.pos());
realPosition = prePos;
realPosition->setValueAndWarp(geometry.pos());
*realPosition = prePos;
}
} else if (ANIMSTYLE.starts_with("popin")) {
@@ -516,25 +540,25 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
const auto GOALSIZE = (geometry.size() * minPerc).clamp({5, 5});
const auto GOALPOS = geometry.pos() + (geometry.size() - GOALSIZE) / 2.f;
alpha.setValueAndWarp(in ? 0.f : 1.f);
alpha = in ? 1.f : 0.f;
alpha->setValueAndWarp(in ? 0.f : 1.f);
*alpha = in ? 1.f : 0.f;
if (in) {
realSize.setValueAndWarp(GOALSIZE);
realPosition.setValueAndWarp(GOALPOS);
realSize = geometry.size();
realPosition = geometry.pos();
realSize->setValueAndWarp(GOALSIZE);
realPosition->setValueAndWarp(GOALPOS);
*realSize = geometry.size();
*realPosition = geometry.pos();
} else {
realSize.setValueAndWarp(geometry.size());
realPosition.setValueAndWarp(geometry.pos());
realSize = GOALSIZE;
realPosition = GOALPOS;
realSize->setValueAndWarp(geometry.size());
realPosition->setValueAndWarp(geometry.pos());
*realSize = GOALSIZE;
*realPosition = GOALPOS;
}
} else {
// fade
realPosition.setValueAndWarp(geometry.pos());
realSize.setValueAndWarp(geometry.size());
alpha = in ? 1.f : 0.f;
realPosition->setValueAndWarp(geometry.pos());
realSize->setValueAndWarp(geometry.size());
*alpha = in ? 1.f : 0.f;
}
if (!in)
@@ -545,7 +569,7 @@ bool CLayerSurface::isFadedOut() {
if (!fadingOut)
return false;
return !realPosition.isBeingAnimated() && !realSize.isBeingAnimated() && !alpha.isBeingAnimated();
return !realPosition->isBeingAnimated() && !realSize->isBeingAnimated() && !alpha->isBeingAnimated();
}
int CLayerSurface::popupsCount() {
@@ -553,7 +577,7 @@ int CLayerSurface::popupsCount() {
return 0;
int no = -1; // we have one dummy
popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no);
popupHead->breadthfirst([](WP<CPopup> p, void* data) { *(int*)data += 1; }, &no);
return no;
}

View File

@@ -5,11 +5,6 @@
#include "WLSurface.hpp"
#include "../helpers/AnimatedVariable.hpp"
struct SLayerRule {
std::string targetNamespace = "";
std::string rule = "";
};
class CLayerShellResource;
class CLayerSurface {
@@ -22,17 +17,17 @@ class CLayerSurface {
public:
~CLayerSurface();
void applyRules();
void startAnimation(bool in, bool instant = false);
bool isFadedOut();
int popupsCount();
void applyRules();
void startAnimation(bool in, bool instant = false);
bool isFadedOut();
int popupsCount();
CAnimatedVariable<Vector2D> realPosition;
CAnimatedVariable<Vector2D> realSize;
CAnimatedVariable<float> alpha;
PHLANIMVAR<Vector2D> realPosition;
PHLANIMVAR<Vector2D> realSize;
PHLANIMVAR<float> alpha;
WP<CLayerShellResource> layerSurface;
wl_list link;
WP<CLayerShellResource> layerSurface;
wl_list link;
// the header providing the enum type cannot be imported here
int interactivity = 0;
@@ -64,7 +59,7 @@ class CLayerSurface {
CBox geometry = {0, 0, 0, 0};
Vector2D position;
std::string szNamespace = "";
std::unique_ptr<CPopup> popupHead;
UP<CPopup> popupHead;
void onDestroy();
void onMap();

View File

@@ -6,6 +6,10 @@
#include "../protocols/core/Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
#include "../render/OpenGL.hpp"
#include <ranges>
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
@@ -16,13 +20,11 @@ CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) {
initAllSignals();
}
CPopup::CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner) : m_pParent(pOwner), m_pResource(popup) {
CPopup::CPopup(SP<CXDGPopupResource> popup, WP<CPopup> pOwner) :
m_pWindowOwner(pOwner->m_pWindowOwner), m_pLayerOwner(pOwner->m_pLayerOwner), m_pParent(pOwner), m_pResource(popup) {
m_pWLSurface = CWLSurface::create();
m_pWLSurface->assign(popup->surface->surface.lock(), this);
m_pLayerOwner = pOwner->m_pLayerOwner;
m_pWindowOwner = pOwner->m_pWindowOwner;
m_vLastSize = popup->surface->current.geometry.size();
reposition();
@@ -57,7 +59,8 @@ void CPopup::initAllSignals() {
}
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
const auto POPUP = m_vChildren.emplace_back(makeShared<CPopup>(popup, this)).get();
const auto& POPUP = m_vChildren.emplace_back(makeShared<CPopup>(popup, m_pSelf));
POPUP->m_pSelf = POPUP;
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
}
@@ -82,13 +85,14 @@ void CPopup::onMap() {
CBox box = m_pWLSurface->resource()->extends();
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
m_vLastPos = coordsRelativeToParent();
g_pInputManager->simulateMouseMovement();
m_pSubsurfaceHead = std::make_unique<CSubsurface>(this);
m_pSubsurfaceHead = makeUnique<CSubsurface>(m_pSelf);
m_pSubsurfaceHead->m_pSelf = m_pSubsurfaceHead;
//unconstrain();
sendScale();
@@ -116,7 +120,7 @@ void CPopup::onUnmap() {
CBox box = m_pWLSurface->resource()->extends();
box.translate(COORDS).expand(4);
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
m_pSubsurfaceHead.reset();
@@ -125,19 +129,20 @@ void CPopup::onUnmap() {
// damage all children
breadthfirst(
[](CPopup* p, void* data) {
[](WP<CPopup> p, void* data) {
if (!p->m_pResource)
return;
auto box = CBox{p->coordsGlobal(), p->size()};
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
},
nullptr);
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
// TODO: probably refocus, but without a motion event?
// const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
if (WASLASTFOCUS)
g_pInputManager->simulateMouseMovement();
// if (WASLASTFOCUS)
// g_pInputManager->simulateMouseMovement();
}
void CPopup::onCommit(bool ignoreSiblings) {
@@ -169,10 +174,10 @@ void CPopup::onCommit(bool ignoreSiblings) {
if (m_vLastSize != m_pResource->surface->surface->current.size || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) {
CBox box = {localToGlobal(m_vLastPos), m_vLastSize};
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
m_vLastSize = m_pResource->surface->surface->current.size;
box = {COORDS, m_vLastSize};
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
m_vLastPos = COORDSLOCAL;
}
@@ -222,7 +227,7 @@ Vector2D CPopup::coordsRelativeToParent() {
if (!m_pResource)
return {};
CPopup* current = this;
WP<CPopup> current = m_pSelf;
offset -= current->m_pResource->surface->current.geometry.pos();
while (current->m_pParent && current->m_pResource) {
@@ -246,16 +251,16 @@ Vector2D CPopup::localToGlobal(const Vector2D& rel) {
Vector2D CPopup::t1ParentCoords() {
if (!m_pWindowOwner.expired())
return m_pWindowOwner->m_vRealPosition.value();
return m_pWindowOwner->m_vRealPosition->value();
if (!m_pLayerOwner.expired())
return m_pLayerOwner->realPosition.value();
return m_pLayerOwner->realPosition->value();
ASSERT(false);
return {};
}
void CPopup::recheckTree() {
CPopup* curr = this;
WP<CPopup> curr = m_pSelf;
while (curr->m_pParent) {
curr = curr->m_pParent;
}
@@ -295,17 +300,17 @@ bool CPopup::visible() {
return false;
}
void CPopup::bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data) {
void CPopup::bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data) {
for (auto const& n : nodes) {
fn(n, data);
}
std::vector<CPopup*> nodes2;
std::vector<WP<CPopup>> nodes2;
nodes2.reserve(nodes.size() * 2);
for (auto const& n : nodes) {
for (auto const& c : n->m_vChildren) {
nodes2.push_back(c.get());
nodes2.push_back(c->m_pSelf);
}
}
@@ -313,15 +318,15 @@ void CPopup::bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPop
bfHelper(nodes2, fn, data);
}
void CPopup::breadthfirst(std::function<void(CPopup*, void*)> fn, void* data) {
std::vector<CPopup*> popups;
popups.push_back(this);
void CPopup::breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data) {
std::vector<WP<CPopup>> popups;
popups.push_back(m_pSelf);
bfHelper(popups, fn, data);
}
CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<CPopup*> popups;
breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups);
WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<WP<CPopup>> popups;
breadthfirst([](WP<CPopup> popup, void* data) { ((std::vector<WP<CPopup>>*)data)->push_back(popup); }, &popups);
for (auto const& p : popups | std::views::reverse) {
if (!p->m_pResource || !p->m_bMapped)
@@ -343,5 +348,5 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
}
}
return nullptr;
return {};
}

View File

@@ -1,9 +1,9 @@
#pragma once
#include <vector>
#include <memory>
#include "Subsurface.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../helpers/memory/Memory.hpp"
class CXDGPopupResource;
@@ -14,7 +14,7 @@ class CPopup {
CPopup(PHLLS pOwner);
// real nodes
CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner);
CPopup(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
~CPopup();
@@ -36,11 +36,13 @@ class CPopup {
bool visible();
// will also loop over this node
void breadthfirst(std::function<void(CPopup*, void*)> fn, void* data);
CPopup* at(const Vector2D& globalCoords, bool allowsInput = false);
void breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data);
WP<CPopup> at(const Vector2D& globalCoords, bool allowsInput = false);
//
SP<CWLSurface> m_pWLSurface;
WP<CPopup> m_pSelf;
bool m_bMapped = false;
private:
// T1 owners, each popup has to have one of these
@@ -48,7 +50,7 @@ class CPopup {
PHLLSREF m_pLayerOwner;
// T2 owners
CPopup* m_pParent = nullptr;
WP<CPopup> m_pParent;
WP<CXDGPopupResource> m_pResource;
@@ -57,12 +59,11 @@ class CPopup {
bool m_bRequestedReposition = false;
bool m_bInert = false;
bool m_bMapped = false;
bool m_bInert = false;
//
std::vector<SP<CPopup>> m_vChildren;
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
std::vector<SP<CPopup>> m_vChildren;
UP<CSubsurface> m_pSubsurfaceHead;
struct {
CHyprSignalListener newPopup;
@@ -81,5 +82,5 @@ class CPopup {
Vector2D localToGlobal(const Vector2D& rel);
Vector2D t1ParentCoords();
static void bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data);
static void bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data);
};

22
src/desktop/Rule.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include <re2/re2.h>
#include "../helpers/memory/Memory.hpp"
#include "Rule.hpp"
#include "../debug/Log.hpp"
CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) {
const bool NEGATIVE = regex_.starts_with("negative:");
negative = NEGATIVE;
regex = makeUnique<RE2>(NEGATIVE ? regex_.substr(9) : regex_);
// TODO: maybe pop an error?
if (!regex->ok())
Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_);
}
bool CRuleRegexContainer::passes(const std::string& str) const {
if (!regex)
return false;
return RE2::FullMatch(str, *regex) != negative;
}

21
src/desktop/Rule.hpp Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include <hyprutils/memory/UniquePtr.hpp>
//NOLINTNEXTLINE
namespace re2 {
class RE2;
};
class CRuleRegexContainer {
public:
CRuleRegexContainer() = default;
CRuleRegexContainer(const std::string& regex);
bool passes(const std::string& str) const;
private:
Hyprutils::Memory::CUniquePointer<re2::RE2> regex;
bool negative = false;
};

View File

@@ -4,13 +4,15 @@
#include "../config/ConfigValue.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/core/Subcompositor.hpp"
#include "../render/Renderer.hpp"
#include "../managers/input/InputManager.hpp"
CSubsurface::CSubsurface(PHLWINDOW pOwner) : m_pWindowParent(pOwner) {
initSignals();
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
}
CSubsurface::CSubsurface(CPopup* pOwner) : m_pPopupParent(pOwner) {
CSubsurface::CSubsurface(WP<CPopup> pOwner) : m_pPopupParent(pOwner) {
initSignals();
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
}
@@ -22,17 +24,13 @@ CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner
initExistingSubsurfaces(pSubsurface->surface.lock());
}
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, CPopup* pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) {
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) {
m_pWLSurface = CWLSurface::create();
m_pWLSurface->assign(pSubsurface->surface.lock(), this);
initSignals();
initExistingSubsurfaces(pSubsurface->surface.lock());
}
CSubsurface::~CSubsurface() {
;
}
void CSubsurface::initSignals() {
if (m_pSubsurface) {
listeners.commitSubsurface = m_pSubsurface->surface->events.commit.registerListener([this](std::any d) { onCommit(); });
@@ -99,11 +97,20 @@ void CSubsurface::onCommit() {
checkSiblingDamage();
if (m_vLastSize != m_pWLSurface->resource()->current.size) {
CBox box{COORDS, m_vLastSize};
g_pHyprRenderer->damageBox(&box);
m_vLastSize = m_pWLSurface->resource()->current.size;
box = {COORDS, m_vLastSize};
g_pHyprRenderer->damageBox(&box);
// TODO: fix this
// CBox box{COORDS, m_vLastSize};
// g_pHyprRenderer->damageBox(box);
// m_vLastSize = m_pWLSurface->resource()->current.size;
// box = {COORDS, m_vLastSize};
// g_pHyprRenderer->damageBox(box);
CBox box;
if (m_pPopupParent)
box = m_pPopupParent->m_pWLSurface->getSurfaceBoxGlobal().value_or(CBox{});
else if (m_pWindowParent)
box = m_pWindowParent->getWindowMainSurfaceBox();
g_pHyprRenderer->damageBox(box);
}
}
@@ -121,16 +128,18 @@ void CSubsurface::onDestroy() {
}
void CSubsurface::onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface) {
CSubsurface* PSUBSURFACE = nullptr;
WP<CSubsurface> PSUBSURFACE;
if (!m_pWindowParent.expired())
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pWindowParent.lock())).get();
PSUBSURFACE = m_vChildren.emplace_back(makeUnique<CSubsurface>(pSubsurface, m_pWindowParent.lock()));
else if (m_pPopupParent)
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pPopupParent)).get();
PSUBSURFACE = m_vChildren.emplace_back(makeUnique<CSubsurface>(pSubsurface, m_pPopupParent));
PSUBSURFACE->m_pSelf = PSUBSURFACE;
ASSERT(PSUBSURFACE);
PSUBSURFACE->m_pParent = this;
PSUBSURFACE->m_pParent = m_pSelf;
}
void CSubsurface::onMap() {
@@ -139,7 +148,7 @@ void CSubsurface::onMap() {
const auto COORDS = coordsGlobal();
CBox box{COORDS, m_vLastSize};
box.expand(4);
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
if (!m_pWindowParent.expired())
m_pWindowParent->updateSurfaceScaleTransformDetails();
@@ -149,7 +158,7 @@ void CSubsurface::onUnmap() {
const auto COORDS = coordsGlobal();
CBox box{COORDS, m_vLastSize};
box.expand(4);
g_pHyprRenderer->damageBox(&box);
g_pHyprRenderer->damageBox(box);
if (m_pWLSurface->resource() == g_pCompositor->m_pLastFocus)
g_pInputManager->releaseAllMouseButtons();
@@ -169,7 +178,7 @@ Vector2D CSubsurface::coordsGlobal() {
Vector2D coords = coordsRelativeToParent();
if (!m_pWindowParent.expired())
coords += m_pWindowParent->m_vRealPosition.value();
coords += m_pWindowParent->m_vRealPosition->value();
else if (m_pPopupParent)
coords += m_pPopupParent->coordsGlobal();

View File

@@ -11,28 +11,30 @@ class CSubsurface {
public:
// root dummy nodes
CSubsurface(PHLWINDOW pOwner);
CSubsurface(CPopup* pOwner);
CSubsurface(WP<CPopup> pOwner);
// real nodes
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, CPopup* pOwner);
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner);
~CSubsurface();
~CSubsurface() = default;
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
Vector2D size();
Vector2D size();
void onCommit();
void onDestroy();
void onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface);
void onMap();
void onUnmap();
void onCommit();
void onDestroy();
void onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface);
void onMap();
void onUnmap();
bool visible();
bool visible();
void recheckDamageForSubsurfaces();
void recheckDamageForSubsurfaces();
WP<CSubsurface> m_pSelf;
private:
struct {
@@ -48,16 +50,16 @@ class CSubsurface {
Vector2D m_vLastSize = {};
// if nullptr, means it's a dummy node
CSubsurface* m_pParent = nullptr;
WP<CSubsurface> m_pParent;
PHLWINDOWREF m_pWindowParent;
CPopup* m_pPopupParent = nullptr;
PHLWINDOWREF m_pWindowParent;
WP<CPopup> m_pPopupParent;
std::vector<std::unique_ptr<CSubsurface>> m_vChildren;
std::vector<UP<CSubsurface>> m_vChildren;
bool m_bInert = false;
bool m_bInert = false;
void initSignals();
void initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface);
void checkSiblingDamage();
};
void initSignals();
void initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface);
void checkSiblingDamage();
};

View File

@@ -1,7 +1,9 @@
#include "WLSurface.hpp"
#include "../Compositor.hpp"
#include "LayerSurface.hpp"
#include "../desktop/Window.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/LayerShell.hpp"
#include "../render/Renderer.hpp"
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface) {
m_pResource = pSurface;
@@ -72,7 +74,7 @@ Vector2D CWLSurface::correctSmallVec() const {
const auto SIZE = getViewporterCorrectedSize();
const auto O = m_pWindowOwner.lock();
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize.value() / O->m_vReportedSize);
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize->value() / O->m_vReportedSize);
}
Vector2D CWLSurface::correctSmallVecBuf() const {

View File

@@ -84,7 +84,11 @@ class CWLSurface {
static SP<CWLSurface> fromResource(SP<CWLSurfaceResource> pSurface);
// used by the alpha-modifier protocol
float m_pAlphaModifier = 1.F;
float m_fAlphaModifier = 1.F;
// used by the hyprland-surface protocol
float m_fOverallOpacity = 1.F;
CRegion m_visibleRegion;
struct {
CSignal destroy;
@@ -116,4 +120,5 @@ class CWLSurface {
} listeners;
friend class CPointerConstraint;
};
friend class CXxColorManagerV4;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
#pragma once
#include <deque>
#include <vector>
#include <string>
#include <optional>
#include "../config/ConfigDataValues.hpp"
#include "../defines.hpp"
#include "../helpers/AnimatedVariable.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/signal/Signal.hpp"
@@ -12,23 +12,25 @@
#include "../macros.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../render/decorations/IHyprWindowDecoration.hpp"
#include "../render/Transformer.hpp"
#include "DesktopTypes.hpp"
#include "Popup.hpp"
#include "Subsurface.hpp"
#include "WLSurface.hpp"
#include "Workspace.hpp"
#include "WindowRule.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
enum eIdleInhibitMode {
enum eIdleInhibitMode : uint8_t {
IDLEINHIBIT_NONE = 0,
IDLEINHIBIT_ALWAYS,
IDLEINHIBIT_FULLSCREEN,
IDLEINHIBIT_FOCUS
};
enum eGroupRules {
enum eGroupRules : uint8_t {
// effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0,
GROUP_SET = 1 << 0, // Open as new group or add to focused group
@@ -40,7 +42,7 @@ enum eGroupRules {
GROUP_OVERRIDE = 1 << 6, // Override other rules
};
enum eGetWindowProperties {
enum eGetWindowProperties : uint8_t {
WINDOW_ONLY = 0,
RESERVED_EXTENTS = 1 << 0,
INPUT_EXTENTS = 1 << 1,
@@ -50,12 +52,13 @@ enum eGetWindowProperties {
USE_PROP_TILED = 1 << 5,
};
enum eSuppressEvents {
enum eSuppressEvents : uint8_t {
SUPPRESS_NONE = 0,
SUPPRESS_FULLSCREEN = 1 << 0,
SUPPRESS_MAXIMIZE = 1 << 1,
SUPPRESS_ACTIVATE = 1 << 2,
SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3,
SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4,
};
class IWindowTransformer;
@@ -64,7 +67,7 @@ struct SAlphaValue {
float m_fAlpha;
bool m_bOverride;
float applyAlpha(float alpha) {
float applyAlpha(float alpha) const {
if (m_bOverride)
return m_fAlpha;
else
@@ -72,8 +75,8 @@ struct SAlphaValue {
};
};
enum eOverridePriority {
PRIORITY_LAYOUT,
enum eOverridePriority : uint8_t {
PRIORITY_LAYOUT = 0,
PRIORITY_WORKSPACE_RULE,
PRIORITY_WINDOW_RULE,
PRIORITY_SET_PROP,
@@ -85,9 +88,7 @@ class CWindowOverridableVar {
CWindowOverridableVar(T const& value, eOverridePriority priority) {
values[priority] = value;
}
CWindowOverridableVar(T const& value) {
defaultValue = value;
}
CWindowOverridableVar(T const& value) : defaultValue{value} {}
CWindowOverridableVar() = default;
~CWindowOverridableVar() = default;
@@ -184,8 +185,12 @@ struct SWindowData {
CWindowOverridableVar<bool> renderUnfocused = false;
CWindowOverridableVar<int> rounding;
CWindowOverridableVar<float> roundingPower;
CWindowOverridableVar<int> borderSize;
CWindowOverridableVar<float> scrollMouse;
CWindowOverridableVar<float> scrollTouchpad;
CWindowOverridableVar<std::string> animationStyle;
CWindowOverridableVar<Vector2D> maxSize;
CWindowOverridableVar<Vector2D> minSize;
@@ -194,32 +199,12 @@ struct SWindowData {
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
};
struct SWindowRule {
std::string szRule;
std::string szValue;
bool v2 = false;
std::string szTitle;
std::string szClass;
std::string szInitialTitle;
std::string szInitialClass;
std::string szTag;
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
};
struct SInitialWorkspaceToken {
PHLWINDOWREF primaryOwner;
std::string workspace;
};
struct sFullscreenState {
struct SFullscreenState {
eFullscreenMode internal = FSMODE_NONE;
eFullscreenMode client = FSMODE_NONE;
};
@@ -250,8 +235,8 @@ class CWindow {
Vector2D m_vSize = Vector2D(0, 0);
// this is the real position and size used to draw the thing
CAnimatedVariable<Vector2D> m_vRealPosition;
CAnimatedVariable<Vector2D> m_vRealSize;
PHLANIMVAR<Vector2D> m_vRealPosition;
PHLANIMVAR<Vector2D> m_vRealSize;
// for not spamming the protocols
Vector2D m_vReportedPosition;
@@ -278,7 +263,7 @@ class CWindow {
bool m_bIsFloating = false;
bool m_bDraggingTiled = false; // for dragging around tiled windows
bool m_bWasMaximized = false;
sFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
SFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
std::string m_szTitle = "";
std::string m_szClass = "";
std::string m_szInitialTitle = "";
@@ -305,33 +290,37 @@ class CWindow {
bool m_bNoInitialFocus = false;
// Fullscreen and Maximize
bool m_bWantsInitialFullscreen = false;
bool m_bWantsInitialFullscreen = false;
MONITORID m_iWantsInitialFullscreenMonitor = MONITOR_INVALID;
// bitfield eSuppressEvents
uint64_t m_eSuppressedEvents = SUPPRESS_NONE;
// desktop components
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
std::unique_ptr<CPopup> m_pPopupHead;
UP<CSubsurface> m_pSubsurfaceHead;
UP<CPopup> m_pPopupHead;
// Animated border
CGradientValueData m_cRealBorderColor = {0};
CGradientValueData m_cRealBorderColorPrevious = {0};
CAnimatedVariable<float> m_fBorderFadeAnimationProgress;
CAnimatedVariable<float> m_fBorderAngleAnimationProgress;
CGradientValueData m_cRealBorderColor = {0};
CGradientValueData m_cRealBorderColorPrevious = {0};
PHLANIMVAR<float> m_fBorderFadeAnimationProgress;
PHLANIMVAR<float> m_fBorderAngleAnimationProgress;
// Fade in-out
CAnimatedVariable<float> m_fAlpha;
bool m_bFadingOut = false;
bool m_bReadyToDelete = false;
Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in
Vector2D m_vOriginalClosedSize; // drawing the closing animations
SBoxExtents m_eOriginalClosedExtents;
bool m_bAnimatingIn = false;
PHLANIMVAR<float> m_fAlpha;
bool m_bFadingOut = false;
bool m_bReadyToDelete = false;
Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in
Vector2D m_vOriginalClosedSize; // drawing the closing animations
SBoxExtents m_eOriginalClosedExtents;
bool m_bAnimatingIn = false;
// For pinned (sticky) windows
bool m_bPinned = false;
// For preserving pinned state when fullscreening a pinned window
bool m_bPinFullscreened = false;
// urgency hint
bool m_bIsUrgent = false;
@@ -340,27 +329,28 @@ class CWindow {
// Window decorations
// TODO: make this a SP.
std::deque<std::unique_ptr<IHyprWindowDecoration>> m_dWindowDecorations;
std::vector<IHyprWindowDecoration*> m_vDecosToRemove;
std::vector<UP<IHyprWindowDecoration>> m_dWindowDecorations;
std::vector<IHyprWindowDecoration*> m_vDecosToRemove;
// Special render data, rules, etc
SWindowData m_sWindowData;
// Transformers
std::vector<std::unique_ptr<IWindowTransformer>> m_vTransformers;
std::vector<UP<IWindowTransformer>> m_vTransformers;
// for alpha
CAnimatedVariable<float> m_fActiveInactiveAlpha;
PHLANIMVAR<float> m_fActiveInactiveAlpha;
PHLANIMVAR<float> m_fMovingFromWorkspaceAlpha;
// animated shadow color
CAnimatedVariable<CColor> m_cRealShadowColor;
PHLANIMVAR<CHyprColor> m_cRealShadowColor;
// animated tint
CAnimatedVariable<float> m_fDimPercent;
PHLANIMVAR<float> m_fDimPercent;
// animate moving to an invisible workspace
int m_iMonitorMovedFrom = -1; // -1 means not moving
CAnimatedVariable<float> m_fMovingToWorkspaceAlpha;
int m_iMonitorMovedFrom = -1; // -1 means not moving
PHLANIMVAR<float> m_fMovingToWorkspaceAlpha;
// swallowing
PHLWINDOWREF m_pSwallowed;
@@ -391,47 +381,44 @@ class CWindow {
bool m_bTearingHint = false;
// stores the currently matched window rules
std::vector<SWindowRule> m_vMatchedRules;
std::vector<SP<CWindowRule>> m_vMatchedRules;
// window tags
CTagKeeper m_tags;
// For the list lookup
bool operator==(const CWindow& rhs) {
bool operator==(const CWindow& rhs) const {
return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
m_bFadingOut == rhs.m_bFadingOut;
}
// methods
CBox getFullWindowBoundingBox();
SBoxExtents getFullWindowExtents();
CBox getWindowBoxUnified(uint64_t props);
inline CBox getWindowMainSurfaceBox() const {
return {m_vRealPosition.value().x, m_vRealPosition.value().y, m_vRealSize.value().x, m_vRealSize.value().y};
}
CBox getFullWindowBoundingBox();
SBoxExtents getFullWindowExtents();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
void addWindowDeco(UP<IHyprWindowDecoration> deco);
void updateWindowDecos();
void removeWindowDeco(IHyprWindowDecoration* deco);
void uncacheWindowDecos();
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType);
void removeDecorationByType(eDecorationType);
void updateToplevel();
void updateSurfaceScaleTransformDetails(bool force = false);
void moveToWorkspace(PHLWORKSPACE);
PHLWINDOW X11TransientFor();
PHLWINDOW x11TransientFor();
void onUnmap();
void onMap();
void setHidden(bool hidden);
bool isHidden();
void applyDynamicRule(const SWindowRule& r);
void applyDynamicRule(const SP<CWindowRule>& r);
void updateDynamicRules();
SBoxExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
float rounding();
float roundingPower();
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(PHLMONITOR pMonitor);
@@ -441,19 +428,17 @@ class CWindow {
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode);
int getRealBorderSize();
float getScrollMouse();
float getScrollTouchpad();
void updateWindowData();
void updateWindowData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(void* ptr);
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
int popupsCount();
void applyGroupRules();
void createGroup();
void destroyGroup();
@@ -470,17 +455,25 @@ class CWindow {
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
void onFocusAnimUpdate();
void onUpdateState();
void onUpdateMeta();
void onX11Configure(CBox box);
void onResourceChangeX11();
std::string fetchTitle();
std::string fetchClass();
void warpCursor();
void warpCursor(bool force = false);
PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
Vector2D requestedMinSize();
Vector2D requestedMaxSize();
void sendWindowSize(Vector2D size, bool force = false, std::optional<Vector2D> overridePos = std::nullopt);
CBox getWindowMainSurfaceBox() const {
return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y};
}
// listeners
void onAck(uint32_t serial);

View File

@@ -0,0 +1,91 @@
#include "WindowRule.hpp"
#include <unordered_set>
#include <algorithm>
#include <re2/re2.h>
#include "../config/ConfigManager.hpp"
static const auto RULES = std::unordered_set<std::string>{
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused",
};
static const auto RULES_PREFIX = std::unordered_set<std::string>{
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", "opacity",
"plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size", "suppressevent", "tag", "workspace", "xray",
};
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
const auto VALS = CVarList(rule, 2, ' ');
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
(g_pConfigManager->mbWindowProperties.find(VALS[0]) != g_pConfigManager->mbWindowProperties.end()) ||
(g_pConfigManager->miWindowProperties.find(VALS[0]) != g_pConfigManager->miWindowProperties.end()) ||
(g_pConfigManager->mfWindowProperties.find(VALS[0]) != g_pConfigManager->mfWindowProperties.end());
if (!VALID)
return;
if (rule == "float")
ruleType = RULE_FLOAT;
else if (rule == "fullscreen")
ruleType = RULE_FULLSCREEN;
else if (rule == "maximize")
ruleType = RULE_MAXIMIZE;
else if (rule == "noinitialfocus")
ruleType = RULE_NOINITIALFOCUS;
else if (rule == "pin")
ruleType = RULE_PIN;
else if (rule == "stayfocused")
ruleType = RULE_STAYFOCUSED;
else if (rule == "tile")
ruleType = RULE_TILE;
else if (rule == "renderunfocused")
ruleType = RULE_RENDERUNFOCUSED;
else if (rule.starts_with("animation"))
ruleType = RULE_ANIMATION;
else if (rule.starts_with("bordercolor"))
ruleType = RULE_BORDERCOLOR;
else if (rule.starts_with("center"))
ruleType = RULE_CENTER;
else if (rule.starts_with("fullscreenstate"))
ruleType = RULE_FULLSCREENSTATE;
else if (rule.starts_with("group"))
ruleType = RULE_GROUP;
else if (rule.starts_with("idleinhibit"))
ruleType = RULE_IDLEINHIBIT;
else if (rule.starts_with("maxsize"))
ruleType = RULE_MAXSIZE;
else if (rule.starts_with("minsize"))
ruleType = RULE_MINSIZE;
else if (rule.starts_with("monitor"))
ruleType = RULE_MONITOR;
else if (rule.starts_with("move"))
ruleType = RULE_MOVE;
else if (rule.starts_with("opacity"))
ruleType = RULE_OPACITY;
else if (rule.starts_with("plugin:"))
ruleType = RULE_PLUGIN;
else if (rule.starts_with("pseudo"))
ruleType = RULE_PSEUDO;
else if (rule.starts_with("size"))
ruleType = RULE_SIZE;
else if (rule.starts_with("suppressevent"))
ruleType = RULE_SUPPRESSEVENT;
else if (rule.starts_with("tag"))
ruleType = RULE_TAG;
else if (rule.starts_with("workspace"))
ruleType = RULE_WORKSPACE;
else if (rule.starts_with("prop"))
ruleType = RULE_PROP;
else {
// check if this is a prop.
const CVarList VARS(rule, 0, 's', true);
if (g_pConfigManager->miWindowProperties.find(VARS[0]) != g_pConfigManager->miWindowProperties.end() ||
g_pConfigManager->mbWindowProperties.find(VARS[0]) != g_pConfigManager->mbWindowProperties.end() ||
g_pConfigManager->mfWindowProperties.find(VARS[0]) != g_pConfigManager->mfWindowProperties.end()) {
*const_cast<std::string*>(&szRule) = "prop " + rule;
ruleType = RULE_PROP;
Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, szRule);
} else {
Debug::log(ERR, "CWindowRule: didn't match a rule that was found valid?!");
ruleType = RULE_INVALID;
}
}
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <cstdint>
#include "Rule.hpp"
class CWindowRule {
public:
CWindowRule(const std::string& rule, const std::string& value, bool isV2 = false, bool isExecRule = false);
enum eRuleType : uint8_t {
RULE_INVALID = 0,
RULE_FLOAT,
RULE_FULLSCREEN,
RULE_MAXIMIZE,
RULE_NOINITIALFOCUS,
RULE_PIN,
RULE_STAYFOCUSED,
RULE_TILE,
RULE_RENDERUNFOCUSED,
RULE_ANIMATION,
RULE_BORDERCOLOR,
RULE_CENTER,
RULE_FULLSCREENSTATE,
RULE_GROUP,
RULE_IDLEINHIBIT,
RULE_MAXSIZE,
RULE_MINSIZE,
RULE_MONITOR,
RULE_MOVE,
RULE_OPACITY,
RULE_PLUGIN,
RULE_PSEUDO,
RULE_SIZE,
RULE_SUPPRESSEVENT,
RULE_TAG,
RULE_WORKSPACE,
RULE_PROP,
};
eRuleType ruleType = RULE_INVALID;
const std::string szValue;
const std::string szRule;
const bool v2 = false;
const bool execRule = false;
std::string szTitle;
std::string szClass;
std::string szInitialTitle;
std::string szInitialClass;
std::string szTag;
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
// precompiled regexes
CRuleRegexContainer rTitle;
CRuleRegexContainer rClass;
CRuleRegexContainer rInitialTitle;
CRuleRegexContainer rInitialClass;
CRuleRegexContainer rV1Regex;
};

View File

@@ -1,7 +1,12 @@
#include "Workspace.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "config/ConfigManager.hpp"
#include "managers/AnimationManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/HookSystemManager.hpp"
#include <hyprutils/animation/AnimatedVariable.hpp>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@@ -11,27 +16,18 @@ PHLWORKSPACE CWorkspace::create(WORKSPACEID id, PHLMONITOR monitor, std::string
return workspace;
}
CWorkspace::CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) {
m_pMonitor = monitor;
m_iID = id;
m_szName = name;
m_bIsSpecialWorkspace = special;
m_bWasCreatedEmpty = isEmpty;
CWorkspace::CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) :
m_iID(id), m_szName(name), m_pMonitor(monitor), m_bIsSpecialWorkspace(special), m_bWasCreatedEmpty(isEmpty) {
;
}
void CWorkspace::init(PHLWORKSPACE self) {
m_pSelf = self;
m_vRenderOffset.create(m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") :
g_pConfigManager->getAnimationPropertyConfig("workspacesIn"),
self, AVARDAMAGE_ENTIRE);
m_fAlpha.create(AVARTYPE_FLOAT,
m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") : g_pConfigManager->getAnimationPropertyConfig("workspacesIn"), self,
AVARDAMAGE_ENTIRE);
m_fAlpha.setValueAndWarp(1.f);
m_vRenderOffset.registerVar();
m_fAlpha.registerVar();
g_pAnimationManager->createAnimation(Vector2D(0, 0), m_vRenderOffset,
g_pConfigManager->getAnimationPropertyConfig(m_bIsSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(1.f, m_fAlpha, g_pConfigManager->getAnimationPropertyConfig(m_bIsSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self,
AVARDAMAGE_ENTIRE);
const auto RULEFORTHIS = g_pConfigManager->getWorkspaceRuleFor(self);
if (RULEFORTHIS.defaultName.has_value())
@@ -58,16 +54,11 @@ void CWorkspace::init(PHLWORKSPACE self) {
EMIT_HOOK_EVENT("createWorkspace", this);
}
SWorkspaceIDName CWorkspace::getPrevWorkspaceIDName(bool perMonitor) const {
if (perMonitor)
return m_sPrevWorkspacePerMonitor;
SWorkspaceIDName CWorkspace::getPrevWorkspaceIDName() const {
return m_sPrevWorkspace;
}
CWorkspace::~CWorkspace() {
m_vRenderOffset.unregister();
Debug::log(LOG, "Destroying workspace ID {}", m_iID);
// check if g_pHookSystem and g_pEventManager exist, they might be destroyed as in when the compositor is closing.
@@ -85,15 +76,15 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
if (!instant) {
const std::string ANIMNAME = std::format("{}{}", m_bIsSpecialWorkspace ? "specialWorkspace" : "workspaces", in ? "In" : "Out");
m_fAlpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
m_vRenderOffset.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
m_fAlpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
m_vRenderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
}
const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle;
const auto ANIMSTYLE = m_fAlpha->getStyle();
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
// set floating windows offset callbacks
m_vRenderOffset.setUpdateCallback([&](void*) {
m_vRenderOffset->setUpdateCallback([&](auto) {
for (auto const& w : g_pCompositor->m_vWindows) {
if (!validMapped(w) || w->workspaceID() != m_iID)
continue;
@@ -106,91 +97,91 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
const auto PMONITOR = m_pMonitor.lock();
float movePerc = 100.f;
if (ANIMSTYLE.find("%") != std::string::npos) {
if (ANIMSTYLE.find('%') != std::string::npos) {
try {
auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ') + 1);
movePerc = std::stoi(percstr.substr(0, percstr.length() - 1));
} catch (std::exception& e) { Debug::log(ERR, "Error in startAnim: invalid percentage"); }
}
m_fAlpha.setValueAndWarp(1.f);
m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
m_fAlpha->setValueAndWarp(1.f);
m_vRenderOffset->setValueAndWarp(Vector2D(0, 0));
if (ANIMSTYLE.starts_with("slidefadevert")) {
if (in) {
m_fAlpha.setValueAndWarp(0.f);
m_vRenderOffset.setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f)));
m_fAlpha = 1.f;
m_vRenderOffset = Vector2D(0, 0);
m_fAlpha->setValueAndWarp(0.f);
m_vRenderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f)));
*m_fAlpha = 1.f;
*m_vRenderOffset = Vector2D(0, 0);
} else {
m_fAlpha.setValueAndWarp(1.f);
m_fAlpha = 0.f;
m_vRenderOffset = Vector2D(0.0, (left ? -PMONITOR->vecSize.y : PMONITOR->vecSize.y) * (movePerc / 100.f));
m_fAlpha->setValueAndWarp(1.f);
*m_fAlpha = 0.f;
*m_vRenderOffset = Vector2D(0.0, (left ? -PMONITOR->vecSize.y : PMONITOR->vecSize.y) * (movePerc / 100.f));
}
} else {
if (in) {
m_fAlpha.setValueAndWarp(0.f);
m_vRenderOffset.setValueAndWarp(Vector2D((left ? PMONITOR->vecSize.x : -PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0));
m_fAlpha = 1.f;
m_vRenderOffset = Vector2D(0, 0);
m_fAlpha->setValueAndWarp(0.f);
m_vRenderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->vecSize.x : -PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0));
*m_fAlpha = 1.f;
*m_vRenderOffset = Vector2D(0, 0);
} else {
m_fAlpha.setValueAndWarp(1.f);
m_fAlpha = 0.f;
m_vRenderOffset = Vector2D((left ? -PMONITOR->vecSize.x : PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0);
m_fAlpha->setValueAndWarp(1.f);
*m_fAlpha = 0.f;
*m_vRenderOffset = Vector2D((left ? -PMONITOR->vecSize.x : PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0);
}
}
} else if (ANIMSTYLE == "fade") {
m_vRenderOffset.setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade.
m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade.
if (in) {
m_fAlpha.setValueAndWarp(0.f);
m_fAlpha = 1.f;
m_fAlpha->setValueAndWarp(0.f);
*m_fAlpha = 1.f;
} else {
m_fAlpha.setValueAndWarp(1.f);
m_fAlpha = 0.f;
m_fAlpha->setValueAndWarp(1.f);
*m_fAlpha = 0.f;
}
} else if (ANIMSTYLE == "slidevert") {
// fallback is slide
const auto PMONITOR = m_pMonitor.lock();
const auto YDISTANCE = PMONITOR->vecSize.y + *PWORKSPACEGAP;
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
m_fAlpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
if (in) {
m_vRenderOffset.setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE));
m_vRenderOffset = Vector2D(0, 0);
m_vRenderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE));
*m_vRenderOffset = Vector2D(0, 0);
} else {
m_vRenderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE);
*m_vRenderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE);
}
} else {
// fallback is slide
const auto PMONITOR = m_pMonitor.lock();
const auto XDISTANCE = PMONITOR->vecSize.x + *PWORKSPACEGAP;
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
m_fAlpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
if (in) {
m_vRenderOffset.setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0));
m_vRenderOffset = Vector2D(0, 0);
m_vRenderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0));
*m_vRenderOffset = Vector2D(0, 0);
} else {
m_vRenderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0);
*m_vRenderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0);
}
}
if (m_bIsSpecialWorkspace) {
// required for open/close animations
if (in) {
m_fAlpha.setValueAndWarp(0.f);
m_fAlpha = 1.f;
m_fAlpha->setValueAndWarp(0.f);
*m_fAlpha = 1.f;
} else {
m_fAlpha.setValueAndWarp(1.f);
m_fAlpha = 0.f;
m_fAlpha->setValueAndWarp(1.f);
*m_fAlpha = 0.f;
}
}
if (instant) {
m_vRenderOffset.warp();
m_fAlpha.warp();
m_vRenderOffset->warp();
m_fAlpha->warp();
}
}
@@ -224,10 +215,7 @@ void CWorkspace::rememberPrevWorkspace(const PHLWORKSPACE& prev) {
m_sPrevWorkspace.id = prev->m_iID;
m_sPrevWorkspace.name = prev->m_szName;
if (prev->m_pMonitor == m_pMonitor) {
m_sPrevWorkspacePerMonitor.id = prev->m_iID;
m_sPrevWorkspacePerMonitor.name = prev->m_szName;
}
prev->m_pMonitor->addPrevWorkspaceID(prev->m_iID);
}
std::string CWorkspace::getConfigName() {
@@ -278,9 +266,9 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
// f - fullscreen state : f[-1], f[0], f[1], or f[2] for different fullscreen states
// -1: no fullscreen, 0: fullscreen, 1: maximized, 2: fullscreen without sending fs state to window
const auto NEXTSPACE = selector.find_first_of(' ', i);
std::string prop = selector.substr(i, NEXTSPACE == std::string::npos ? std::string::npos : NEXTSPACE - i);
i = std::min(NEXTSPACE, std::string::npos - 1);
const auto CLOSING_BRACKET = selector.find_first_of(']', i);
std::string prop = selector.substr(i, CLOSING_BRACKET == std::string::npos ? std::string::npos : CLOSING_BRACKET + 1 - i);
i = std::min(CLOSING_BRACKET, std::string::npos - 1);
if (cur == 'r') {
WORKSPACEID from = 0, to = 0;
@@ -296,7 +284,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
return false;
}
const auto DASHPOS = prop.find("-");
const auto DASHPOS = prop.find('-');
const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1);
if (!isNumber(LHS) || !isNumber(RHS)) {
@@ -332,7 +320,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
const auto SHOULDBESPECIAL = configStringToInt(prop);
if ((bool)SHOULDBESPECIAL != m_bIsSpecialWorkspace)
if (SHOULDBESPECIAL && (bool)*SHOULDBESPECIAL != m_bIsSpecialWorkspace)
return false;
continue;
}
@@ -367,7 +355,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
const auto WANTSNAMED = configStringToInt(prop);
if (WANTSNAMED != (m_iID <= -1337))
if (WANTSNAMED && *WANTSNAMED != (m_iID <= -1337))
return false;
continue;
}
@@ -422,18 +410,18 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
int count;
if (wantsCountGroup)
count = g_pCompositor->getGroupsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
else
count = g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
if (count != from)
return false;
continue;
}
const auto DASHPOS = prop.find("-");
const auto DASHPOS = prop.find('-');
const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1);
if (!isNumber(LHS) || !isNumber(RHS)) {
@@ -456,11 +444,11 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
WORKSPACEID count;
if (wantsCountGroup)
count = g_pCompositor->getGroupsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
else
count = g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
if (std::clamp(count, from, to) != count)
return false;
@@ -525,3 +513,138 @@ bool CWorkspace::inert() {
MONITORID CWorkspace::monitorID() {
return m_pMonitor ? m_pMonitor->ID : MONITOR_INVALID;
}
PHLWINDOW CWorkspace::getFullscreenWindow() {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == m_pSelf && w->isFullscreen())
return w;
}
return nullptr;
}
bool CWorkspace::isVisible() {
return m_bVisible;
}
bool CWorkspace::isVisibleNotCovered() {
const auto PMONITOR = m_pMonitor.lock();
if (PMONITOR->activeSpecialWorkspace)
return PMONITOR->activeSpecialWorkspace->m_iID == m_iID;
return PMONITOR->activeWorkspace->m_iID == m_iID;
}
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
int no = 0;
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
continue;
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
continue;
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
continue;
no++;
}
return no;
}
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
int no = 0;
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
continue;
if (!w->m_sGroupData.head)
continue;
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
continue;
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
continue;
no++;
}
return no;
}
PHLWINDOW CWorkspace::getFirstWindow() {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && !w->isHidden())
return w;
}
return nullptr;
}
PHLWINDOW CWorkspace::getTopLeftWindow() {
const auto PMONITOR = m_pMonitor.lock();
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden())
continue;
const auto WINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
if (WINDOWIDEALBB.x <= PMONITOR->vecPosition.x + 1 && WINDOWIDEALBB.y <= PMONITOR->vecPosition.y + 1)
return w;
}
return nullptr;
}
bool CWorkspace::hasUrgentWindow() {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && w->m_bIsUrgent)
return true;
}
return false;
}
void CWorkspace::updateWindowDecos() {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace != m_pSelf)
continue;
w->updateWindowDecos();
}
}
void CWorkspace::updateWindowData() {
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_pSelf.lock());
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace != m_pSelf)
continue;
w->updateWindowData(WORKSPACERULE);
}
}
void CWorkspace::forceReportSizesToWindows() {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden())
continue;
w->sendWindowSize(w->m_vRealSize->goal(), true);
}
}
void CWorkspace::rename(const std::string& name) {
if (g_pCompositor->isWorkspaceSpecial(m_iID))
return;
Debug::log(LOG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_iID, name);
m_szName = name;
g_pEventManager->postEvent({"renameworkspace", std::to_string(m_iID) + "," + m_szName});
}
void CWorkspace::updateWindows() {
m_bHasFullscreenWindow = std::ranges::any_of(g_pCompositor->m_vWindows, [this](const auto& w) { return w->m_bIsMapped && w->m_pWorkspace == m_pSelf && w->isFullscreen(); });
for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->m_pWorkspace != m_pSelf)
continue;
w->updateDynamicRules();
}
}

View File

@@ -24,22 +24,19 @@ class CWorkspace {
// Workspaces ID-based have IDs > 0
// and workspaces name-based have IDs starting with -1337
WORKSPACEID m_iID = WORKSPACE_INVALID;
std::string m_szName = "";
PHLMONITORREF m_pMonitor;
// Previous workspace ID and name is stored during a workspace change, allowing travel
// to the previous workspace.
SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor;
WORKSPACEID m_iID = WORKSPACE_INVALID;
std::string m_szName = "";
PHLMONITORREF m_pMonitor;
bool m_bHasFullscreenWindow = false;
eFullscreenMode m_efFullscreenMode = FSMODE_NONE;
bool m_bHasFullscreenWindow = false;
eFullscreenMode m_efFullscreenMode = FSMODE_NONE;
wl_array m_wlrCoordinateArr;
wl_array m_wlrCoordinateArr;
// for animations
CAnimatedVariable<Vector2D> m_vRenderOffset;
CAnimatedVariable<float> m_fAlpha;
bool m_bForceRendering = false;
PHLANIMVAR<Vector2D> m_vRenderOffset;
PHLANIMVAR<float> m_fAlpha;
bool m_bForceRendering = false;
// allows damage to propagate.
bool m_bVisible = false;
@@ -63,26 +60,35 @@ class CWorkspace {
// Inert: destroyed and invalid. If this is true, release the ptr you have.
bool inert();
void startAnim(bool in, bool left, bool instant = false);
void setActive(bool on);
void moveToMonitor(const MONITORID&);
MONITORID monitorID();
PHLWINDOW getLastFocusedWindow();
void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace);
std::string getConfigName();
bool matchesStaticSelector(const std::string& selector);
void markInert();
SWorkspaceIDName getPrevWorkspaceIDName(bool perMonitor) const;
SWorkspaceIDName getPrevWorkspaceIDName() const;
void updateWindowDecos();
void updateWindowData();
int getWindows(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroups(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
bool hasUrgentWindow();
PHLWINDOW getFirstWindow();
PHLWINDOW getTopLeftWindow();
PHLWINDOW getFullscreenWindow();
bool isVisible();
bool isVisibleNotCovered();
void rename(const std::string& name = "");
void forceReportSizesToWindows();
void updateWindows();
private:
void init(PHLWORKSPACE self);
void init(PHLWORKSPACE self);
// Previous workspace ID and name is stored during a workspace change, allowing travel
// to the previous workspace.
SWorkspaceIDName m_sPrevWorkspace;
SP<HOOK_CALLBACK_FN> m_pFocusedWindowHook;
bool m_bInert = true;

View File

@@ -4,14 +4,14 @@
#include <string>
#include "../helpers/signal/Signal.hpp"
enum eHIDCapabilityType : uint32_t {
enum eHIDCapabilityType : uint8_t {
HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0),
HID_INPUT_CAPABILITY_POINTER = (1 << 1),
HID_INPUT_CAPABILITY_TOUCH = (1 << 2),
HID_INPUT_CAPABILITY_TABLET = (1 << 3),
};
enum eHIDType {
enum eHIDType : uint8_t {
HID_TYPE_UNKNOWN = 0,
HID_TYPE_POINTER,
HID_TYPE_KEYBOARD,
@@ -27,7 +27,7 @@ enum eHIDType {
*/
class IHID {
public:
virtual ~IHID() {}
virtual ~IHID() = default;
virtual uint32_t getCapabilities() = 0;
virtual eHIDType getType();

View File

@@ -8,6 +8,8 @@
#include <aquamarine/input/Input.hpp>
#include <cstring>
using namespace Hyprutils::OS;
#define LED_COUNT 3
constexpr static std::array<const char*, 8> MODNAMES = {
@@ -41,13 +43,14 @@ void IKeyboard::clearManuallyAllocd() {
if (xkbKeymap)
xkb_keymap_unref(xkbKeymap);
if (xkbKeymapFD >= 0)
close(xkbKeymapFD);
if (xkbSymState)
xkb_state_unref(xkbSymState);
xkbSymState = nullptr;
xkbKeymap = nullptr;
xkbState = nullptr;
xkbStaticState = nullptr;
xkbKeymapFD = -1;
xkbKeymapFD.reset();
}
void IKeyboard::setKeymap(const SStringRuleNames& rules) {
@@ -143,31 +146,30 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
void IKeyboard::updateKeymapFD() {
Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName);
if (xkbKeymapFD >= 0)
close(xkbKeymapFD);
xkbKeymapFD = -1;
if (xkbKeymapFD.isValid())
xkbKeymapFD.reset();
auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
xkbKeymapString = cKeymapStr;
free(cKeymapStr);
int rw, ro;
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, &rw, &ro))
CFileDescriptor rw, ro;
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, rw, ro))
Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap");
else {
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw, 0);
close(rw);
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw.get(), 0);
rw.reset();
if (keymapFDDest == MAP_FAILED) {
Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap");
close(ro);
ro.reset();
} else {
memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length());
munmap(keymapFDDest, xkbKeymapString.length() + 1);
xkbKeymapFD = ro;
xkbKeymapFD = std::move(ro);
}
}
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD);
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD.get());
}
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {

View File

@@ -6,6 +6,7 @@
#include <optional>
#include <xkbcommon/xkbcommon.h>
#include <hyprutils/os/FileDescriptor.hpp>
AQUAMARINE_FORWARD(IKeyboard);
@@ -96,7 +97,7 @@ class IKeyboard : public IHID {
std::string xkbFilePath = "";
std::string xkbKeymapString = "";
int xkbKeymapFD = -1;
Hyprutils::OS::CFileDescriptor xkbKeymapFD;
SStringRuleNames currentRules;
int repeatRate = 0;

View File

@@ -19,6 +19,7 @@ class IPointer : public IHID {
struct SMotionEvent {
uint32_t timeMs = 0;
Vector2D delta, unaccel;
bool mouse = false;
};
struct SMotionAbsoluteEvent {
@@ -31,6 +32,7 @@ class IPointer : public IHID {
uint32_t timeMs = 0;
uint32_t button = 0;
wl_pointer_button_state state = WL_POINTER_BUTTON_STATE_PRESSED;
bool mouse = false;
};
struct SAxisEvent {
@@ -40,6 +42,7 @@ class IPointer : public IHID {
wl_pointer_axis_relative_direction relativeDirection = WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL;
double delta = 0.0;
int32_t deltaDiscrete = 0;
bool mouse = false;
};
struct SSwipeBeginEvent {

View File

@@ -1,6 +1,5 @@
#include "Keyboard.hpp"
#include "../defines.hpp"
#include "../Compositor.hpp"
#include <aquamarine/input/Input.hpp>

View File

@@ -26,6 +26,7 @@ CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
.timeMs = E.timeMs,
.delta = E.delta,
.unaccel = E.unaccel,
.mouse = true,
});
});
@@ -46,6 +47,7 @@ CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
.timeMs = E.timeMs,
.button = E.button,
.state = E.pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED,
.mouse = true,
});
});
@@ -59,6 +61,7 @@ CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
.relativeDirection = (wl_pointer_axis_relative_direction)E.direction,
.delta = E.delta,
.deltaDiscrete = E.discrete,
.mouse = true,
});
});

View File

@@ -3,7 +3,6 @@
#include "IHID.hpp"
#include "../macros.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp"
AQUAMARINE_FORWARD(ITablet);
AQUAMARINE_FORWARD(ITabletTool);
@@ -27,7 +26,7 @@ class CTablet : public IHID {
virtual eHIDType getType();
SP<Aquamarine::ITablet> aq();
enum eTabletToolAxes {
enum eTabletToolAxes : uint16_t {
HID_TABLET_TOOL_AXIS_X = (1 << 0),
HID_TABLET_TOOL_AXIS_Y = (1 << 1),
HID_TABLET_TOOL_AXIS_TILT_X = (1 << 2),
@@ -92,9 +91,10 @@ class CTablet : public IHID {
WP<CTablet> self;
bool relativeInput = false;
bool absolutePos = false;
std::string boundOutput = "";
CBox activeArea;
CBox boundBox; // output-local
CBox boundBox;
private:
CTablet(SP<Aquamarine::ITablet> tablet);
@@ -172,7 +172,7 @@ class CTabletTool : public IHID {
static SP<CTabletTool> create(SP<Aquamarine::ITabletTool> tool);
~CTabletTool();
enum eTabletToolType {
enum eTabletToolType : uint8_t {
HID_TABLET_TOOL_TYPE_PEN = 1,
HID_TABLET_TOOL_TYPE_ERASER,
HID_TABLET_TOOL_TYPE_BRUSH,
@@ -183,7 +183,7 @@ class CTabletTool : public IHID {
HID_TABLET_TOOL_TYPE_TOTEM,
};
enum eTabletToolCapabilities {
enum eTabletToolCapabilities : uint8_t {
HID_TABLET_TOOL_CAPABILITY_TILT = (1 << 0),
HID_TABLET_TOOL_CAPABILITY_PRESSURE = (1 << 1),
HID_TABLET_TOOL_CAPABILITY_DISTANCE = (1 << 2),

View File

@@ -11,7 +11,7 @@ SP<CVirtualPointer> CVirtualPointer::create(SP<CVirtualPointerV1Resource> resour
}
CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : pointer(resource) {
if (!resource->good())
if UNLIKELY (!resource->good())
return;
listeners.destroy = pointer->events.destroy.registerListener([this](std::any d) {
@@ -38,7 +38,7 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
listeners.holdBegin = pointer->events.holdBegin.registerListener([this](std::any d) { pointerEvents.holdBegin.emit(d); });
listeners.holdEnd = pointer->events.holdEnd.registerListener([this](std::any d) { pointerEvents.holdEnd.emit(d); });
boundOutput = resource->boundOutput ? resource->boundOutput->szName : "entire";
boundOutput = resource->boundOutput ? resource->boundOutput->szName : "";
deviceName = pointer->name;
}

View File

@@ -1,12 +1,7 @@
#pragma once
#include "../defines.hpp"
//
// LISTEN_NAME -> the wl_listener
//
// LISTENER_NAME -> the wl_listener.notify function
//
// NOLINTNEXTLINE(readability-identifier-naming)
namespace Events {
// Window events
DYNLISTENFUNC(commitWindow);

View File

@@ -12,10 +12,17 @@
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../xwayland/XSurface.hpp"
#include "managers/AnimationManager.hpp"
#include "managers/PointerManager.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/AnimationManager.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::Animation;
// ------------------------------------------------------------ //
// __ _______ _ _ _____ ______ _______ //
@@ -27,15 +34,17 @@ using namespace Hyprutils::String;
// //
// ------------------------------------------------------------ //
void setAnimToMove(void* data) {
auto* const PANIMCFG = g_pConfigManager->getAnimationPropertyConfig("windowsMove");
static void setVector2DAnimToMove(WP<CBaseAnimatedVariable> pav) {
const auto PAV = pav.lock();
if (!PAV)
return;
CBaseAnimatedVariable* animvar = (CBaseAnimatedVariable*)data;
CAnimatedVariable<Vector2D>* animvar = dynamic_cast<CAnimatedVariable<Vector2D>*>(PAV.get());
animvar->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove"));
animvar->setConfig(PANIMCFG);
if (animvar->getWindow() && !animvar->getWindow()->m_vRealPosition.isBeingAnimated() && !animvar->getWindow()->m_vRealSize.isBeingAnimated())
animvar->getWindow()->m_bAnimatingIn = false;
const auto PHLWINDOW = animvar->m_Context.pWindow.lock();
if (PHLWINDOW)
PHLWINDOW->m_bAnimatingIn = false;
}
void Events::listener_mapWindow(void* owner, void* data) {
@@ -121,162 +130,186 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bX11ShouldntFocus = PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_pXWaylandSurface->wantsFocus());
if (PWORKSPACE->m_bDefaultFloating)
PWINDOW->m_bIsFloating = true;
if (PWORKSPACE->m_bDefaultPseudo) {
PWINDOW->m_bIsPseudotiled = true;
CBox desiredGeometry = {0};
g_pXWaylandManager->getGeometryForWindow(PWINDOW, &desiredGeometry);
PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
}
// window rules
PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode;
std::optional<sFullscreenState> requestedFSState;
std::optional<SFullscreenState> requestedFSState;
if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen))
requestedClientFSMode = FSMODE_FULLSCREEN;
MONITORID requestedFSMonitor = PWINDOW->m_iWantsInitialFullscreenMonitor;
for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) {
try {
const auto MONITORSTR = trim(r.szRule.substr(r.szRule.find(' ')));
switch (r->ruleType) {
case CWindowRule::RULE_MONITOR: {
try {
const auto MONITORSTR = trim(r->szRule.substr(r->szRule.find(' ')));
if (MONITORSTR == "unset") {
PWINDOW->m_pMonitor = PMONITOR;
} else {
if (isNumber(MONITORSTR)) {
const MONITORID MONITOR = std::stoi(MONITORSTR);
if (const auto PM = g_pCompositor->getMonitorFromID(MONITOR); PM)
PWINDOW->m_pMonitor = PM;
else
PWINDOW->m_pMonitor = g_pCompositor->m_vMonitors.at(0);
if (MONITORSTR == "unset") {
PWINDOW->m_pMonitor = PMONITOR;
} else {
const auto PMONITOR = g_pCompositor->getMonitorFromName(MONITORSTR);
if (PMONITOR)
PWINDOW->m_pMonitor = PMONITOR;
else {
Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
continue;
if (isNumber(MONITORSTR)) {
const MONITORID MONITOR = std::stoi(MONITORSTR);
if (const auto PM = g_pCompositor->getMonitorFromID(MONITOR); PM)
PWINDOW->m_pMonitor = PM;
else
PWINDOW->m_pMonitor = g_pCompositor->m_vMonitors.at(0);
} else {
const auto PMONITOR = g_pCompositor->getMonitorFromName(MONITORSTR);
if (PMONITOR)
PWINDOW->m_pMonitor = PMONITOR;
else {
Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
continue;
}
}
}
}
const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock();
const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock();
if (PWINDOW->m_pMonitor != PMONITOR) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID()));
PMONITOR = PMONITORFROMID;
}
PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
if (PWINDOW->m_pMonitor != PMONITOR) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID()));
PMONITOR = PMONITORFROMID;
}
PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
PWORKSPACE = PWINDOW->m_pWorkspace;
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.starts_with("workspace")) {
// check if it isnt unset
const auto WORKSPACERQ = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
if (WORKSPACERQ == "unset") {
requestedWorkspace = "";
} else {
requestedWorkspace = WORKSPACERQ;
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
requestedFSMonitor = MONITOR_INVALID;
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r->szRule, r->szValue, e.what()); }
break;
}
case CWindowRule::RULE_WORKSPACE: {
// check if it isnt unset
const auto WORKSPACERQ = r->szRule.substr(r->szRule.find_first_of(' ') + 1);
const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ;
if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName)
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r.szValue);
} else if (r.szRule.starts_with("float")) {
PWINDOW->m_bIsFloating = true;
} else if (r.szRule.starts_with("tile")) {
PWINDOW->m_bIsFloating = false;
} else if (r.szRule.starts_with("pseudo")) {
PWINDOW->m_bIsPseudotiled = true;
} else if (r.szRule.starts_with("noinitialfocus")) {
PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.starts_with("fullscreenstate")) {
const auto ARGS = CVarList(r.szRule.substr(r.szRule.find_first_of(' ') + 1), 2, ' ');
int internalMode, clientMode;
try {
internalMode = std::stoi(ARGS[0]);
} catch (std::exception& e) { internalMode = 0; }
try {
clientMode = std::stoi(ARGS[1]);
} catch (std::exception& e) { clientMode = 0; }
requestedFSState = sFullscreenState{.internal = (eFullscreenMode)internalMode, .client = (eFullscreenMode)clientMode};
} else if (r.szRule.starts_with("suppressevent")) {
CVarList vars(r.szRule, 0, 's', true);
for (size_t i = 1; i < vars.size(); ++i) {
if (vars[i] == "fullscreen")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN;
else if (vars[i] == "maximize")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_MAXIMIZE;
else if (vars[i] == "activate")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE;
else if (vars[i] == "activatefocus")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY;
if (WORKSPACERQ == "unset")
requestedWorkspace = "";
else
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
requestedWorkspace = WORKSPACERQ;
const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ;
if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName)
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->szValue);
requestedFSMonitor = MONITOR_INVALID;
break;
}
} else if (r.szRule == "pin") {
PWINDOW->m_bPinned = true;
} else if (r.szRule == "fullscreen") {
requestedInternalFSMode = FSMODE_FULLSCREEN;
} else if (r.szRule == "maximize") {
requestedInternalFSMode = FSMODE_MAXIMIZED;
} else if (r.szRule == "stayfocused") {
PWINDOW->m_bStayFocused = true;
} else if (r.szRule.starts_with("group")) {
if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE)
continue;
// `group` is a shorthand of `group set`
if (trim(r.szRule) == "group") {
PWINDOW->m_eGroupRules |= GROUP_SET;
continue;
case CWindowRule::RULE_FLOAT: {
PWINDOW->m_bIsFloating = true;
break;
}
CVarList vars(r.szRule, 0, 's');
std::string vPrev = "";
for (auto const& v : vars) {
if (v == "group")
case CWindowRule::RULE_TILE: {
PWINDOW->m_bIsFloating = false;
break;
}
case CWindowRule::RULE_PSEUDO: {
PWINDOW->m_bIsPseudotiled = true;
break;
}
case CWindowRule::RULE_NOINITIALFOCUS: {
PWINDOW->m_bNoInitialFocus = true;
break;
}
case CWindowRule::RULE_FULLSCREENSTATE: {
const auto ARGS = CVarList(r->szRule.substr(r->szRule.find_first_of(' ') + 1), 2, ' ');
int internalMode, clientMode;
try {
internalMode = std::stoi(ARGS[0]);
} catch (std::exception& e) { internalMode = 0; }
try {
clientMode = std::stoi(ARGS[1]);
} catch (std::exception& e) { clientMode = 0; }
requestedFSState = SFullscreenState{.internal = (eFullscreenMode)internalMode, .client = (eFullscreenMode)clientMode};
break;
}
case CWindowRule::RULE_SUPPRESSEVENT: {
CVarList vars(r->szRule, 0, 's', true);
for (size_t i = 1; i < vars.size(); ++i) {
if (vars[i] == "fullscreen")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN;
else if (vars[i] == "maximize")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_MAXIMIZE;
else if (vars[i] == "activate")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE;
else if (vars[i] == "activatefocus")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY;
else if (vars[i] == "fullscreenoutput")
PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT;
else
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
}
break;
}
case CWindowRule::RULE_PIN: {
PWINDOW->m_bPinned = true;
break;
}
case CWindowRule::RULE_FULLSCREEN: {
requestedInternalFSMode = FSMODE_FULLSCREEN;
break;
}
case CWindowRule::RULE_MAXIMIZE: {
requestedInternalFSMode = FSMODE_MAXIMIZED;
break;
}
case CWindowRule::RULE_STAYFOCUSED: {
PWINDOW->m_bStayFocused = true;
break;
}
case CWindowRule::RULE_GROUP: {
if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE)
continue;
if (v == "set") {
// `group` is a shorthand of `group set`
if (trim(r->szRule) == "group") {
PWINDOW->m_eGroupRules |= GROUP_SET;
} else if (v == "new") {
// shorthand for `group barred set`
PWINDOW->m_eGroupRules |= (GROUP_SET | GROUP_BARRED);
} else if (v == "lock") {
PWINDOW->m_eGroupRules |= GROUP_LOCK;
} else if (v == "invade") {
PWINDOW->m_eGroupRules |= GROUP_INVADE;
} else if (v == "barred") {
PWINDOW->m_eGroupRules |= GROUP_BARRED;
} else if (v == "deny") {
PWINDOW->m_sGroupData.deny = true;
} else if (v == "override") {
// Clear existing rules
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
} else if (v == "unset") {
// Clear existing rules and stop processing
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
break;
} else if (v == "always") {
if (vPrev == "set" || vPrev == "group")
PWINDOW->m_eGroupRules |= GROUP_SET_ALWAYS;
else if (vPrev == "lock")
PWINDOW->m_eGroupRules |= GROUP_LOCK_ALWAYS;
else
Debug::log(ERR, "windowrule `group` does not support `{} always`", vPrev);
continue;
}
vPrev = v;
CVarList vars(r->szRule, 0, 's');
std::string vPrev = "";
for (auto const& v : vars) {
if (v == "group")
continue;
if (v == "set") {
PWINDOW->m_eGroupRules |= GROUP_SET;
} else if (v == "new") {
// shorthand for `group barred set`
PWINDOW->m_eGroupRules |= (GROUP_SET | GROUP_BARRED);
} else if (v == "lock") {
PWINDOW->m_eGroupRules |= GROUP_LOCK;
} else if (v == "invade") {
PWINDOW->m_eGroupRules |= GROUP_INVADE;
} else if (v == "barred") {
PWINDOW->m_eGroupRules |= GROUP_BARRED;
} else if (v == "deny") {
PWINDOW->m_sGroupData.deny = true;
} else if (v == "override") {
// Clear existing rules
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
} else if (v == "unset") {
// Clear existing rules and stop processing
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
break;
} else if (v == "always") {
if (vPrev == "set" || vPrev == "group")
PWINDOW->m_eGroupRules |= GROUP_SET_ALWAYS;
else if (vPrev == "lock")
PWINDOW->m_eGroupRules |= GROUP_LOCK_ALWAYS;
else
Debug::log(ERR, "windowrule `group` does not support `{} always`", vPrev);
}
vPrev = v;
}
break;
}
default: break;
}
PWINDOW->applyDynamicRule(r);
}
@@ -296,7 +329,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID);
if (!pWorkspace)
pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->monitorID(), requestedWorkspaceName);
pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->monitorID(), requestedWorkspaceName, false);
PWORKSPACE = pWorkspace;
@@ -314,10 +347,39 @@ void Events::listener_mapWindow(void* owner, void* data) {
PMONITOR = g_pCompositor->m_pLastMonitor.lock();
}
requestedFSMonitor = MONITOR_INVALID;
} else
workspaceSilent = false;
}
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT)
requestedFSMonitor = MONITOR_INVALID;
else if (requestedFSMonitor != MONITOR_INVALID) {
if (const auto PM = g_pCompositor->getMonitorFromID(requestedFSMonitor); PM)
PWINDOW->m_pMonitor = PM;
const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock();
if (PWINDOW->m_pMonitor != PMONITOR) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID()));
PMONITOR = PMONITORFROMID;
}
PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
PWORKSPACE = PWINDOW->m_pWorkspace;
Debug::log(LOG, "Requested monitor, applying to {:mw}", PWINDOW);
}
if (PWORKSPACE->m_bDefaultFloating)
PWINDOW->m_bIsFloating = true;
if (PWORKSPACE->m_bDefaultPseudo) {
PWINDOW->m_bIsPseudotiled = true;
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(PWINDOW);
PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
}
PWINDOW->updateWindowData();
// Verify window swallowing. Get the swallower before calling onWindowCreated(PWINDOW) because getSwallower() wouldn't get it after if PWINDOW gets auto grouped.
@@ -330,131 +392,139 @@ void Events::listener_mapWindow(void* owner, void* data) {
// size and move rules
for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("size")) {
try {
switch (r->ruleType) {
case CWindowRule::RULE_SIZE: {
try {
auto stringToFloatClamp = [](const std::string& VALUE, const float CURR, const float REL) {
if (VALUE.starts_with('<'))
return std::min(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
else if (VALUE.starts_with('>'))
return std::max(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
auto stringToFloatClamp = [](const std::string& VALUE, const float CURR, const float REL) {
if (VALUE.starts_with('<'))
return std::min(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
else if (VALUE.starts_with('>'))
return std::max(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
return stringToPercentage(VALUE, REL);
};
return stringToPercentage(VALUE, REL);
};
const auto VALUE = r->szRule.substr(r->szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(PWINDOW);
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) :
stringToFloatClamp(SIZEXSTR, PWINDOW->m_vRealSize->goal().x, PMONITOR->vecSize.x);
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) :
stringToFloatClamp(SIZEXSTR, PWINDOW->m_vRealSize.goal().x, PMONITOR->vecSize.x);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) :
stringToFloatClamp(SIZEYSTR, PWINDOW->m_vRealSize->goal().y, PMONITOR->vecSize.y);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) :
stringToFloatClamp(SIZEYSTR, PWINDOW->m_vRealSize.goal().y, PMONITOR->vecSize.y);
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY});
PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY});
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->szRule, r->szValue); }
break;
}
case CWindowRule::RULE_MOVE: {
try {
auto value = r->szRule.substr(r->szRule.find(' ') + 1);
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.starts_with("move")) {
try {
auto value = r.szRule.substr(r.szRule.find(' ') + 1);
const bool ONSCREEN = value.starts_with("onscreen");
const bool ONSCREEN = value.starts_with("onscreen");
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
const bool CURSOR = value.starts_with("cursor");
if (CURSOR)
value = value.substr(value.find_first_of(' ') + 1);
const auto POSXSTR = value.substr(0, value.find(' '));
const auto POSYSTR = value.substr(value.find(' ') + 1);
int posX = 0;
int posY = 0;
if (POSXSTR.starts_with("100%-")) {
const bool subtractWindow = POSXSTR.starts_with("100%-w-");
const auto POSXRAW = (subtractWindow) ? POSXSTR.substr(7) : 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);
if (subtractWindow)
posX -= PWINDOW->m_vRealSize.goal().x;
const bool CURSOR = value.starts_with("cursor");
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posX = !POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x;
} else {
// cursor
if (POSXSTR == "cursor") {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x;
value = value.substr(value.find_first_of(' ') + 1);
const auto POSXSTR = value.substr(0, value.find(' '));
const auto POSYSTR = value.substr(value.find(' ') + 1);
int posX = 0;
int posY = 0;
if (POSXSTR.starts_with("100%-")) {
const bool subtractWindow = POSXSTR.starts_with("100%-w-");
const auto POSXRAW = (subtractWindow) ? POSXSTR.substr(7) : 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);
if (subtractWindow)
posX -= PWINDOW->m_vRealSize->goal().x;
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posX = !POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x;
} else {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x +
(!POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize.goal().x);
// cursor
if (POSXSTR == "cursor") {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x;
} else {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x +
(!POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize->goal().x);
}
}
}
if (POSYSTR.starts_with("100%-")) {
const bool subtractWindow = POSYSTR.starts_with("100%-w-");
const auto POSYRAW = (subtractWindow) ? POSYSTR.substr(7) : 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);
if (POSYSTR.starts_with("100%-")) {
const bool subtractWindow = POSYSTR.starts_with("100%-w-");
const auto POSYRAW = (subtractWindow) ? POSYSTR.substr(7) : 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);
if (subtractWindow)
posY -= PWINDOW->m_vRealSize.goal().y;
if (subtractWindow)
posY -= PWINDOW->m_vRealSize->goal().y;
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posY = !POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y;
} else {
// cursor
if (POSYSTR == "cursor") {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y;
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posY = !POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y;
} else {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y +
(!POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize.goal().y);
// cursor
if (POSYSTR == "cursor") {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y;
} else {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y +
(!POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize->goal().y);
}
}
}
if (ONSCREEN) {
int borderSize = PWINDOW->getRealBorderSize();
if (ONSCREEN) {
int borderSize = PWINDOW->getRealBorderSize();
posX = std::clamp(posX, (int)(PMONITOR->vecReservedTopLeft.x + borderSize),
(int)(PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PWINDOW->m_vRealSize.goal().x - borderSize));
posX = std::clamp(posX, (int)(PMONITOR->vecReservedTopLeft.x + borderSize),
(int)(PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PWINDOW->m_vRealSize->goal().x - borderSize));
posY = std::clamp(posY, (int)(PMONITOR->vecReservedTopLeft.y + borderSize),
(int)(PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PWINDOW->m_vRealSize.goal().y - borderSize));
}
posY = std::clamp(posY, (int)(PMONITOR->vecReservedTopLeft.y + borderSize),
(int)(PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PWINDOW->m_vRealSize->goal().y - borderSize));
}
Debug::log(LOG, "Rule move, applying to {}", PWINDOW);
Debug::log(LOG, "Rule move, applying to {}", PWINDOW);
PWINDOW->m_vRealPosition = Vector2D(posX, posY) + PMONITOR->vecPosition;
*PWINDOW->m_vRealPosition = Vector2D(posX, posY) + PMONITOR->vecPosition;
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.starts_with("center")) {
auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r.szRule, 2, ' ');
if (ARGS[1] == "1")
RESERVEDOFFSET = (PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight) / 2.f;
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r->szRule, r->szValue); }
break;
}
case CWindowRule::RULE_CENTER: {
auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r->szRule, 2, ' ');
if (ARGS[1] == "1")
RESERVEDOFFSET = (PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight) / 2.f;
PWINDOW->m_vRealPosition = PMONITOR->middle() - PWINDOW->m_vRealSize.goal() / 2.f + RESERVEDOFFSET;
*PWINDOW->m_vRealPosition = PMONITOR->middle() - PWINDOW->m_vRealSize->goal() / 2.f + RESERVEDOFFSET;
break;
}
default: break;
}
}
// set the pseudo size to the GOAL of our current size
// because the windows are animated on RealSize
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal();
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize->goal();
g_pCompositor->changeWindowZOrder(PWINDOW, true);
} else {
@@ -463,30 +533,31 @@ void Events::listener_mapWindow(void* owner, void* data) {
bool setPseudo = false;
for (auto const& r : PWINDOW->m_vMatchedRules) {
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(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
if (r->ruleType != CWindowRule::RULE_SIZE)
continue;
const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(PWINDOW);
try {
const auto VALUE = r->szRule.substr(r->szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) : stringToPercentage(SIZEXSTR, PMONITOR->vecSize.x);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) : stringToPercentage(SIZEYSTR, PMONITOR->vecSize.y);
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) : stringToPercentage(SIZEXSTR, PMONITOR->vecSize.x);
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) : stringToPercentage(SIZEYSTR, PMONITOR->vecSize.y);
setPseudo = true;
PWINDOW->m_vPseudoSize = Vector2D(SIZEX, SIZEY);
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
}
setPseudo = true;
PWINDOW->m_vPseudoSize = Vector2D(SIZEX, SIZEY);
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->szRule, r->szValue); }
}
if (!setPseudo)
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal() - Vector2D(10, 10);
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize->goal() - Vector2D(10, 10);
}
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow.lock();
@@ -509,18 +580,18 @@ void Events::listener_mapWindow(void* owner, void* data) {
else if (*PNEWTAKESOVERFS == 1)
requestedInternalFSMode = PWINDOW->m_pWorkspace->m_efFullscreenMode;
else if (*PNEWTAKESOVERFS == 2)
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), FSMODE_NONE);
g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_pWorkspace->getFullscreenWindow(), FSMODE_NONE);
}
if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus &&
(!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) &&
!g_pInputManager->isConstrained()) {
g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA);
PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sWindowData.noDim.valueOrDefault() ? 0.f : *PDIMSTRENGTH);
PWINDOW->m_fActiveInactiveAlpha->setValueAndWarp(*PACTIVEALPHA);
PWINDOW->m_fDimPercent->setValueAndWarp(PWINDOW->m_sWindowData.noDim.valueOrDefault() ? 0.f : *PDIMSTRENGTH);
} else {
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PINACTIVEALPHA);
PWINDOW->m_fDimPercent.setValueAndWarp(0);
PWINDOW->m_fActiveInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA);
PWINDOW->m_fDimPercent->setValueAndWarp(0);
}
if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN))
@@ -531,15 +602,15 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (!PWINDOW->m_bNoInitialFocus && (requestedInternalFSMode.has_value() || requestedClientFSMode.has_value() || requestedFSState.has_value())) {
// fix fullscreen on requested (basically do a switcheroo)
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow)
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), FSMODE_NONE);
g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_pWorkspace->getFullscreenWindow(), FSMODE_NONE);
PWINDOW->m_vRealPosition.warp();
PWINDOW->m_vRealSize.warp();
PWINDOW->m_vRealPosition->warp();
PWINDOW->m_vRealSize->warp();
if (requestedFSState.has_value()) {
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE);
g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value());
} else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
else if (requestedInternalFSMode.has_value())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value());
else if (requestedClientFSMode.has_value())
@@ -569,7 +640,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bFirstMap = false;
Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition.goal(), PWINDOW->m_vRealSize.goal());
Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition->goal(), PWINDOW->m_vRealSize->goal());
auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName;
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, PWINDOW->m_szClass, PWINDOW->m_szTitle)});
@@ -582,17 +653,17 @@ void Events::listener_mapWindow(void* owner, void* data) {
// do animations
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false);
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_fAlpha->setValueAndWarp(0.f);
*PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealPosition->setCallbackOnEnd(setVector2DAnimToMove);
PWINDOW->m_vRealSize->setCallbackOnEnd(setVector2DAnimToMove);
// recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
// avoid this window being visible
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_bIsFloating)
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha->setValueAndWarp(0.f);
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->transform);
@@ -603,10 +674,18 @@ void Events::listener_mapWindow(void* owner, void* data) {
// fix some xwayland apps that don't behave nicely
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize;
g_pCompositor->updateWorkspaceWindows(PWINDOW->workspaceID());
if (PWINDOW->m_pWorkspace)
PWINDOW->m_pWorkspace->updateWindows();
if (PMONITOR && PWINDOW->isX11OverrideRedirect())
PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale;
// Fix some X11 popups being invisible / having incorrect size on open.
// What the ACTUAL FUCK is going on?????? I HATE X11
if (!PWINDOW->isX11OverrideRedirect() && PWINDOW->m_bIsX11 && PWINDOW->m_bIsFloating) {
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true, PWINDOW->m_vRealPosition->goal() - Vector2D{1, 1});
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true);
}
}
void Events::listener_unmapWindow(void* owner, void* data) {
@@ -627,8 +706,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
const auto PMONITOR = PWINDOW->m_pMonitor.lock();
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.value() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.value();
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition->value() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize->value();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
@@ -641,7 +720,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
// Allow the renderer to catch the last frame.
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
g_pHyprRenderer->makeWindowSnapshot(PWINDOW);
// swallowing
if (valid(PWINDOW->m_pSwallowed)) {
@@ -699,7 +778,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pCompositor->setWindowFullscreenInternal(PWINDOWCANDIDATE, CURRENTFSMODE);
}
if (!PWINDOWCANDIDATE && g_pCompositor->getWindowsOnWorkspace(PWINDOW->workspaceID()) == 0)
if (!PWINDOWCANDIDATE && PWINDOW->m_pWorkspace && PWINDOW->m_pWorkspace->getWindows() == 0)
g_pInputManager->refocus();
g_pInputManager->sendMotionEventsToFocused();
@@ -718,18 +797,19 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pCompositor->addToFadingOutSafe(PWINDOW);
if (!PWINDOW->m_bX11DoesntWantBorders) // don't animate out if they weren't animated in.
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.value() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise onWindowPostCreateClose will ignore it
if (!PWINDOW->m_bX11DoesntWantBorders) // don't animate out if they weren't animated in.
*PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition->value() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise onWindowPostCreateClose will ignore it
// anims
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, true);
PWINDOW->m_fAlpha = 0.f;
*PWINDOW->m_fAlpha = 0.f;
// recheck idle inhibitors
g_pInputManager->recheckIdleInhibitorStatus();
// force report all sizes (QT sometimes has an issue with this)
g_pCompositor->forceReportSizesToWindowsOnWorkspace(PWINDOW->workspaceID());
if (PWINDOW->m_pWorkspace)
PWINDOW->m_pWorkspace->forceReportSizesToWindows();
// update lastwindow after focus
PWINDOW->onUnmap();
@@ -753,8 +833,8 @@ void Events::listener_commitWindow(void* owner, void* data) {
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
if (!PWINDOW->m_bIsX11 && !PWINDOW->isFullscreen() && PWINDOW->m_bIsFloating) {
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize;
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize;
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->layoutMinSize();
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->layoutMaxSize();
PWINDOW->clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional<Vector2D>{MAXSIZE} : std::nullopt);
g_pHyprRenderer->damageWindow(PWINDOW);
@@ -772,7 +852,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
g_pSeatManager->isPointerFrameSkipped = false;
g_pSeatManager->isPointerFrameCommit = false;
} else
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y,
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition->goal().x, PWINDOW->m_vRealPosition->goal().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
if (g_pSeatManager->isPointerFrameSkipped) {
@@ -832,15 +912,6 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->listeners.commit.reset();
}
void Events::listener_setTitleWindow(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!validMapped(PWINDOW))
return;
PWINDOW->onUpdateMeta();
}
void Events::listener_activateX11(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
@@ -872,8 +943,8 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
if (!PWINDOW->m_bIsMapped || !PWINDOW->m_pXWaylandSurface || !PWINDOW->m_pXWaylandSurface->overrideRedirect)
return;
const auto POS = PWINDOW->m_vRealPosition.goal();
const auto SIZ = PWINDOW->m_vRealSize.goal();
const auto POS = PWINDOW->m_vRealPosition->goal();
const auto SIZ = PWINDOW->m_vRealSize->goal();
if (PWINDOW->m_pXWaylandSurface->geometry.size() > Vector2D{1, 1})
PWINDOW->setHidden(false);
@@ -881,7 +952,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->setHidden(true);
if (PWINDOW->isFullscreen() || !PWINDOW->m_bIsFloating) {
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true);
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true);
g_pHyprRenderer->damageWindow(PWINDOW);
return;
}
@@ -895,27 +966,27 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
Debug::log(LOG, "Unmanaged window {} requests geometry update to {:j} {:j}", PWINDOW, LOGICALPOS, PWINDOW->m_pXWaylandSurface->geometry.size());
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vRealPosition.setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y));
PWINDOW->m_vRealPosition->setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y));
if (abs(std::floor(SIZ.x) - PWINDOW->m_pXWaylandSurface->geometry.w) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_pXWaylandSurface->geometry.h) > 2)
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_pXWaylandSurface->geometry.size());
PWINDOW->m_vRealSize->setValueAndWarp(PWINDOW->m_pXWaylandSurface->geometry.size());
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = PWINDOW->m_pMonitor.lock(); PMONITOR) {
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goal() / PMONITOR->scale);
PWINDOW->m_vRealSize->setValueAndWarp(PWINDOW->m_vRealSize->goal() / PMONITOR->scale);
}
}
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.goal();
PWINDOW->m_vSize = PWINDOW->m_vRealSize.goal();
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition->goal();
PWINDOW->m_vSize = PWINDOW->m_vRealSize->goal();
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace;
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition->value() + PWINDOW->m_vRealSize->value() / 2.f)->activeWorkspace;
g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->updateWindowDecos();
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goal();
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goal();
PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition->goal();
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize->goal();
}
}

View File

@@ -1,92 +0,0 @@
#include "AnimatedVariable.hpp"
#include "../managers/AnimationManager.hpp"
#include "../config/ConfigManager.hpp"
CBaseAnimatedVariable::CBaseAnimatedVariable(ANIMATEDVARTYPE type) : m_Type(type) {
; // dummy var
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWindow = pWindow;
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pLayer = pLayer;
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWorkspace = pWorkspace;
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_bDummy = false;
}
CBaseAnimatedVariable::~CBaseAnimatedVariable() {
unregister();
}
void CBaseAnimatedVariable::unregister() {
if (!g_pAnimationManager)
return;
std::erase_if(g_pAnimationManager->m_vAnimatedVariables, [&](const auto& other) { return other == this; });
m_bIsRegistered = false;
disconnectFromActive();
}
void CBaseAnimatedVariable::registerVar() {
if (!m_bIsRegistered)
g_pAnimationManager->m_vAnimatedVariables.push_back(this);
m_bIsRegistered = true;
}
int CBaseAnimatedVariable::getDurationLeftMs() {
return std::max(
(int)(m_pConfig->pValues->internalSpeed * 100) - (int)std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - animationBegin).count(), 0);
}
float CBaseAnimatedVariable::getPercent() {
const auto DURATIONPASSED = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - animationBegin).count();
return std::clamp((DURATIONPASSED / 100.f) / m_pConfig->pValues->internalSpeed, 0.f, 1.f);
}
float CBaseAnimatedVariable::getCurveValue() {
if (!m_bIsBeingAnimated)
return 1.f;
const auto SPENT = getPercent();
if (SPENT >= 1.f)
return 1.f;
return g_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT);
}
void CBaseAnimatedVariable::connectToActive() {
g_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up
if (!m_bIsConnectedToActive)
g_pAnimationManager->m_vActiveAnimatedVariables.push_back(this);
m_bIsConnectedToActive = true;
}
void CBaseAnimatedVariable::disconnectFromActive() {
std::erase_if(g_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == this; });
m_bIsConnectedToActive = false;
}

View File

@@ -1,59 +1,49 @@
#pragma once
#include <functional>
#include <any>
#include <chrono>
#include <type_traits>
#include "math/Math.hpp"
#include <hyprutils/animation/AnimatedVariable.hpp>
#include "Color.hpp"
#include "../defines.hpp"
#include "../debug/Log.hpp"
#include "../desktop/DesktopTypes.hpp"
enum ANIMATEDVARTYPE {
AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT,
AVARTYPE_VECTOR,
AVARTYPE_COLOR
};
// Utility to bind a type with its corresponding ANIMATEDVARTYPE
template <class T>
struct typeToANIMATEDVARTYPE_t {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_INVALID;
};
template <>
struct typeToANIMATEDVARTYPE_t<float> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_FLOAT;
};
template <>
struct typeToANIMATEDVARTYPE_t<Vector2D> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_VECTOR;
};
template <>
struct typeToANIMATEDVARTYPE_t<CColor> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_COLOR;
};
template <class T>
inline constexpr ANIMATEDVARTYPE typeToANIMATEDVARTYPE = typeToANIMATEDVARTYPE_t<T>::value;
enum AVARDAMAGEPOLICY {
enum eAVarDamagePolicy : int8_t {
AVARDAMAGE_NONE = -1,
AVARDAMAGE_ENTIRE = 0,
AVARDAMAGE_BORDER,
AVARDAMAGE_SHADOW
};
class CAnimationManager;
struct SAnimationPropertyConfig;
class CHyprRenderer;
class CWindow;
class CWorkspace;
class CLayerSurface;
enum eAnimatedVarType : int8_t {
AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT,
AVARTYPE_VECTOR,
AVARTYPE_COLOR
};
// Utility to bind a type with its corresponding eAnimatedVarType
template <class T>
// NOLINTNEXTLINE(readability-identifier-naming)
struct STypeToAnimatedVarType_t {
static constexpr eAnimatedVarType value = AVARTYPE_INVALID;
};
template <>
struct STypeToAnimatedVarType_t<float> {
static constexpr eAnimatedVarType value = AVARTYPE_FLOAT;
};
template <>
struct STypeToAnimatedVarType_t<Vector2D> {
static constexpr eAnimatedVarType value = AVARTYPE_VECTOR;
};
template <>
struct STypeToAnimatedVarType_t<CHyprColor> {
static constexpr eAnimatedVarType value = AVARTYPE_COLOR;
};
template <class T>
inline constexpr eAnimatedVarType typeToeAnimatedVarType = STypeToAnimatedVarType_t<T>::value;
// Utility to define a concept as a list of possible type
template <class T, class... U>
@@ -63,247 +53,21 @@ concept OneOf = (... or std::same_as<T, U>);
// This is mainly to get better errors if we put a type that's not supported
// Otherwise template errors are ugly
template <class T>
concept Animable = OneOf<T, Vector2D, float, CColor>;
concept Animable = OneOf<T, Vector2D, float, CHyprColor>;
class CBaseAnimatedVariable {
public:
CBaseAnimatedVariable(ANIMATEDVARTYPE type);
void create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy);
void create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy);
void create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy);
void create(SAnimationPropertyConfig* pAnimConfig, AVARDAMAGEPOLICY policy);
struct SAnimationContext {
PHLWINDOWREF pWindow;
PHLWORKSPACEREF pWorkspace;
PHLLSREF pLayer;
CBaseAnimatedVariable(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable(CBaseAnimatedVariable&&) = delete;
CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete;
virtual ~CBaseAnimatedVariable();
void unregister();
void registerVar();
virtual void warp(bool endCallback = true) = 0;
//
void setConfig(SAnimationPropertyConfig* pConfig) {
m_pConfig = pConfig;
}
SAnimationPropertyConfig* getConfig() {
return m_pConfig;
}
int getDurationLeftMs();
/* returns the spent (completion) % */
float getPercent();
/* returns the current curve value */
float getCurveValue();
// checks if an animation is in progress
inline bool isBeingAnimated() {
return m_bIsBeingAnimated;
}
/* sets a function to be ran when the animation finishes.
if an animation is not running, runs instantly.
if "remove" is set to true, will remove the callback when ran. */
void setCallbackOnEnd(std::function<void(void* thisptr)> func, bool remove = true) {
m_fEndCallback = func;
m_bRemoveEndAfterRan = remove;
if (!isBeingAnimated())
onAnimationEnd();
}
/* sets a function to be ran when an animation is started.
if "remove" is set to true, will remove the callback when ran. */
void setCallbackOnBegin(std::function<void(void* thisptr)> func, bool remove = true) {
m_fBeginCallback = func;
m_bRemoveBeginAfterRan = remove;
}
/* Sets the update callback, called every time the value is animated and a step is done
Warning: calling unregisterVar/registerVar in this handler will cause UB */
void setUpdateCallback(std::function<void(void* thisptr)> func) {
m_fUpdateCallback = func;
}
/* resets all callbacks. Does not call any. */
void resetAllCallbacks() {
m_fBeginCallback = nullptr;
m_fEndCallback = nullptr;
m_fUpdateCallback = nullptr;
m_bRemoveBeginAfterRan = false;
m_bRemoveEndAfterRan = false;
}
PHLWINDOW getWindow() {
return m_pWindow.lock();
}
protected:
PHLWINDOWREF m_pWindow;
PHLWORKSPACEREF m_pWorkspace;
PHLLSREF m_pLayer;
SAnimationPropertyConfig* m_pConfig = nullptr;
bool m_bDummy = true;
bool m_bIsRegistered = false;
bool m_bIsBeingAnimated = false;
std::chrono::steady_clock::time_point animationBegin;
AVARDAMAGEPOLICY m_eDamagePolicy = AVARDAMAGE_NONE;
ANIMATEDVARTYPE m_Type;
bool m_bRemoveEndAfterRan = true;
bool m_bRemoveBeginAfterRan = true;
std::function<void(void* thisptr)> m_fEndCallback;
std::function<void(void* thisptr)> m_fBeginCallback;
std::function<void(void* thisptr)> m_fUpdateCallback;
bool m_bIsConnectedToActive = false;
void connectToActive();
void disconnectFromActive();
// methods
void onAnimationEnd() {
m_bIsBeingAnimated = false;
disconnectFromActive();
if (m_fEndCallback) {
// loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false.
auto removeEndCallback = m_bRemoveEndAfterRan;
m_fEndCallback(this);
if (removeEndCallback)
m_fEndCallback = nullptr; // reset
}
}
void onAnimationBegin() {
m_bIsBeingAnimated = true;
connectToActive();
if (m_fBeginCallback) {
m_fBeginCallback(this);
if (m_bRemoveBeginAfterRan)
m_fBeginCallback = nullptr; // reset
}
}
friend class CAnimationManager;
friend class CWorkspace;
friend class CLayerSurface;
friend class CHyprRenderer;
eAVarDamagePolicy eDamagePolicy = AVARDAMAGE_NONE;
};
template <Animable VarType>
class CAnimatedVariable : public CBaseAnimatedVariable {
public:
CAnimatedVariable() : CBaseAnimatedVariable(typeToANIMATEDVARTYPE<VarType>) {} // dummy var
using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable<VarType, SAnimationContext>;
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pWindow, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pLayer, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pWorkspace, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, policy);
m_Value = value;
m_Goal = value;
}
template <Animable VarType>
using PHLANIMVAR = SP<CAnimatedVariable<VarType>>;
using CBaseAnimatedVariable::create;
CAnimatedVariable(const CAnimatedVariable&) = delete;
CAnimatedVariable(CAnimatedVariable&&) = delete;
CAnimatedVariable& operator=(const CAnimatedVariable&) = delete;
CAnimatedVariable& operator=(CAnimatedVariable&&) = delete;
~CAnimatedVariable() = default;
// gets the current vector value (real time)
const VarType& value() const {
return m_Value;
}
// gets the goal vector value
const VarType& goal() const {
return m_Goal;
}
CAnimatedVariable& operator=(const VarType& v) {
if (v == m_Goal)
return *this;
m_Goal = v;
animationBegin = std::chrono::steady_clock::now();
m_Begun = m_Value;
onAnimationBegin();
return *this;
}
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const VarType& v) {
if (v == m_Value)
return;
m_Value = v;
animationBegin = std::chrono::steady_clock::now();
m_Begun = m_Value;
onAnimationBegin();
}
// Sets the actual value and goal
void setValueAndWarp(const VarType& v) {
m_Goal = v;
m_bIsBeingAnimated = true;
warp();
}
void warp(bool endCallback = true) override {
if (!m_bIsBeingAnimated)
return;
m_Value = m_Goal;
m_bIsBeingAnimated = false;
if (m_fUpdateCallback)
m_fUpdateCallback(this);
if (endCallback)
onAnimationEnd();
}
private:
VarType m_Value{};
VarType m_Goal{};
VarType m_Begun{};
// owners
friend class CAnimationManager;
friend class CWorkspace;
friend class CLayerSurface;
friend class CHyprRenderer;
};
template <Animable VarType>
using PHLANIMVARREF = WP<CAnimatedVariable<VarType>>;

View File

@@ -1,79 +0,0 @@
#include "BezierCurve.hpp"
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include <chrono>
#include <algorithm>
void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
m_dPoints.clear();
const auto BEGIN = std::chrono::high_resolution_clock::now();
m_dPoints.emplace_back(Vector2D(0, 0));
for (auto const& p : *pVec) {
m_dPoints.push_back(p);
}
m_dPoints.emplace_back(Vector2D(1, 1));
RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: {})", m_dPoints.size());
// bake BAKEDPOINTS points for faster lookups
// T -> X ( / BAKEDPOINTS )
for (int i = 0; i < BAKEDPOINTS; ++i) {
m_aPointsBaked[i] = Vector2D(getXForT((i + 1) / (float)BAKEDPOINTS), getYForT((i + 1) / (float)BAKEDPOINTS));
}
const auto ELAPSEDUS = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGIN).count() / 1000.f;
const auto POINTSSIZE = m_aPointsBaked.size() * sizeof(m_aPointsBaked[0]) / 1000.f;
const auto BEGINCALC = std::chrono::high_resolution_clock::now();
for (int j = 1; j < 10; ++j) {
float i = j / 10.0f;
getYForPoint(i);
}
const auto ELAPSEDCALCAVG = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGINCALC).count() / 1000.f / 10.f;
Debug::log(LOG, "Created a bezier curve, baked {} points, mem usage: {:.2f}kB, time to bake: {:.2f}µs. Estimated average calc time: {:.2f}µs.", BAKEDPOINTS, POINTSSIZE,
ELAPSEDUS, ELAPSEDCALCAVG);
}
float CBezierCurve::getYForT(float t) {
return 3 * t * pow(1 - t, 2) * m_dPoints[1].y + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].y + pow(t, 3);
}
float CBezierCurve::getXForT(float t) {
return 3 * t * pow(1 - t, 2) * m_dPoints[1].x + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].x + pow(t, 3);
}
// Todo: this probably can be done better and faster
float CBezierCurve::getYForPoint(float x) {
if (x >= 1.f)
return 1.f;
int index = 0;
bool below = true;
for (int step = (BAKEDPOINTS + 1) / 2; step > 0; step /= 2) {
if (below)
index += step;
else
index -= step;
below = m_aPointsBaked[index].x < x;
}
int lowerIndex = index - (!below || index == BAKEDPOINTS - 1);
// in the name of performance i shall make a hack
const auto LOWERPOINT = &m_aPointsBaked[lowerIndex];
const auto UPPERPOINT = &m_aPointsBaked[lowerIndex + 1];
const auto PERCINDELTA = (x - LOWERPOINT->x) / (UPPERPOINT->x - LOWERPOINT->x);
if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) // can sometimes happen for VERY small x
return 0.f;
return LOWERPOINT->y + (UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA;
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <deque>
#include <array>
#include <vector>
#include "math/Math.hpp"
constexpr int BAKEDPOINTS = 255;
constexpr float INVBAKEDPOINTS = 1.f / BAKEDPOINTS;
// an implementation of a cubic bezier curve
// might do better later
class CBezierCurve {
public:
// sets up the bezier curve.
// this EXCLUDES the 0,0 and 1,1 points,
void setup(std::vector<Vector2D>* points);
float getYForT(float t);
float getXForT(float t);
float getYForPoint(float x);
private:
// this INCLUDES the 0,0 and 1,1 points.
std::deque<Vector2D> m_dPoints;
std::array<Vector2D, BAKEDPOINTS> m_aPointsBaked;
};

View File

@@ -30,25 +30,27 @@ constexpr LD operator""_TB(const LD BYTES) {
return BYTES * 1024 * 1024 * 1024 * 1024;
}
//NOLINTBEGIN
template <typename T>
using __acceptable_byte_operation_type = typename std::enable_if<std::is_trivially_constructible<T, ULL>::value || std::is_trivially_constructible<T, LD>::value>::type;
using internal_hl_acceptable_byte_operation_type = typename std::enable_if<std::is_trivially_constructible<T, ULL>::value || std::is_trivially_constructible<T, LD>::value>::type;
template <typename X, typename = __acceptable_byte_operation_type<X>>
template <typename X, typename = internal_hl_acceptable_byte_operation_type<X>>
constexpr X kBtoBytes(const X kB) {
return kB * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
template <typename X, typename = internal_hl_acceptable_byte_operation_type<X>>
constexpr X MBtoBytes(const X MB) {
return MB * 1024 * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
template <typename X, typename = internal_hl_acceptable_byte_operation_type<X>>
constexpr X GBtoBytes(const X GB) {
return GB * 1024 * 1024 * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
template <typename X, typename = internal_hl_acceptable_byte_operation_type<X>>
constexpr X TBtoBytes(const X TB) {
return TB * 1024 * 1024 * 1024 * 1024;
}
//NOLINTEND
#undef ULL
#undef LD

View File

@@ -5,22 +5,45 @@
#define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0)
#define BLUE(c) ((double)(((c)) & 0xff) / 255.0)
CColor::CColor() {}
CHyprColor::CHyprColor() = default;
CColor::CColor(float r, float g, float b, float a) {
this->r = r;
this->g = g;
this->b = b;
this->a = a;
CHyprColor::CHyprColor(float r_, float g_, float b_, float a_) : r(r_), g(g_), b(b_), a(a_) {
okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab();
}
CColor::CColor(uint64_t hex) {
this->r = RED(hex);
this->g = GREEN(hex);
this->b = BLUE(hex);
this->a = ALPHA(hex);
CHyprColor::CHyprColor(uint64_t hex) : r(RED(hex)), g(GREEN(hex)), b(BLUE(hex)), a(ALPHA(hex)) {
okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab();
}
uint32_t CColor::getAsHex() const {
CHyprColor::CHyprColor(const Hyprgraphics::CColor& color, float a_) : a(a_) {
const auto SRGB = color.asRgb();
r = SRGB.r;
g = SRGB.g;
b = SRGB.b;
okLab = color.asOkLab();
}
uint32_t CHyprColor::getAsHex() const {
return (uint32_t)(a * 255.f) * 0x1000000 + (uint32_t)(r * 255.f) * 0x10000 + (uint32_t)(g * 255.f) * 0x100 + (uint32_t)(b * 255.f) * 0x1;
}
}
Hyprgraphics::CColor::SSRGB CHyprColor::asRGB() const {
return {r, g, b};
}
Hyprgraphics::CColor::SOkLab CHyprColor::asOkLab() const {
return okLab;
}
Hyprgraphics::CColor::SHSL CHyprColor::asHSL() const {
return Hyprgraphics::CColor(okLab).asHSL();
}
CHyprColor CHyprColor::stripA() const {
return {r, g, b, 1.F};
}
CHyprColor CHyprColor::modifyA(float newa) const {
return {r, g, b, newa};
}

View File

@@ -1,35 +1,62 @@
#pragma once
#include <cstdint>
#include <hyprgraphics/color/Color.hpp>
#include "../macros.hpp"
class CColor {
class CHyprColor {
public:
CColor();
CColor(float r, float g, float b, float a);
CColor(uint64_t);
float r = 0, g = 0, b = 0, a = 1.f;
CHyprColor();
CHyprColor(float r, float g, float b, float a);
CHyprColor(const Hyprgraphics::CColor& col, float a);
CHyprColor(uint64_t);
// AR32
uint32_t getAsHex() const;
uint32_t getAsHex() const;
Hyprgraphics::CColor::SSRGB asRGB() const;
Hyprgraphics::CColor::SOkLab asOkLab() const;
Hyprgraphics::CColor::SHSL asHSL() const;
CHyprColor stripA() const;
CHyprColor modifyA(float newa) const;
CColor operator-(const CColor& c2) const {
return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);
//
bool operator==(const CHyprColor& c2) const {
return c2.r == r && c2.g == g && c2.b == b && c2.a == a;
}
CColor operator+(const CColor& c2) const {
return CColor(r + c2.r, g + c2.g, b + c2.b, a + c2.a);
// stubs for the AnimationMgr
CHyprColor operator-(const CHyprColor& c2) const {
RASSERT(false, "CHyprColor: - is a STUB");
return {};
}
CColor operator*(const float& v) const {
return CColor(r * v, g * v, b * v, a * v);
CHyprColor operator+(const CHyprColor& c2) const {
RASSERT(false, "CHyprColor: + is a STUB");
return {};
}
bool operator==(const CColor& c2) const {
return r == c2.r && g == c2.g && b == c2.b && a == c2.a;
CHyprColor operator*(const float& c2) const {
RASSERT(false, "CHyprColor: * is a STUB");
return {};
}
CColor stripA() const {
return {r, g, b, 1};
}
double r = 0, g = 0, b = 0, a = 0;
private:
Hyprgraphics::CColor::SOkLab okLab; // cache for the OkLab representation
};
//NOLINTNEXTLINE
namespace Colors {
static const CHyprColor WHITE = CHyprColor(1.F, 1.F, 1.F, 1.F);
static const CHyprColor GREEN = CHyprColor(0.F, 1.F, 0.F, 1.F);
static const CHyprColor BLUE = CHyprColor(0.F, 0.F, 1.F, 1.F);
static const CHyprColor RED = CHyprColor(1.F, 0.F, 0.F, 1.F);
static const CHyprColor ORANGE = CHyprColor(1.F, 0.5F, 0.F, 1.F);
static const CHyprColor YELLOW = CHyprColor(1.F, 1.F, 0.F, 1.F);
static const CHyprColor MAGENTA = CHyprColor(1.F, 0.F, 1.F, 1.F);
static const CHyprColor PURPLE = CHyprColor(0.5F, 0.F, 0.5F, 1.F);
static const CHyprColor LIME = CHyprColor(0.5F, 1.F, 0.1F, 1.F);
static const CHyprColor LIGHT_BLUE = CHyprColor(0.1F, 1.F, 1.F, 1.F);
static const CHyprColor BLACK = CHyprColor(0.F, 0.F, 0.F, 1.F);
};

View File

@@ -231,7 +231,7 @@ inline const std::vector<SPixelFormat> GLES3_FORMATS = {
},
};
SHMFormat FormatUtils::drmToShm(DRMFormat drm) {
SHMFormat NFormatUtils::drmToShm(DRMFormat drm) {
switch (drm) {
case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
@@ -241,7 +241,7 @@ SHMFormat FormatUtils::drmToShm(DRMFormat drm) {
return drm;
}
DRMFormat FormatUtils::shmToDRM(SHMFormat shm) {
DRMFormat NFormatUtils::shmToDRM(SHMFormat shm) {
switch (shm) {
case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
@@ -251,7 +251,7 @@ DRMFormat FormatUtils::shmToDRM(SHMFormat shm) {
return shm;
}
const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
const SPixelFormat* NFormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
for (auto const& fmt : GLES3_FORMATS) {
if (fmt.drmFormat == drm)
return &fmt;
@@ -260,7 +260,7 @@ const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
return nullptr;
}
const SPixelFormat* FormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) {
const SPixelFormat* NFormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) {
for (auto const& fmt : GLES3_FORMATS) {
if (fmt.glFormat == (int)glFormat && fmt.glType == (int)glType && fmt.withAlpha == alpha)
return &fmt;
@@ -269,23 +269,23 @@ const SPixelFormat* FormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_
return nullptr;
}
bool FormatUtils::isFormatOpaque(DRMFormat drm) {
const auto FMT = FormatUtils::getPixelFormatFromDRM(drm);
bool NFormatUtils::isFormatOpaque(DRMFormat drm) {
const auto FMT = NFormatUtils::getPixelFormatFromDRM(drm);
if (!FMT)
return false;
return !FMT->withAlpha;
}
int FormatUtils::pixelsPerBlock(const SPixelFormat* const fmt) {
int NFormatUtils::pixelsPerBlock(const SPixelFormat* const fmt) {
return fmt->blockSize.x * fmt->blockSize.y > 0 ? fmt->blockSize.x * fmt->blockSize.y : 1;
}
int FormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
}
uint32_t FormatUtils::drmFormatToGL(DRMFormat drm) {
uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) {
switch (drm) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
@@ -302,7 +302,7 @@ uint32_t FormatUtils::drmFormatToGL(DRMFormat drm) {
return GL_RGBA;
}
uint32_t FormatUtils::glFormatToType(uint32_t gl) {
uint32_t NFormatUtils::glFormatToType(uint32_t gl) {
return gl != GL_RGBA ?
#ifdef GLES2
GL_UNSIGNED_INT_2_10_10_10_REV_EXT :
@@ -312,14 +312,14 @@ uint32_t FormatUtils::glFormatToType(uint32_t gl) {
GL_UNSIGNED_BYTE;
}
std::string FormatUtils::drmFormatName(DRMFormat drm) {
std::string NFormatUtils::drmFormatName(DRMFormat drm) {
auto n = drmGetFormatName(drm);
std::string name = n;
free(n);
return name;
}
std::string FormatUtils::drmModifierName(uint64_t mod) {
std::string NFormatUtils::drmModifierName(uint64_t mod) {
auto n = drmGetFormatModifierName(mod);
std::string name = n;
free(n);

View File

@@ -22,7 +22,7 @@ struct SPixelFormat {
typedef Aquamarine::SDRMFormat SDRMFormat;
namespace FormatUtils {
namespace NFormatUtils {
SHMFormat drmToShm(DRMFormat drm);
DRMFormat shmToDRM(SHMFormat shm);

View File

@@ -3,9 +3,13 @@
#include <algorithm>
#include "../Compositor.hpp"
#include "../managers/TokenManager.hpp"
#include "Monitor.hpp"
#include "../config/ConfigManager.hpp"
#include "fs/FsUtils.hpp"
#include <optional>
#include <cstring>
#include <cmath>
#include <filesystem>
#include <set>
#include <sys/utsname.h>
#include <sys/mman.h>
@@ -17,7 +21,9 @@
#include <execinfo.h>
#endif
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/Process.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
@@ -158,26 +164,6 @@ std::string absolutePath(const std::string& rawpath, const std::string& currentP
return value;
}
void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const std::string& ownerString) {
ASSERT(pSignal);
ASSERT(pListener);
wl_signal_add(pSignal, pListener);
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
}
std::string escapeJSONStrings(const std::string& str) {
std::ostringstream oss;
for (auto const& c : str) {
@@ -261,7 +247,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
WORKSPACEID id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0;
while (++id < LONG_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0)) {
if (!invalidWSes.contains(id) && (!PWORKSPACE || PWORKSPACE->getWindows() == 0)) {
result.id = id;
return result;
}
@@ -275,7 +261,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
if (!valid(PWORKSPACE))
return {WORKSPACE_INVALID};
const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->m_sPrevWorkspace.id);
const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->getPrevWorkspaceIDName().id);
if (!PLASTWORKSPACE)
return {WORKSPACE_INVALID};
@@ -583,18 +569,12 @@ float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Ve
// Execute a shell command and get the output
std::string execAndGet(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
using PcloseType = int (*)(FILE*);
const std::unique_ptr<FILE, PcloseType> pipe(popen(cmd, "r"), static_cast<PcloseType>(pclose));
if (!pipe) {
Debug::log(ERR, "execAndGet: failed in pipe");
return "";
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
CProcess proc("/bin/sh", {"-c", cmd});
if (!proc.runSync())
return "error";
return proc.stdOut();
}
void logSystemInfo() {
@@ -610,11 +590,28 @@ void logSystemInfo() {
Debug::log(NONE, "\n");
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
const std::string GPUINFO = execAndGet("pciconf -lv | grep -F -A4 vga");
#elif defined(__arm__) || defined(__aarch64__)
const std::string GPUINFO = execAndGet("cat /proc/device-tree/soc*/gpu*/compatible");
std::string GPUINFO;
const std::filesystem::path dev_tree = "/proc/device-tree";
try {
if (std::filesystem::exists(dev_tree) && std::filesystem::is_directory(dev_tree)) {
std::for_each(std::filesystem::directory_iterator(dev_tree), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& entry) {
if (std::filesystem::is_directory(entry) && entry.path().filename().string().starts_with("soc")) {
std::for_each(std::filesystem::directory_iterator(entry.path()), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& sub_entry) {
if (std::filesystem::is_directory(sub_entry) && sub_entry.path().filename().string().starts_with("gpu")) {
std::filesystem::path file_path = sub_entry.path() / "compatible";
std::ifstream file(file_path);
if (file)
GPUINFO.append(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
}
});
}
});
}
} catch (...) { GPUINFO = "error"; }
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
#endif
Debug::log(LOG, "GPU information:\n{}\n", GPUINFO);
@@ -625,7 +622,7 @@ void logSystemInfo() {
// log etc
Debug::log(LOG, "os-release:");
Debug::log(NONE, "{}", execAndGet("cat /etc/os-release"));
Debug::log(NONE, "{}", NFsUtils::readFileAsString("/etc/os-release").value_or("error"));
}
int64_t getPPIDof(int64_t pid) {
@@ -677,44 +674,91 @@ int64_t getPPIDof(int64_t pid) {
#endif
}
int64_t configStringToInt(const std::string& VALUE) {
std::expected<int64_t, std::string> configStringToInt(const std::string& VALUE) {
auto parseHex = [](const std::string& value) -> std::expected<int64_t, std::string> {
try {
size_t position;
auto result = stoll(value, &position, 16);
if (position == value.size())
return result;
} catch (const std::exception&) {}
return std::unexpected("invalid hex " + value);
};
if (VALUE.starts_with("0x")) {
// Values with 0x are hex
const auto VALUEWITHOUTHEX = VALUE.substr(2);
return stol(VALUEWITHOUTHEX, nullptr, 16);
return parseHex(VALUE);
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6));
if (trim(VALUEWITHOUTFUNC).length() != 8) {
Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)");
// try doing it the comma way first
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) {
// cool
std::string rolling = VALUEWITHOUTFUNC;
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
rolling = rolling.substr(rolling.find(',') + 1);
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
rolling = rolling.substr(rolling.find(',') + 1);
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
rolling = rolling.substr(rolling.find(',') + 1);
uint8_t a = 0;
if (!r || !g || !b)
return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
try {
a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f);
} catch (std::exception& e) { return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); }
return a * (Hyprlang::INT)0x1000000 + *r * (Hyprlang::INT)0x10000 + *g * (Hyprlang::INT)0x100 + *b;
} else if (VALUEWITHOUTFUNC.length() == 8) {
const auto RGBA = parseHex(VALUEWITHOUTFUNC);
if (!RGBA)
return RGBA;
// now we need to RGBA -> ARGB. The config holds ARGB only.
return (*RGBA >> 8) + 0x1000000 * (*RGBA & 0xFF);
}
const auto RGBA = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
return std::unexpected("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values");
// now we need to RGBA -> ARGB. The config holds ARGB only.
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5));
if (trim(VALUEWITHOUTFUNC).length() != 6) {
Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
// try doing it the comma way first
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) {
// cool
std::string rolling = VALUEWITHOUTFUNC;
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
rolling = rolling.substr(rolling.find(',') + 1);
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
rolling = rolling.substr(rolling.find(',') + 1);
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
if (!r || !g || !b)
return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
return (Hyprlang::INT)0xFF000000 + *r * (Hyprlang::INT)0x10000 + *g * (Hyprlang::INT)0x100 + *b;
} else if (VALUEWITHOUTFUNC.length() == 6) {
auto r = parseHex(VALUEWITHOUTFUNC);
return r ? *r + 0xFF000000 : r;
}
const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
return RGB + 0xFF000000; // 0xFF for opaque
return std::unexpected("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values");
} else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
return 1;
} else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
return 0;
}
if (VALUE.empty() || !isNumber(VALUE))
return 0;
if (VALUE.empty() || !isNumber(VALUE, false))
return std::unexpected("cannot parse \"" + VALUE + "\" as an int.");
return std::stoll(VALUE);
try {
const auto RES = std::stoll(VALUE);
return RES;
} catch (std::exception& e) { return std::unexpected(std::string{"stoll threw: "} + e.what()); }
return std::unexpected("parse error");
}
Vector2D configStringToVector2D(const std::string& VALUE) {
@@ -792,50 +836,48 @@ bool envEnabled(const std::string& env) {
return std::string(ENV) == "1";
}
std::pair<int, std::string> openExclusiveShm() {
std::pair<CFileDescriptor, std::string> openExclusiveShm() {
// Only absolute paths can be shared across different shm_open() calls
std::string name = "/" + g_pTokenManager->getRandomUUID();
for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
return {fd, name};
CFileDescriptor fd{shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)};
if (fd.isValid())
return {std::move(fd), name};
}
return {-1, ""};
return {{}, ""};
}
int allocateSHMFile(size_t len) {
CFileDescriptor allocateSHMFile(size_t len) {
auto [fd, name] = openExclusiveShm();
if (fd < 0)
return -1;
if (!fd.isValid())
return {};
shm_unlink(name.c_str());
int ret;
do {
ret = ftruncate(fd, len);
ret = ftruncate(fd.get(), len);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
return {};
}
return fd;
return std::move(fd);
}
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
bool allocateSHMFilePair(size_t size, CFileDescriptor& rw_fd_ptr, CFileDescriptor& ro_fd_ptr) {
auto [fd, name] = openExclusiveShm();
if (fd < 0) {
if (!fd.isValid()) {
return false;
}
// CLOEXEC is guaranteed to be set by shm_open
int ro_fd = shm_open(name.c_str(), O_RDONLY, 0);
if (ro_fd < 0) {
CFileDescriptor ro_fd{shm_open(name.c_str(), O_RDONLY, 0)};
if (!ro_fd.isValid()) {
shm_unlink(name.c_str());
close(fd);
return false;
}
@@ -843,24 +885,20 @@ bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
// Make sure the file cannot be re-opened in read-write mode (e.g. via
// "/proc/self/fd/" on Linux)
if (fchmod(fd, 0) != 0) {
close(fd);
close(ro_fd);
if (fchmod(fd.get(), 0) != 0) {
return false;
}
int ret;
do {
ret = ftruncate(fd, size);
ret = ftruncate(fd.get(), size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
close(ro_fd);
return false;
}
*rw_fd_ptr = fd;
*ro_fd_ptr = ro_fd;
rw_fd_ptr = std::move(fd);
ro_fd_ptr = std::move(ro_fd);
return true;
}
@@ -869,4 +907,4 @@ float stringToPercentage(const std::string& VALUE, const float REL) {
return (std::stof(VALUE.substr(0, VALUE.length() - 1)) * REL) / 100.f;
else
return std::stof(VALUE);
};
}

View File

@@ -1,11 +1,11 @@
#pragma once
#include <optional>
#include <string>
#include <wayland-server.h>
#include "math/Math.hpp"
#include <vector>
#include <format>
#include <expected>
#include <hyprutils/os/FileDescriptor.hpp>
#include "../SharedDefs.hpp"
#include "../macros.hpp"
@@ -19,28 +19,26 @@ struct SWorkspaceIDName {
std::string name;
};
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);
bool isDirection(const std::string&);
bool isDirection(const char&);
SWorkspaceIDName getWorkspaceIDNameFromString(const 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&);
Vector2D configStringToVector2D(const std::string&);
std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
double normalizeAngleRad(double ang);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
bool envEnabled(const std::string& env);
int allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr);
float stringToPercentage(const std::string& VALUE, const float REL);
std::string absolutePath(const std::string&, const std::string&);
std::string escapeJSONStrings(const std::string& str);
bool isDirection(const std::string&);
bool isDirection(const char&);
SWorkspaceIDName getWorkspaceIDNameFromString(const 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);
std::expected<int64_t, std::string> configStringToInt(const std::string&);
Vector2D configStringToVector2D(const std::string&);
std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
double normalizeAngleRad(double ang);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
bool envEnabled(const std::string& env);
Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr);
float stringToPercentage(const std::string& VALUE, const float REL);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View File

@@ -1,5 +1,6 @@
#include "Monitor.hpp"
#include "MiscFunctions.hpp"
#include "../macros.hpp"
#include "math/Math.hpp"
#include "sync/SyncReleaser.hpp"
#include "../Compositor.hpp"
@@ -16,15 +17,24 @@
#include "../managers/PointerManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../render/Renderer.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/input/InputManager.hpp"
#include "sync/SyncTimeline.hpp"
#include "../desktop/LayerSurface.hpp"
#include <aquamarine/output/Output.hpp>
#include "debug/Log.hpp"
#include "debug/HyprNotificationOverlay.hpp"
#include <hyprutils/string/String.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
#include <cstring>
#include <ranges>
using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
using namespace Hyprutils::OS;
int ratHandler(void* data) {
static int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor(((CMonitor*)data)->self.lock());
return 1;
@@ -39,8 +49,11 @@ CMonitor::~CMonitor() {
}
void CMonitor::onConnect(bool noRule) {
EMIT_HOOK_EVENT("preMonitorAdded", self.lock());
CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }};
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
@@ -84,7 +97,7 @@ void CMonitor::onConnect(bool noRule) {
return;
Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName);
g_pHyprRenderer->applyMonitorRule(self.lock(), &activeMonitorRule, true);
applyMonitorRule(&activeMonitorRule, true);
return;
}
@@ -98,7 +111,7 @@ void CMonitor::onConnect(bool noRule) {
SMonitorRule rule = activeMonitorRule;
rule.resolution = SIZE;
g_pHyprRenderer->applyMonitorRule(self.lock(), &rule);
applyMonitorRule(&rule);
});
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
@@ -171,7 +184,7 @@ void CMonitor::onConnect(bool noRule) {
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(self.lock(), &monitorRule, true);
applyMonitorRule(&monitorRule, true);
if (!state.commit())
Debug::log(WARN, "state.commit() failed in CMonitor::onCommit");
@@ -238,6 +251,7 @@ void CMonitor::onConnect(bool noRule) {
}
void CMonitor::onDisconnect(bool destroy) {
EMIT_HOOK_EVENT("preMonitorRemoved", self.lock());
CScopeGuard x = {[this]() {
if (g_pCompositor->m_bIsShuttingDown)
return;
@@ -312,7 +326,7 @@ void CMonitor::onDisconnect(bool destroy) {
g_pCompositor->warpCursorTo(BACKUPMON->vecPosition + BACKUPMON->vecTransformedSize / 2.F, true);
// move workspaces
std::deque<PHLWORKSPACE> wspToMove;
std::vector<PHLWORKSPACE> wspToMove;
for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_pMonitor == self || !w->m_pMonitor)
wspToMove.push_back(w);
@@ -359,6 +373,392 @@ void CMonitor::onDisconnect(bool destroy) {
std::erase_if(g_pCompositor->m_vMonitors, [&](PHLMONITOR& el) { return el.get() == this; });
}
bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
static auto PDISABLESCALECHECKS = CConfigValue<Hyprlang::INT>("debug:disable_scale_checks");
Debug::log(LOG, "Applying monitor rule for {}", szName);
activeMonitorRule = *pMonitorRule;
if (forceSize.has_value())
activeMonitorRule.resolution = forceSize.value();
const auto RULE = &activeMonitorRule;
// if it's disabled, disable and ignore
if (RULE->disabled) {
if (m_bEnabled)
onDisconnect();
events.modeChanged.emit();
return true;
}
// don't touch VR headsets
if (output->nonDesktop)
return true;
if (!m_bEnabled) {
onConnect(true); // enable it.
Debug::log(LOG, "Monitor {} is disabled but is requested to be enabled", szName);
force = true;
}
// Check if the rule isn't already applied
// TODO: clean this up lol
if (!force && DELTALESSTHAN(vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(vecPixelSize.y, RULE->resolution.y, 1) &&
DELTALESSTHAN(refreshRate, RULE->refreshRate, 1) && setScale == RULE->scale &&
((DELTALESSTHAN(vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(vecPosition.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) &&
transform == RULE->transform && RULE->enable10bit == enabled10bit && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", szName);
setMirror(RULE->mirrorOf);
return true;
}
bool autoScale = false;
if (RULE->scale > 0.1) {
scale = RULE->scale;
} else {
autoScale = true;
const auto DEFAULTSCALE = getDefaultScale();
scale = DEFAULTSCALE;
}
setScale = scale;
transform = RULE->transform;
// accumulate requested modes in reverse order (cause inesrting at front is inefficient)
std::vector<SP<Aquamarine::SOutputMode>> requestedModes;
std::string requestedStr = "preferred";
// use sortFunc, add best 3 to requestedModes in reverse, since we test in reverse
auto addBest3Modes = [&](auto const& sortFunc) {
auto sortedModes = output->modes;
std::ranges::sort(sortedModes, sortFunc);
if (sortedModes.size() > 3)
sortedModes.erase(sortedModes.begin() + 3, sortedModes.end());
requestedModes.insert(requestedModes.end(), sortedModes.rbegin(), sortedModes.rend());
};
// last fallback is preferred mode, btw this covers resolution == Vector2D()
if (!output->preferredMode())
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE", output->name);
else
requestedModes.push_back(output->preferredMode());
if (RULE->resolution == Vector2D(-1, -1)) {
requestedStr = "highrr";
// sort prioritizing refresh rate 1st and resolution 2nd, then add best 3
addBest3Modes([](auto const& a, auto const& b) {
if (std::round(a->refreshRate) > std::round(b->refreshRate))
return true;
else if (DELTALESSTHAN((float)a->refreshRate, (float)b->refreshRate, 1.F) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
return true;
return false;
});
} else if (RULE->resolution == Vector2D(-1, -2)) {
requestedStr = "highres";
// sort prioritizing resultion 1st and refresh rate 2nd, then add best 3
addBest3Modes([](auto const& a, auto const& b) {
if (a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
return true;
else if (DELTALESSTHAN(a->pixelSize.x, b->pixelSize.x, 1) && DELTALESSTHAN(a->pixelSize.y, b->pixelSize.y, 1) &&
std::round(a->refreshRate) > std::round(b->refreshRate))
return true;
return false;
});
} else if (RULE->resolution != Vector2D()) {
// user requested mode
requestedStr = std::format("{:X0}@{:.2f}Hz", RULE->resolution, RULE->refreshRate);
// sort by closeness to requested, then add best 3
addBest3Modes([&](auto const& a, auto const& b) {
if (abs(a->pixelSize.x - RULE->resolution.x) < abs(b->pixelSize.x - RULE->resolution.x))
return true;
if (a->pixelSize.x == b->pixelSize.x && abs(a->pixelSize.y - RULE->resolution.y) < abs(b->pixelSize.y - RULE->resolution.y))
return true;
if (a->pixelSize == b->pixelSize && abs((a->refreshRate / 1000.f) - RULE->refreshRate) < abs((b->refreshRate / 1000.f) - RULE->refreshRate))
return true;
return false;
});
// if the best mode isnt close to requested, then try requested as custom mode first
if (!requestedModes.empty()) {
auto bestMode = requestedModes.back();
if (!DELTALESSTHAN(bestMode->pixelSize.x, RULE->resolution.x, 1) || !DELTALESSTHAN(bestMode->pixelSize.y, RULE->resolution.y, 1) ||
!DELTALESSTHAN(bestMode->refreshRate / 1000.f, RULE->refreshRate, 1))
requestedModes.push_back(makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = RULE->refreshRate * 1000.f}));
}
// then if requested is custom, try custom mode first
if (RULE->drmMode.type == DRM_MODE_TYPE_USERDEF) {
if (output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM)
Debug::log(ERR, "Tried to set custom modeline on non-DRM output");
else
requestedModes.push_back(makeShared<Aquamarine::SOutputMode>(
Aquamarine::SOutputMode{.pixelSize = {RULE->drmMode.hdisplay, RULE->drmMode.vdisplay}, .refreshRate = RULE->drmMode.vrefresh, .modeInfo = RULE->drmMode}));
}
}
const auto WAS10B = enabled10bit;
const auto OLDRES = vecPixelSize;
bool success = false;
// Needed in case we are switching from a custom modeline to a standard mode
customDrmMode = {};
currentMode = nullptr;
output->state->setFormat(DRM_FORMAT_XRGB8888);
prevDrmFormat = drmFormat;
drmFormat = DRM_FORMAT_XRGB8888;
output->state->resetExplicitFences();
if (Debug::trace) {
Debug::log(TRACE, "Monitor {} requested modes:", szName);
if (requestedModes.empty())
Debug::log(TRACE, "| None");
else {
for (auto const& mode : requestedModes | std::views::reverse) {
Debug::log(TRACE, "| {:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
}
}
}
for (auto const& mode : requestedModes | std::views::reverse) {
std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF) {
output->state->setCustomMode(mode);
if (!state.test()) {
Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr);
continue;
}
customDrmMode = mode->modeInfo.value();
} else {
output->state->setMode(mode);
if (!state.test()) {
Debug::log(ERR, "Monitor {}: REJECTED available mode {}!", szName, modeStr);
if (mode->preferred)
Debug::log(ERR, "Monitor {}: REJECTED preferred mode!!!", szName);
continue;
}
customDrmMode = {};
}
refreshRate = mode->refreshRate / 1000.f;
vecSize = mode->pixelSize;
currentMode = mode;
success = true;
if (mode->preferred)
Debug::log(LOG, "Monitor {}: requested {}, using preferred mode {}", szName, requestedStr, modeStr);
else if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF)
Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr);
else
Debug::log(LOG, "Monitor {}: requested {}, using available mode {}", szName, requestedStr, modeStr);
break;
}
// try requested as custom mode jic it works
if (!success && RULE->resolution != Vector2D() && RULE->resolution != Vector2D(-1, -1) && RULE->resolution != Vector2D(-1, -2)) {
auto refreshRate = output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->refreshRate * 1000 : 0;
auto mode = makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = refreshRate});
std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
output->state->setCustomMode(mode);
if (state.test()) {
Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr);
refreshRate = mode->refreshRate / 1000.f;
vecSize = mode->pixelSize;
currentMode = mode;
customDrmMode = {};
success = true;
} else
Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr);
}
// try any of the modes if none of the above work
if (!success) {
for (auto const& mode : output->modes) {
output->state->setMode(mode);
if (!state.test())
continue;
auto errorMessage =
std::format("Monitor {} failed to set any requested modes, falling back to mode {:X0}@{:.2f}Hz", szName, mode->pixelSize, mode->refreshRate / 1000.f);
Debug::log(WARN, errorMessage);
g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING);
refreshRate = mode->refreshRate / 1000.f;
vecSize = mode->pixelSize;
currentMode = mode;
customDrmMode = {};
success = true;
break;
}
}
if (!success) {
Debug::log(ERR, "Monitor {} has NO FALLBACK MODES, and an INVALID one was requested: {:X0}@{:.2f}Hz", szName, RULE->resolution, RULE->refreshRate);
return true;
}
vrrActive = output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR()
|| createdByUser; // wayland backend doesn't allow for disabling adaptive_sync
vecPixelSize = vecSize;
// clang-format off
static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{
std::vector<std::pair<std::string, uint32_t>>{ /* 10-bit */
{"DRM_FORMAT_XRGB2101010", DRM_FORMAT_XRGB2101010}, {"DRM_FORMAT_XBGR2101010", DRM_FORMAT_XBGR2101010}, {"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}
},
std::vector<std::pair<std::string, uint32_t>>{ /* 8-bit */
{"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}
}
};
// clang-format on
bool set10bit = false;
for (auto const& fmt : formats[(int)!RULE->enable10bit]) {
output->state->setFormat(fmt.second);
prevDrmFormat = drmFormat;
drmFormat = fmt.second;
if (!state.test()) {
Debug::log(ERR, "output {} failed basic test on format {}", szName, fmt.first);
} else {
Debug::log(LOG, "output {} succeeded basic test on format {}", szName, fmt.first);
if (RULE->enable10bit && fmt.first.contains("101010"))
set10bit = true;
break;
}
}
enabled10bit = set10bit;
Vector2D logicalSize = vecPixelSize / scale;
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
// invalid scale, will produce fractional pixels.
// find the nearest valid.
float searchScale = std::round(scale * 120.0);
bool found = false;
double scaleZero = searchScale / 120.0;
Vector2D logicalZero = vecPixelSize / scaleZero;
if (logicalZero == logicalZero.round())
scale = scaleZero;
else {
for (size_t i = 1; i < 90; ++i) {
double scaleUp = (searchScale + i) / 120.0;
double scaleDown = (searchScale - i) / 120.0;
Vector2D logicalUp = vecPixelSize / scaleUp;
Vector2D logicalDown = vecPixelSize / scaleDown;
if (logicalUp == logicalUp.round()) {
found = true;
searchScale = scaleUp;
break;
}
if (logicalDown == logicalDown.round()) {
found = true;
searchScale = scaleDown;
break;
}
}
if (!found) {
if (autoScale)
scale = std::round(scaleZero);
else {
Debug::log(ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", scale);
g_pConfigManager->addParseError("Invalid scale passed to monitor " + szName + ", failed to find a clean divisor");
scale = getDefaultScale();
}
} else {
if (!autoScale) {
Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", scale, searchScale);
g_pConfigManager->addParseError(
std::format("Invalid scale passed to monitor {}, failed to find a clean divisor. Suggested nearest scale: {:5f}", szName, searchScale));
scale = getDefaultScale();
} else
scale = searchScale;
}
}
}
output->scheduleFrame();
if (!state.commit())
Debug::log(ERR, "Couldn't commit output named {}", output->name);
Vector2D xfmd = transform % 2 == 1 ? Vector2D{vecPixelSize.y, vecPixelSize.x} : vecPixelSize;
vecSize = (xfmd / scale).round();
vecTransformedSize = xfmd;
if (createdByUser) {
CBox transformedBox = {0, 0, vecTransformedSize.x, vecTransformedSize.y};
transformedBox.transform(wlTransformToHyprutils(invertTransform(transform)), vecTransformedSize.x, vecTransformedSize.y);
vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
}
updateMatrix();
if (WAS10B != enabled10bit || OLDRES != vecPixelSize)
g_pHyprOpenGL->destroyMonitorResources(self.lock());
g_pCompositor->arrangeMonitors();
damage.setSize(vecTransformedSize);
// Set scale for all surfaces on this monitor, needed for some clients
// but not on unsafe state to avoid crashes
if (!g_pCompositor->m_bUnsafeState) {
for (auto const& w : g_pCompositor->m_vWindows) {
w->updateSurfaceScaleTransformDetails();
}
}
// updato us
g_pHyprRenderer->arrangeLayersForMonitor(ID);
// reload to fix mirrors
g_pConfigManager->m_bWantsMonitorReload = true;
Debug::log(LOG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", szName, vecPixelSize, refreshRate, scale, (int)transform, vecPosition,
(int)enabled10bit);
EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr);
events.modeChanged.emit();
return true;
}
void CMonitor::addDamage(const pixman_region32_t* rg) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
@@ -368,18 +768,18 @@ void CMonitor::addDamage(const pixman_region32_t* rg) {
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
void CMonitor::addDamage(const CRegion* rg) {
addDamage(const_cast<CRegion*>(rg)->pixman());
void CMonitor::addDamage(const CRegion& rg) {
addDamage(const_cast<CRegion*>(&rg)->pixman());
}
void CMonitor::addDamage(const CBox* box) {
void CMonitor::addDamage(const CBox& box) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
if (damage.damage(*box))
if (damage.damage(box))
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
@@ -410,7 +810,7 @@ bool CMonitor::matchesStaticSelector(const std::string& selector) const {
// match by description
const auto DESCRIPTIONSELECTOR = selector.substr(5);
return DESCRIPTIONSELECTOR == szShortDescription || DESCRIPTIONSELECTOR == szDescription;
return szDescription.starts_with(DESCRIPTIONSELECTOR) || szShortDescription.starts_with(DESCRIPTIONSELECTOR);
} else {
// match by selector
return szName == selector;
@@ -528,7 +928,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
setupDefaultWS(RULE);
g_pHyprRenderer->applyMonitorRule(self.lock(), (SMonitorRule*)&RULE, true); // will apply the offset and stuff
applyMonitorRule((SMonitorRule*)&RULE, true); // will apply the offset and stuff
} else {
PHLMONITOR BACKUPMON = nullptr;
for (auto const& m : g_pCompositor->m_vMonitors) {
@@ -539,7 +939,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
}
// move all the WS
std::deque<PHLWORKSPACE> wspToMove;
std::vector<PHLWORKSPACE> wspToMove;
for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_pMonitor == self || !w->m_pMonitor)
wspToMove.push_back(w);
@@ -627,17 +1027,17 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
if (!noFocus && !g_pCompositor->m_pLastMonitor->activeSpecialWorkspace &&
!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
auto pWindow = pWorkspace->getLastFocusedWindow();
auto pWindow = pWorkspace->m_bHasFullscreenWindow ? pWorkspace->getFullscreenWindow() : pWorkspace->getLastFocusedWindow();
if (!pWindow) {
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
pWindow = pWorkspace->getTopLeftWindow();
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
pWindow = pWorkspace->getFirstWindow();
}
g_pCompositor->focusWindow(pWindow);
@@ -738,16 +1138,16 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && !w->isX11OverrideRedirect()) {
// 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.goal();
Vector2D pos = w->m_vRealPosition->goal();
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.goal() / 2.f;
pos = middle() / 2.f - w->m_vRealSize->goal() / 2.f;
} else
pos = pos - PMONFROMMIDDLE->vecPosition + vecPosition;
w->m_vRealPosition = pos;
w->m_vPosition = pos;
*w->m_vRealPosition = pos;
w->m_vPosition = pos;
}
}
}
@@ -780,6 +1180,29 @@ void CMonitor::moveTo(const Vector2D& pos) {
vecPosition = pos;
}
SWorkspaceIDName CMonitor::getPrevWorkspaceIDName(const WORKSPACEID id) {
while (!prevWorkSpaces.empty()) {
const int PREVID = prevWorkSpaces.top();
prevWorkSpaces.pop();
if (PREVID == id) // skip same workspace
continue;
// recheck if previous workspace's was moved to another monitor
const auto ws = g_pCompositor->getWorkspaceByID(PREVID);
if (ws && ws->monitorID() == ID)
return {.id = PREVID, .name = ws->m_szName};
}
return {.id = WORKSPACE_INVALID};
}
void CMonitor::addPrevWorkspaceID(const WORKSPACEID id) {
if (!prevWorkSpaces.empty() && prevWorkSpaces.top() == id)
return;
prevWorkSpaces.emplace(id);
}
Vector2D CMonitor::middle() {
return vecPosition + vecSize / 2.f;
}
@@ -879,19 +1302,15 @@ bool CMonitor::attemptDirectScanout() {
// wait for the explicit fence if present, and if kms explicit is allowed
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled;
int explicitWaitFD = -1;
CFileDescriptor explicitWaitFD;
if (DOEXPLICIT) {
explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
if (explicitWaitFD < 0)
if (!explicitWaitFD.isValid())
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd");
}
DOEXPLICIT = DOEXPLICIT && explicitWaitFD >= 0;
DOEXPLICIT = DOEXPLICIT && explicitWaitFD.isValid();
auto cleanup = CScopeGuard([explicitWaitFD, this]() {
output->state->resetExplicitFences();
if (explicitWaitFD >= 0)
close(explicitWaitFD);
});
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
@@ -901,8 +1320,8 @@ bool CMonitor::attemptDirectScanout() {
output->state->resetExplicitFences();
if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD);
output->state->setExplicitInFence(explicitWaitFD);
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD.get());
output->state->setExplicitInFence(explicitWaitFD.get());
}
bool ok = output->commit();
@@ -1008,11 +1427,7 @@ void CMonitor::onMonitorFrame() {
g_pHyprRenderer->renderMonitor(self.lock());
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
}
CMonitorState::~CMonitorState() {
CMonitorState::CMonitorState(CMonitor* owner) : m_pOwner(owner) {
;
}

View File

@@ -1,11 +1,13 @@
#pragma once
#include "../defines.hpp"
#include <deque>
#include "WLClasses.hpp"
#include <stack>
#include <vector>
#include "../SharedDefs.hpp"
#include "MiscFunctions.hpp"
#include "WLClasses.hpp"
#include <array>
#include <memory>
#include <xf86drmMode.h>
#include "Timer.hpp"
#include "math/Math.hpp"
@@ -14,9 +16,10 @@
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
// Enum for the different types of auto directions, e.g. auto-left, auto-up.
enum eAutoDirs {
enum eAutoDirs : uint8_t {
DIR_AUTO_NONE = 0, /* None will be treated as right. */
DIR_AUTO_UP,
DIR_AUTO_DOWN,
@@ -30,7 +33,7 @@ struct SMonitorRule {
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60;
float refreshRate = 60; // Hz
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
@@ -45,7 +48,7 @@ class CSyncTimeline;
class CMonitorState {
public:
CMonitorState(CMonitor* owner);
~CMonitorState();
~CMonitorState() = default;
bool commit();
bool test();
@@ -89,10 +92,8 @@ class CMonitor {
CDamageRing damage;
SP<Aquamarine::IOutput> output;
float refreshRate = 60;
int framesToSkip = 0;
float refreshRate = 60; // Hz
int forceFullFrames = 0;
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
float xwaylandScale = 1.f;
@@ -163,9 +164,10 @@ class CMonitor {
// methods
void onConnect(bool noRule);
void onDisconnect(bool destroy = false);
bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false);
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg);
void addDamage(const CBox* box);
void addDamage(const CRegion& rg);
void addDamage(const CBox& box);
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&);
bool isMirror();
@@ -190,6 +192,7 @@ class CMonitor {
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
WP<CWindow> m_previousFSWindow;
// For the list lookup
@@ -197,11 +200,16 @@ class CMonitor {
return vecPosition == rhs.vecPosition && vecSize == rhs.vecSize && szName == rhs.szName;
}
private:
void setupDefaultWS(const SMonitorRule&);
WORKSPACEID findAvailableDefaultWS();
// workspace previous per monitor functionality
SWorkspaceIDName getPrevWorkspaceIDName(const WORKSPACEID id);
void addPrevWorkspaceID(const WORKSPACEID id);
bool doneScheduled = false;
private:
void setupDefaultWS(const SMonitorRule&);
WORKSPACEID findAvailableDefaultWS();
bool doneScheduled = false;
std::stack<WORKSPACEID> prevWorkSpaces;
struct {
CHyprSignalListener frame;

View File

@@ -1,62 +1,59 @@
#include "SdDaemon.hpp"
#include "memory/Memory.hpp"
#include <memory>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <cerrno>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stdlib.h>
#include <cstdlib>
#include <cstring>
namespace Systemd {
int SdBooted(void) {
if (!faccessat(AT_FDCWD, "/run/systemd/system/", F_OK, AT_SYMLINK_NOFOLLOW))
return true;
int NSystemd::sdBooted() {
if (!faccessat(AT_FDCWD, "/run/systemd/system/", F_OK, AT_SYMLINK_NOFOLLOW))
return true;
if (errno == ENOENT)
return false;
if (errno == ENOENT)
return false;
return -errno;
}
int SdNotify(int unsetEnvironment, const char* state) {
int fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
constexpr char envVar[] = "NOTIFY_SOCKET";
auto cleanup = [unsetEnvironment, envVar](int* fd) {
if (unsetEnvironment)
unsetenv(envVar);
close(*fd);
};
std::unique_ptr<int, decltype(cleanup)> fdCleaup(&fd, cleanup);
const char* addr = getenv(envVar);
if (!addr)
return 0;
// address length must be at most this; see man 7 unix
size_t addrLen = strnlen(addr, 107);
struct sockaddr_un unixAddr;
unixAddr.sun_family = AF_UNIX;
strncpy(unixAddr.sun_path, addr, addrLen);
if (unixAddr.sun_path[0] == '@')
unixAddr.sun_path[0] = '\0';
if (connect(fd, (const sockaddr*)&unixAddr, sizeof(struct sockaddr_un)) < 0)
return -errno;
// arbitrary value which seems to be enough for s-d messages
ssize_t stateLen = strnlen(state, 128);
if (write(fd, state, stateLen) == stateLen)
return 1;
return -errno;
}
return -errno;
}
int NSystemd::sdNotify(int unsetEnvironment, const char* state) {
int fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
constexpr char envVar[] = "NOTIFY_SOCKET";
auto cleanup = [unsetEnvironment, envVar](const int* fd) {
if (unsetEnvironment)
unsetenv(envVar);
close(*fd);
};
std::unique_ptr<int, decltype(cleanup)> fdCleaup(&fd, cleanup);
const char* addr = getenv(envVar);
if (!addr)
return 0;
// address length must be at most this; see man 7 unix
size_t addrLen = strnlen(addr, 107);
struct sockaddr_un unixAddr;
unixAddr.sun_family = AF_UNIX;
strncpy(unixAddr.sun_path, addr, addrLen);
if (unixAddr.sun_path[0] == '@')
unixAddr.sun_path[0] = '\0';
if (connect(fd, (const sockaddr*)&unixAddr, sizeof(struct sockaddr_un)) < 0)
return -errno;
// arbitrary value which seems to be enough for s-d messages
ssize_t stateLen = strnlen(state, 128);
if (write(fd, state, stateLen) == stateLen)
return 1;
return -errno;
}

View File

@@ -1,6 +1,6 @@
#pragma once
namespace Systemd {
int SdBooted(void);
int SdNotify(int unset_environment, const char* state);
namespace NSystemd {
int sdBooted(void);
int sdNotify(int unset_environment, const char* state);
}

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