Compare commits

...

256 Commits

Author SHA1 Message Date
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
Vaxry
a425fbebe4 version: bump to 0.45.0 2024-11-09 14:27:47 +00:00
Vaxry
cca227a53e tablet: use inputMgr unified naming scheme
ref #8301
2024-11-09 02:34:04 +00:00
Vaxry
b9c439a55e compositor: make sure we don't ret early on no surface
if there is no implicit surface passed, make sure the current focus is not null, otherwise we nope early without focusing the window

fixes #8293
2024-11-09 02:27:01 +00:00
Vaxry
726d697821 popup: minor cleanups
don't iterate over unmapped popups in breadthfirst, don't refocus if it wasnt in focus

ref #8293
2024-11-09 02:26:24 +00:00
Vaxry
0ccc0ace88 input: ignore non-kb ls-es in refocusLastWindow
ref #8293
2024-11-09 02:25:54 +00:00
Vaxry
f43d4a8638 layershell: minor cleanups and improvements to focus
ref #8293
2024-11-09 02:25:34 +00:00
Vaxry
4c7a2faf85 input: cleanup device naming logic
ref #8301
2024-11-09 01:53:05 +00:00
nickodei
3b66351eeb input: Refocus window on scrolling if follows mouse (#8361) 2024-11-08 17:25:37 +00:00
Mihai Fufezan
e58e97b0a3 Nix: build aquamarine and hyprutils in debug when using hyprland-debug 2024-11-07 10:54:52 +02:00
Mihai Fufezan
2dd0b2af71 flake.lock: update 2024-11-07 10:54:52 +02:00
dawsers
2ec2b3bfb3 renderer: minor fixup to window rendering logic (#8359)
Don't render animating windows out of their monitor when they are not moving workspaces
2024-11-06 23:30:17 +00:00
Vaxry
0ec128e5ed renderer: don't rely on datarootdir for local share 2024-11-06 22:11:46 +00:00
Mihai Fufezan
083a5cf3c1 CI: update actions 2024-11-06 19:50:01 +02:00
Aqa-Ib
8f42401aa8 groups: add merge_groups_on_groupbar (#8362) 2024-11-06 16:52:10 +00:00
Vaxry
97a309b784 layershell: check if workspace is valid in onMap
ref #8296
2024-11-06 14:02:44 +00:00
izmyname
3bf6f78dad hyprland-systemd.desktop: change name back to Hyprland (#8351) 2024-11-05 20:14:48 +02:00
Kamikadze
e4ab28b1fb defaultConfig: update default config values for shadows (#8360) 2024-11-05 18:03:37 +00:00
Vaxry
81ad218b8b shadow: fix double premultiplication
shader takes straight alpha aaaa
2024-11-05 16:28:55 +00:00
Vaxry
64c46db087 hyprctl: add mirrorOf to hyprctl monitors
fixes #8026
2024-11-05 16:20:04 +00:00
Vaxry
0920572e70 shaders: improve corner AA in borders shader 2024-11-05 16:11:05 +00:00
Vaxry
55ccb1a8cf shaders: fixup jagged edges in texture rounded corners 2024-11-05 16:00:39 +00:00
Vaxry
d1638a09ba shadow: add sharp and refactor options
options moved to decoration:shadow:
2024-11-05 15:44:40 +00:00
Vaxry
e3882b23d0 screencopy: fix build with legacyrenderer
fixes #8355
2024-11-05 09:59:03 +00:00
Vaxry
88e9e03945 renderer: add expand_undersized_textures
adds an option to disable the texture expansion for textures that are smaller while resizing up
2024-11-04 19:45:23 +00:00
Mihai Fufezan
0fb9a04526 flake.lock: update xdph 2024-11-04 20:01:40 +02:00
Mihai Fufezan
44899cd548 nix/overlays: fix xdph overlay
Partial fix for https://github.com/hyprwm/Hyprland/issues/8343
2024-11-04 19:43:25 +02:00
Gliczy
cd0d049126 flake.lock: update xdph 2024-11-03 18:43:08 +02:00
Vaxry
180c26ada6 renderer: safeguard against non-sampleable currentFB in blurMainFb
fixes #8325
2024-11-03 15:16:13 +00:00
trianta
5833abbbd1 xwayland: minor fixups for stability (#8323)
* xwayland: add inline safe closing of fds and fix LOCK_FILE_MODE permissions

* xwayland: auto recreate xwayland instance if it crashes

* xwayland: delay auto-restart until later
2024-11-03 14:59:46 +00:00
diniamo
514e0ff509 flake: update nixpkgs 2024-11-03 12:14:49 +02:00
Vaxry
40081cb330 renderer: improve api around new framebuffer changes
ref #8325
2024-11-02 15:26:45 +00:00
Mihai Fufezan
32b18179dd CMake: systemd fixes 2024-11-02 00:20:52 +02:00
Pavel Solovev
29e7dc6428 Systemd fixes
Fix installation path, install the service only if the systemd option is enabled
2024-11-02 00:20:52 +02:00
izmyname
3c0605c68e hyprland-systemd.desktop improvements (#8318) 2024-11-01 19:21:36 +02:00
Vaxry
d8b865366a renderer: Add a missing texture asset and a user check
When an asset is missing, instead of a black screen, render an obnoxious, yet standard, missing texture.

Additionally, warn the user assets failed to load.

Shoutout to Arch for having their assets broken for months. Fix your shit. I am tired of it, and it's negatively impacting users.
2024-11-01 15:52:09 +00:00
vaxerski
3852418d24 hyprctl: reload windowrules on reloadAll 2024-11-01 13:03:12 +00:00
Ikalco
c4d214c42d monitors: fix vrr breaking monitor disconnect (#8314) 2024-11-01 12:30:26 +00:00
Mike Will
93b4478e70 snap: add option border_overlap and other improvements (#8289)
* snap: add option `border_overlap` and other improvements

I really liked the way borders used to overlap when snapping and how
only the window's main surface would snap to the monitor, so I would
like to bring that behavior back, but in the form of a config option.

Other improvements include:

- reduced the number of snap functions from 4 down to 2, and only
one ever gets called at any given time.

- border size should not be added to gap size. It seemed like the
right thing to do at the time, but it makes snapping feel way
stronger than it actually should.

- all const variables have been given the all-caps naming convention.

- to avoid excessive casting, border size is declared as a double.

- to avoid excessive x + w, y + h calculations. I'm using a struct
called Range and working only with start and end values until the
very end of the function.

- check for both monitor snapping as well as reserved monitor space
snapping in a relatively efficient way.

* snap: always border-align for corners and reserved monitor space

We probably don't want to treat reserved monitor space as if it were just
a smaller monitor. Instead, it should be treated more like a borderless
window, which means our window's border should never encroach upon it.
2024-10-31 11:21:08 +00:00
Tom Englund
7c7a84ff60 internal: more profiling less calls and local copies (#8300)
* compositor: reduce amount of window box copies

mousemoveunified can call this very frequently, the cbox copying
actually shows up as an impact in such cases, move it down in the scope
and only do it when necessery.

* core: constify and reference frequent calls

profiling shows these as frequent called functions try to reduce the
amount of copies with references and const the variables.

* pointermgr: remove not used local copy, const ref

remove unneded local copies and const ref cursorsize.

* inputmgr: reduce amount of calls to vectortowindow

the amount of calls to g_pCompositor->vectorToWindowUnified fast ramps
up in cpu usage with enough windows existing and moving the mouse, move
the PWINDOWIDEAL up and reuse it if its already the same.

* protocol: compositor remove unused local copy

remove unused local copy of accumulateCurrentBufferDamage and const
previousBuffer.

* renderer: reduce scope of variables and refactor

move a few variables down in their scopes to reduce the amount of calls
and copies when not needed, also add one more for loop in
renderWorkspaceWindows and store the windows in a vector with
weakpointers that should be rendered, this adds a loop but reduces the
amount of repeated calls to shouldRenderWindow and also makes the rest
of the loops go over way smaller vector when many windows exist.
2024-10-30 23:20:32 +00:00
Vaxry
a0b2169ed6 input: revert #8279 2024-10-30 22:14:43 +00:00
nickodei
ee91df62f0 input: simulate mouse movement after scroll to refocus window (#8279) 2024-10-30 19:12:16 +00:00
MightyPlaza
12c1bb936d internal: check size limit in layouts (#8298)
modified:   src/desktop/Window.cpp
modified:   src/desktop/Window.hpp
modified:   src/events/Windows.cpp
modified:   src/helpers/MiscFunctions.cpp
modified:   src/helpers/MiscFunctions.hpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/IHyprLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/macros.hpp
2024-10-30 18:58:36 +00:00
Aqa-Ib
5f721dce36 group: fix moveWindowIntoGroup (#8297) 2024-10-30 10:00:58 +00:00
staticssleever668
d679d20029 seat: avoid sending pointless 'keymap' and 'repeat_info' events (#8276)
#### Describe your PR, what does it fix/add?

Fix lag spikes when pressing more than 6 keys at the same time.

 #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.)

Debugging process:
<details>
This is triggered by typing some applications, like CopyQ or XWayland.
Typing in Firefox doesn't lead to lag, however it itself does lag
handling these events.

Profiling CopyQ shows that paths leading to
`QtWaylandClient::QWaylandInputDevice::Keyboard::keyboard` take over
80% of processing time of an otherwise idle program.

Looking at output of 'wev' even when it's not focused shows same events
received over and over again.

```
[14:     wl_keyboard] repeat_info: rate: 25 keys/sec; delay: 300 ms
[14:     wl_keyboard] keymap: format: 1 (xkb v1), size: 64754
```

Looking at what passes through CInputManager::onKeyboardKey() ->
CSeatManager::setKeyboard() shows Hyprland 'switching' between endpoints
of the same keyboard, one of them being named like the other but with
'-1' suffix.
</details>

Tested changing layouts in Fcitx5 and with following config.

```
input:kb_layout = us,cz
input:kb_variant = ,qwerty
input:kb_options = grp:alt_shift_toggle
```

Also tested changing 'input:repeat_delay' while running.

Curiously, now these events appear in the output of 'wev' only once.
Changing layouts still seems to work fine though.

 #### Is it ready for merging, or does it need work?

Ready for merging.
2024-10-28 19:25:27 +00:00
MightyPlaza
7188ee4f99 hyprctl: move setprop into dispatchers (#8275)
* move setprop into dispatchers
modified:   src/debug/HyprCtl.cpp
modified:   src/managers/KeybindManager.cpp
modified:   src/managers/KeybindManager.hpp

* add deprecated
modified:   src/debug/HyprCtl.cpp
2024-10-28 18:18:58 +00:00
Tom Englund
c7315617eb internal: few more marginal optimisations from profiling (#8271)
* deco: reduce local temporars and function calls

profiling shows this is a high used function, reduce the amount of
function calls and local temporar copies and also check if we even need
to add extents at all in the loop.

* popup: optimize bfhelper in popups

pass nodes as const reference and reserve the size of the children node
vector help reduce the reallocations.

* procotol: make compositor bfhelper use const ref

use const ref for nodes and reserve second nodes vector size to reduce
amount of reallocation needed.
2024-10-28 18:02:52 +00:00
Aqa-Ib
d49a1334a8 swallow: check if swallow_regex doesn't exist (#8265)
Avoid to run CWindow::getSwallower() when `swallow_regex` doesn't exist in the user's config file.
2024-10-28 16:52:14 +00:00
Mike Will
2c481202ef layout: slight adjustments to snapping logic (#8273)
fixes some bugs with resizing and corner snapping
2024-10-28 13:39:05 +00:00
Vaxry
6cf193e166 layout: don't snap to self and allow same-pid snaps
fixes #8255
2024-10-27 23:41:22 +00:00
Vaxry
b1120ec433 layout: window snapping cleanup + fixes
way better now heh

fixes #8259 fixes #8267
2024-10-27 23:39:57 +00:00
Vaxry
5d4b54b012 core: move internal structures to monitor pointers (#8266) 2024-10-27 18:45:38 +00:00
Ryan
b6e226c320 groupbar: set locked color when groups are globally locked (#8257) 2024-10-27 18:26:42 +00:00
Tom Englund
f9b52203f5 internal: optimize cursor move a bit (#8264)
* window: inline and const getWindowMainSurfaceBox

getWindowMainSurfaceBox gets called a lot of times from deep down from
mousemoveunified, profiling mousemoveunified it spends quite a lot of
cpu time in here, let the compiler optimize the call to
getWindowMainSurfaceBox by inlining and making it const. reducing the
overhead.

* inputmgr: return early and use std::any_of

return early in mousemoveunified to reduce the amount of unnecessery
calls to various pointers when not needed, also make isconstrained use
std::any_of instead of for loop to use the STL optimized paths with
hopes and dreams marginally faster.

* decoration: return early, reduce temporar copy

return earlier and reduce the temp copies by using one .lock instead of
two
2024-10-27 17:51:26 +00:00
Aqa-Ib
a3d3b4fd64 groups: fix swallowing (#8223)
* fix swallowing for groups

* remove unnecessary check

* clang-format

* clarify comment

* make variables consistent

* make aditional variables consistent
2024-10-27 01:44:55 +01:00
izmyname
f3f7d3629a Build with hyprland-session.service (#8251)
Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-10-26 22:49:00 +03:00
Damianu
c356e42500 misc: Fix bad links to wiki (#8240)
Same as in https://github.com/hyprwm/hyprland-wiki/pull/828
2024-10-26 16:50:31 +01:00
Aqa-Ib
0b29caf9ab core: fix group members disappearing when you move the group to another monitor (#8237)
* fix group members disappearance when you move the group to another monitor

* remove repeated action
2024-10-26 02:22:37 +01:00
Vaxry
3dd8db83f1 pointer: add default auto for no_hw_cursors
auto defaults to off on nvidia, on for everyone else. Gotta wait until we do fucking drm_dumb and it fucking works
2024-10-26 02:12:43 +01:00
Vaxry
d5689bb539 internal: cleanup CMonitor usage and fix a few ref hogs
ref #8221
2024-10-26 02:06:13 +01:00
izmyname
e5384774a8 example/hyprland-session.service: add support for xdg autostart (#8230) 2024-10-25 23:38:59 +03:00
Tom Englund
f0e023bff2 security-context: avoid UB in C macro (#8229)
to safely use wl_container_of with a class the class has to be no
virtual functions, no inheritance, and uniform access control (e.g all
public)

work around this by putting this into a destroywrapper struct.
2024-10-25 11:26:48 +01:00
izmyname
3cec45d821 Improve hyprland-session.service (#8225) 2024-10-24 17:50:53 +03:00
czlabinger
7f46680ab1 hyprctl: add caps/num lock state for keyboards (#8145)
---------

Co-authored-by: Behzad <behzadasbahi@gmail.com>
2024-10-24 14:01:08 +01:00
Honkazel
f603a22af0 internal: Remove some unused lambda captures (#8218) 2024-10-24 13:12:41 +01:00
Honkazel
cdac64970e Makefile: fix legacyrendererdebug typo (#8214) 2024-10-23 23:32:39 +03:00
chitoroagad
6e0aadc585 updated flake.lock 2024-10-23 07:19:55 +03:00
Aqa-Ib
2b6ff6837e groups: add group_on_movetoworkspace (#8159) 2024-10-23 00:51:25 +01:00
Vaxry
29997ef4ba default/config: improve default animations
objectively better
2024-10-22 21:34:21 +01:00
KAGEYAM4
5e96d738e6 hyprpm: Add option to notify on fail and keep original notify (#8167)
* Only generate notification on fail

Hyprpm fail/pass notification are mutually exclusive.

* Add option to notify on fail and keep original notify (#1)

* Add option to notify on fail and keep original notify
---------

Co-authored-by: KAGEYAM4 <75798544+KAGEYAM4@users.noreply.github.com>

---------

Co-authored-by: littleblack111 <littleblack11111@gmail.com>
2024-10-22 00:28:42 +01:00
Ikalco
9df0f0b66c renderer: fix floating window damage (#8182) 2024-10-21 16:09:14 +01:00
Mike Will
4093b993a2 input: add snapping to floating windows (#8088)
* add snapping to floating windows

Works for both moving and resizing of windows.
It comes with 3 options:

`general:snap:enabled` - whether it's enabled, off by default

`general:snap:window_gap` - minimum gap in pixels between windows before
snapping. Setting to 0 effectively turns off this method of snapping.

`general:snap:monitor_gap` - minimum gap in pixels between window and
monitor edges before snapping. Again, setting it to 0 effectively turns
it off.

* snap: add more ignore criteria and change if clause into a guard

* snap: refactor code

* snap: new refactoring approach and account for border size

* snap: do corner snapping after all edge snapping is done

The approach of performing corner snaps after each individual edge snap
results in far fewer scenarios where snapping can occur.

After trying it out for a while, I found that I prefer an approach
that's more prone to snapping.

* snap: combine snapWindows and snapMonitor into a single function

* snap: add forced aspect ratio functionality

* snap: avoid directly referring to border_size config value

* snap: address vaxerski feedback

- add new line between functions
- use std::function typedef for SnapFn and make snap functions static
- avoid uninitialized variable declarations.
- change ignore condition m_bIsX11 to isX11OverrideRedirect()
- use braces for CBox and Vector2D declarations.
- add SNAP_INVALID to eSnapEdge enum
- use bitshift notation for eSnapEdge and eRectCorner
- make performSnap a non-member function.

* snap: add corner-snapping to forced aspect ratio mode
2024-10-21 16:08:25 +01:00
Vaxry
08cc063e17 monitor: avoid crash on released buffer in surf 2024-10-19 23:48:25 +01:00
Vaxry
a17850e41c layersurface: fixup brace style 2024-10-19 23:47:28 +01:00
Vaxry
6a5c342063 layersurface: round geom in arrangeLayerArray
fixes #8171
2024-10-19 23:45:51 +01:00
Vaxry
f044e4c951 internal: Move CMonitor to SP (#8178)
* move monitors to sp

* XD
2024-10-19 23:03:29 +01:00
Vaxry
ce3ba798df defaultConfig: improve smart gaps 2024-10-19 17:03:22 +01:00
Vaxry
0eaf3581a3 window: guard PMONITOR in commit listener
ref #8170
2024-10-19 16:24:03 +01:00
Vaxry
62ee5cc273 monitor: modernize/refactor last legacy-handled events 2024-10-19 16:21:47 +01:00
Vaxry
48bf32c5de foreign-toplevel-wlr: don't send updates to X11 OR windows 2024-10-19 16:09:53 +01:00
Vaxry
904f9b6aee foreign-toplevel: don't send updates to X11 OR windows 2024-10-19 16:09:53 +01:00
Maximilian Seidler
e5d3a71263 config: fix generateConfig loop (#8164)
* config: fix generateConfig loop

* config: cleanup getMainConfigPath
2024-10-19 15:49:56 +01:00
Maximilian Seidler
0e630e9e74 session-lock: reset seat grab on a new session lock (#8147) 2024-10-17 21:05:55 +01:00
MightyPlaza
5f30cb7753 windowrules: allow specifying max size in size window rule (#8021)
* allow specifying max size in size window rule
modified:   src/events/Windows.cpp

* clean up
modified:   src/events/Windows.cpp
2024-10-17 21:03:17 +01:00
Behzad
495b92fb53 makefile: fix typo (#8127) 2024-10-17 15:20:18 +01:00
Vaxry
b57086aa43 window: properly break cycles in X11TransientFor
ref #8045
2024-10-16 22:23:15 +01:00
zakk4223
09581d32fd hyprpm: Fix crashes due to misplaced fmt argument(s) (#8140) 2024-10-16 22:13:59 +01:00
Aqa-Ib
86e9f69a69 layout: move applyGroupRules() to onWindowCreated() (#8139) 2024-10-16 22:13:35 +01:00
Vaxry
781828a56e output: send enter events on late wl_output binds
fixes #6560
2024-10-16 21:59:33 +01:00
vaxerski
0baf166d39 [gha] Nix: update inputs 2024-10-16 10:01:31 +00:00
Aqa-Ib
ace803948a layout: enable group rules for new floating windows (#8122)
* layout: enable group rules for new floating windows

* fix comment

* do not apply group rules to a new floating window if it shouldBeFloated.
fixes child windows

* comment
2024-10-16 10:59:47 +01:00
Aqa-Ib
01c2ff34dd layout: simplify the conditions to autogroup (#8120) 2024-10-14 19:31:17 +01:00
fanlumaster(Fany Full)
22b1370ae5 IME: Fixup IME popup candidate windows position when scale is not 1.0 (#8117) 2024-10-14 18:03:16 +01:00
deadacute
f309681d4a example: update desktop file to include DesktopNames 2024-10-14 18:26:07 +03:00
Ikalco
ce62521883 drm-lease: fix crashes and implementation (#8116) 2024-10-14 11:37:42 +01:00
Aqa-Ib
e7fd0f5aec layout: deny auto-grouping a new floating window into a tiled group (#8108) 2024-10-14 00:25:19 +01:00
Vaxry
abfd550ee2 xwm: avoid infinite parent lookup loop in lookupParentExists
ref #8045
2024-10-14 00:24:32 +01:00
Vaxry
8e51a36c7f config/example: add optional smart gaps to the default config
ref #8106 #8114
2024-10-14 00:19:10 +01:00
Vaxry
5c3bd8e93d notif-overlay: add a bit of padding for icons 2024-10-13 17:26:44 +01:00
Toni500git
05a5e0b4f1 hyprland: convert std::cout and std::cerr to std::println() 2024-10-13 14:13:56 +01:00
Toni500git
b61d4c3636 hyprctl: convert std::cout and std::cerr to std::println() 2024-10-13 14:13:56 +01:00
Toni500git
e79d3cd2ef hyprpm: convert std::cout and std::cerr to std::println() 2024-10-13 14:13:56 +01:00
Vaxry
1822707c7e drm-syncobj: fix crash on missing timelines
fixes #8092
2024-10-12 17:56:46 +01:00
Vaxry
c3f7c9bbb5 xcursor: don't crash on broken permissions in X themes
ref #8079
2024-10-12 15:18:39 +01:00
UjinT34
ee8116ac5d input: Fix VRR for constrained cursors (#6877) 2024-10-12 01:29:51 +01:00
Edgars Cīrulis
f5db483973 drm-timeline: Add check for conflicting acquire and release points (#8083)
Signed-off-by: Edgars Cirulis <edgarsciruliss@gmail.com>
2024-10-12 01:12:07 +01:00
Toni500github
7564b26b7d internal: improve version query and define HYPRLAND_VERSION (#8034) 2024-10-11 12:19:16 +01:00
Jasson
178a300eea xwayland: minor cleanups and fixes (#8076) 2024-10-11 12:07:25 +01:00
Vaxry
d655a10381 config/layout: nuke no_gaps_when_only (#8072) 2024-10-11 10:56:19 +01:00
Vaxry
b65773bea9 hyprpm: disallow shallow on unknown branch 2024-10-10 11:01:13 +01:00
Vaxry
c4eb194033 gammactrl: guard pMonitor in setGamma 2024-10-09 22:00:06 +01:00
Aqa-Ib
6ae89940c7 layout: add merge_floated_into_tiled_on_groupbar (#8042) 2024-10-09 10:58:49 +01:00
JManch
3d28879c26 hyprerror: fix height calc with bottom bar (#8043) 2024-10-09 10:24:35 +01:00
trianta
223dcc8bac output: update state even if no owner exists (#8044) 2024-10-09 10:24:05 +01:00
Mihai Fufezan
6ce07ee864 CI/release: remove script backup line 2024-10-09 12:00:43 +03:00
Vaxry
bc299928ad output/xdg-output: avoid sending events to released globals
ref #6835
2024-10-09 00:26:40 +01:00
Vaxry
ac658500fb keyboard: update group state on change for the sym resolve state
fixes #8038
2024-10-08 23:33:10 +01:00
Vaxry
8cced091f5 renderer: reserve space for error at the bottom if that's set
ref #8040
2024-10-08 21:58:40 +01:00
Vaxry
91299f7039 hyprerror: make hyprerror reserve space (#8040) 2024-10-08 21:20:25 +01:00
Vaxry
60308a2bb5 defaultConfig: add a nofocus rule for weird X windows
ref #6543
2024-10-08 20:28:34 +01:00
Aqa-Ib
613eac4603 layout: remove unnecessary check after 45e8219 (#8037) 2024-10-08 19:31:15 +01:00
JManch
e4a26f4f1d dispatchers: allow leading whitespace in window parameter (#8016) 2024-10-08 17:50:06 +01:00
Vaxry
57b632ead8 pointer: expand sw cursor damage box
fixes #8031

just a bit, rounding errors I guess
2024-10-08 17:03:19 +01:00
Vaxry
1bf63dfdcd protocols: Add support for hyprland-ctm-control-v1 (#8023)
* initial ctm support

* flake.lock: update

* Meson: bump required versions and add ctm proto

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-10-08 16:59:15 +01:00
vaxerski
e0cfbec66b keybinds: fixup xkb_states for resolve_by_sym
fixes #7750
2024-10-08 13:15:58 +01:00
davc0n
b3a7e3109b misc: refactor version command (#8027)
Fixes a minor spacing issue if git status is not dirty.
Additionally now should be easier to extend it eventually.
2024-10-08 11:42:51 +01:00
Aqa-Ib
45e82199fb layout: add drag_into_group to control merging dragging windows (#8004) 2024-10-08 11:20:41 +01:00
Vaxry
4711796d38 config: give simple help for super+q not working
only on default config :P
2024-10-08 09:54:25 +01:00
Ikalco
0d70c44253 screencopy: fix screencopy frames not being cleaned up (#8017)
---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
2024-10-08 01:23:48 +01:00
MightyPlaza
a364df4c9e internal: use clampWindowSize to unify min/maxsize handling (#8014)
modified:   src/desktop/Window.cpp
modified:   src/desktop/Window.hpp
modified:   src/events/Windows.cpp
2024-10-07 19:52:49 +01:00
Artur Manuel
46d990f1b6 feat: add a custom made treewide formatter (#7992) 2024-10-07 21:49:19 +03:00
Timon Schelling
5bf7b1e1fa flake.nix: add xdph follows
fixes a duplicate  hyprland-protocols instance sometimes being created due to xdph not following hyprlands instance
2024-10-07 21:43:35 +03:00
Aqa-Ib
97444ed7a8 layout: fix auto group when opening a new window in a non-focused workspace using window rules (#8006) 2024-10-07 13:22:55 +01:00
Vaxry
4e41cda27e security-context: close client fds after disconnect 2024-10-06 15:08:26 +01:00
Vaxry
da86aac0f5 security-context: implement protocol
fixes #7318
2024-10-06 14:07:07 +01:00
259 changed files with 8300 additions and 4627 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

@@ -33,7 +33,9 @@ runs:
libfontenc \
libglvnd \
libinput \
libjxl \
libliftoff \
libwebp \
libxcursor \
libxcvt \
libxfont2 \
@@ -58,7 +60,8 @@ runs:
xcb-util \
xcb-util-image \
libzip \
librsvg
librsvg \
re2
- name: Get hyprwayland-scanner-git
shell: bash
@@ -69,6 +72,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

@@ -39,7 +39,7 @@ jobs:
tar -cvf Hyprland.tar.xz hyprland
- name: Release
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Build archive
path: Hyprland.tar.xz

View File

@@ -17,14 +17,14 @@ jobs:
run: sudo apt install pandoc
- name: Clone repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT }}
- name: Build man pages
run: make man
- uses: stefanzweifel/git-auto-commit-action@v4
- uses: stefanzweifel/git-auto-commit-action@v5
name: Commit
with:
commit_message: "[gha] build man pages"

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

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT }}
@@ -22,6 +22,6 @@ jobs:
run: nix/update-inputs.sh
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v4
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "[gha] Nix: update inputs"

View File

@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout Hyprland
id: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive
@@ -20,7 +20,6 @@ jobs:
run: |
git fetch --unshallow || echo "failed unshallowing"
bash -c scripts/generateVersion.sh
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak
- name: Create tarball with submodules
id: tar

View File

@@ -13,7 +13,7 @@ jobs:
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Scan with Flawfinder
uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c

View File

@@ -19,7 +19,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/stale@v5
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.STALEBOT_PAT }}
stale-issue-label: "stale"

4
.gitignore vendored
View File

@@ -14,6 +14,7 @@ _deps
build/
result*
/.pre-commit-config.yaml
/.vscode/
/.idea/
.envrc
@@ -39,3 +40,6 @@ PKGBUILD
src/version.h
hyprpm/Makefile
hyprctl/Makefile
**/.#*.*
**/#*.*#

View File

@@ -15,6 +15,7 @@ include(GNUInstallDirs)
set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
set(BINDIR ${CMAKE_INSTALL_BINDIR})
configure_file(hyprland.pc.in hyprland.pc @ONLY)
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
@@ -71,6 +72,8 @@ else()
message(STATUS "Configuring Hyprland in Release with CMake")
endif()
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
include_directories(. "src/" "protocols/")
set(CMAKE_CXX_STANDARD 26)
@@ -98,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)
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.2.3)
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
@@ -121,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)
@@ -219,6 +226,15 @@ if(NO_SYSTEMD)
else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
add_compile_definitions(USES_SYSTEMD)
# session file -uwsm
if(NO_UWSM)
message(STATUS "UWSM support is disabled...")
else()
message(STATUS "UWSM support is enabled (NO_UWSM not defined)...")
install(FILES ${CMAKE_SOURCE_DIR}/systemd/hyprland-uwsm.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
endif()
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
@@ -232,7 +248,7 @@ 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()
@@ -274,7 +290,7 @@ endfunction()
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.2.0)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
if(hyprland_protocols_dep_FOUND)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
@@ -301,6 +317,8 @@ 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" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false)
@@ -331,6 +349,7 @@ protocolnew("staging/drm-lease" "drm-lease-v1" false)
protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)
protocolwayland()

View File

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

View File

@@ -100,7 +100,7 @@ easy IPC, much more QoL stuff than other compositors and more...
<!----------------------------------------------------------------------------->
[Configure]: https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
[Configure]: https://wiki.hyprland.org/Configuring/
[Stars]: https://starchart.cc/hyprwm/Hyprland
[Hypr]: https://github.com/hyprwm/Hypr

View File

@@ -1 +1 @@
0.44.0
0.46.0

View File

@@ -3,7 +3,7 @@
First of all, please remember to:
- Check that your issue is not a duplicate
- Read the [FAQ](https://wiki.hyprland.org/FAQ/)
- Read the [Configuring Page](https://wiki.hyprland.org/Configuring/Configuring-Hyprland)
- Read the [Configuring Page](https://wiki.hyprland.org/Configuring/)
<br/>

View File

@@ -1,14 +0,0 @@
[Unit]
Description=Hyprland - Tiling compositor with the looks
Documentation=man:Hyprland(1)
BindsTo=graphical-session.target
Before=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.target
[Service]
Type=notify
ExecStart=/usr/bin/Hyprland
ExecStop=/usr/bin/hyprctl dispatch exit
Restart=on-failure
Slice=session.slice

View File

@@ -1,6 +1,6 @@
# This is an example Hyprland config file.
# Refer to the wiki for more information.
# https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
# https://wiki.hyprland.org/Configuring/
# Please note not all available settings / options are set here.
# For a full list, see the wiki
@@ -86,10 +86,12 @@ decoration {
active_opacity = 1.0
inactive_opacity = 1.0
drop_shadow = true
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
shadow {
enabled = true
range = 4
render_power = 3
color = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
@@ -103,20 +105,44 @@ decoration {
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = true
enabled = yes, please :)
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = myBezier, 0.05, 0.9, 0.1, 1.05
bezier = easeOutQuint,0.23,1,0.32,1
bezier = easeInOutCubic,0.65,0.05,0.36,1
bezier = linear,0,0,1,1
bezier = almostLinear,0.5,0.5,0.75,1.0
bezier = quick,0.15,0,0.1,1
animation = windows, 1, 7, myBezier
animation = windowsOut, 1, 7, default, popin 80%
animation = border, 1, 10, default
animation = borderangle, 1, 8, default
animation = fade, 1, 7, default
animation = workspaces, 1, 6, default
animation = global, 1, 10, default
animation = border, 1, 5.39, easeOutQuint
animation = windows, 1, 4.79, easeOutQuint
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
animation = windowsOut, 1, 1.49, linear, popin 87%
animation = fadeIn, 1, 1.73, almostLinear
animation = fadeOut, 1, 1.46, almostLinear
animation = fade, 1, 3.03, quick
animation = layers, 1, 3.81, easeOutQuint
animation = layersIn, 1, 4, easeOutQuint, fade
animation = layersOut, 1, 1.5, linear, fade
animation = fadeLayersIn, 1, 1.79, almostLinear
animation = fadeLayersOut, 1, 1.39, almostLinear
animation = workspaces, 1, 1.94, almostLinear, fade
animation = workspacesIn, 1, 1.21, almostLinear, fade
animation = workspacesOut, 1, 1.94, almostLinear, fade
}
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@@ -255,4 +281,8 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0

View File

@@ -2,4 +2,6 @@
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
Exec=Hyprland
Type=Application
Type=Application
DesktopNames=Hyprland
Keywords=tiling;wayland;compositor;

168
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1727261104,
"narHash": "sha256-rxDI7WrxIRV9it9mDCHcLa7xQykf1JloXnoXr5xQ8zI=",
"lastModified": 1734364797,
"narHash": "sha256-2h1c+P0v3l0Z/ypUSsAPhU/yiSRgFwjVFODWp0S3d/w=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "b82fdaff917582a9d568969e15e61b398c71e990",
"rev": "8e77618b403a82fde2105a8e3cd7cabe7ef00952",
"type": "github"
},
"original": {
@@ -29,6 +29,43 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"hyprcursor": {
"inputs": {
"hyprlang": [
@@ -42,11 +79,11 @@
]
},
"locked": {
"lastModified": 1727532803,
"narHash": "sha256-ZaZ7h7PY8mQc4vtGmVqWLAq9CAO02gHMyNR5yY8zDmM=",
"lastModified": 1734364709,
"narHash": "sha256-+2bZJL2u5hva7rSp65OfKJBK+k03T6GB/NCvpoS1OOo=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "b98726e431d4d3ed58bd58bee1047cdb81cec69f",
"rev": "f388aacd22be4a6e4d634fbaf6f75eb0713d239a",
"type": "github"
},
"original": {
@@ -55,6 +92,32 @@
"type": "github"
}
},
"hyprgraphics": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1733684019,
"narHash": "sha256-2kYREgmSmbLsmDpLEq96hxVAU3qz8aCvVhF65yCFZHY=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "fb2c0268645a77403af3b8a4ce8fa7ba5917f15d",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprgraphics",
"type": "github"
}
},
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
@@ -65,11 +128,11 @@
]
},
"locked": {
"lastModified": 1727451107,
"narHash": "sha256-qV9savtHwmZUa0eJE294WYJjKPGB2+bJhwByFShsVyo=",
"lastModified": 1728345020,
"narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "6b3261ee13a6d2b99de79a31d352f6996e35bde3",
"rev": "a7c183800e74f337753de186522b9017a07a8cee",
"type": "github"
},
"original": {
@@ -78,28 +141,29 @@
"type": "github"
}
},
"hyprland-protocols_2": {
"hyprland-qtutils": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"nixpkgs": [
"xdph",
"nixpkgs"
],
"systems": [
"xdph",
"systems"
]
},
"locked": {
"lastModified": 1721326555,
"narHash": "sha256-zCu4R0CSHEactW9JqYki26gy8h9f6rHmSwj4XJmlHgg=",
"lastModified": 1733940128,
"narHash": "sha256-hmfXWj2GA9cj1QUkPFYtAAeohhs615zL4E3APy3FnvQ=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "5a11232266bf1a1f5952d5b179c3f4b2facaaa84",
"repo": "hyprland-qtutils",
"rev": "3833097e50473a152dd614d4b468886840b4ea78",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-protocols",
"repo": "hyprland-qtutils",
"type": "github"
}
},
@@ -116,11 +180,11 @@
]
},
"locked": {
"lastModified": 1725997860,
"narHash": "sha256-d/rZ/fHR5l1n7PeyLw0StWMNLXVU9c4HFyfskw568so=",
"lastModified": 1734364628,
"narHash": "sha256-ii8fzJfI953n/EmIxVvq64ZAwhvwuuPHWfGd61/mJG8=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "dfeb5811dd6485490cce18d6cc1e38a055eea876",
"rev": "16e59c1eb13d9fb6de066f54e7555eb5e8a4aba5",
"type": "github"
},
"original": {
@@ -139,11 +203,11 @@
]
},
"locked": {
"lastModified": 1727300645,
"narHash": "sha256-OvAtVLaSRPnbXzOwlR1fVqCXR7i+ICRX3aPMCdIiv+c=",
"lastModified": 1733502241,
"narHash": "sha256-KAUNC4Dgq8WQjYov5auBw/usaHixhacvb7cRDd0AG/k=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "3f5293432b6dc6a99f26aca2eba3876d2660665c",
"rev": "104117aed6dd68561be38b50f218190aa47f2cd8",
"type": "github"
},
"original": {
@@ -177,11 +241,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1727348695,
"narHash": "sha256-J+PeFKSDV+pHL7ukkfpVzCOO7mBSrrpJ3svwBFABbhI=",
"lastModified": 1734119587,
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1925c603f17fc89f4c8f6bf6f631a802ad85d784",
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
"type": "github"
},
"original": {
@@ -191,15 +255,57 @@
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1734279981,
"narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols",
"hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems",
"xdph": "xdph"
}
@@ -221,7 +327,9 @@
},
"xdph": {
"inputs": {
"hyprland-protocols": "hyprland-protocols_2",
"hyprland-protocols": [
"hyprland-protocols"
],
"hyprlang": [
"hyprlang"
],
@@ -239,11 +347,11 @@
]
},
"locked": {
"lastModified": 1727524473,
"narHash": "sha256-1DGktDtSWIJpnDbVoj/qpvJSH5zg6JbOfuh6xqZMap0=",
"lastModified": 1734124279,
"narHash": "sha256-YNpFfiQjYt2o6LGcMN9NkjVvprC8ELrIpLHlbZbclRM=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "7e500e679ede40e79cf2d89b5f5fa3e34923bd26",
"rev": "0c6861f819f6d31f6195c9864709b2556f00b5cf",
"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.hyprutils.follows = "hyprutils";
};
hyprlang = {
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
@@ -51,10 +65,16 @@
url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprland-protocols.follows = "hyprland-protocols";
inputs.hyprlang.follows = "hyprlang";
inputs.hyprutils.follows = "hyprutils";
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
};
pre-commit-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs @ {
@@ -76,7 +96,7 @@
pkgsCrossFor = eachSystem (system: crossSystem:
import nixpkgs {
localSystem = system;
crossSystem = crossSystem;
inherit crossSystem;
overlays = with self.overlays; [
hyprland-packages
hyprland-extras
@@ -91,6 +111,18 @@
self.packages.${system})
// {
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
hyprland-treewide-formatter = {
enable = true;
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
pass_filenames = false;
excludes = ["subprojects"];
always_run = true;
};
};
};
});
packages = eachSystem (system: {
@@ -98,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;
@@ -119,10 +149,11 @@
hardeningDisable = ["fortify"];
inputsFrom = [pkgsFor.${system}.hyprland];
packages = [pkgsFor.${system}.clang-tools];
inherit (self.checks.${system}.pre-commit-check) shellHook;
};
});
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
nixosModules.default = import ./nix/module.nix inputs;
homeManagerModules.default = import ./nix/hm-module.nix self;

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,5 +1,7 @@
#pragma once
#include <string_view>
const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help]
commands:

View File

@@ -1,3 +1,5 @@
#include <re2/re2.h>
#include <cctype>
#include <netdb.h>
#include <netinet/in.h>
@@ -17,13 +19,12 @@
#include <iostream>
#include <string>
#include <print>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <filesystem>
#include <cstdarg>
#include <regex>
#include <sys/socket.h>
#include <hyprutils/string/String.hpp>
#include <cstring>
@@ -44,18 +45,24 @@ struct SInstanceData {
bool valid = true;
};
void log(std::string str) {
void log(const std::string& str) {
if (quiet)
return;
std::cout << str << "\n";
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";
}
@@ -65,6 +72,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;
@@ -105,7 +117,7 @@ std::vector<SInstanceData> instances() {
static volatile bool sigintReceived = false;
void intHandler(int sig) {
sigintReceived = true;
std::cout << "[hyprctl] SIGINT received, closing connection" << std::endl;
std::println("[hyprctl] SIGINT received, closing connection");
}
int rollingRead(const int socket) {
@@ -115,12 +127,12 @@ int rollingRead(const int socket) {
constexpr size_t BUFFER_SIZE = 8192;
std::array<char, BUFFER_SIZE> buffer = {0};
long sizeWritten = 0;
std::cout << "[hyprctl] reading from socket following up log:" << std::endl;
std::println("[hyprctl] reading from socket following up log:");
while (!sigintReceived) {
sizeWritten = read(socket, buffer.data(), BUFFER_SIZE);
if (sizeWritten < 0 && errno != EAGAIN) {
if (errno != EINTR)
std::cout << "Couldn't read (5) " << strerror(errno) << ":" << errno << std::endl;
std::println("Couldn't read (5): {}: {}", strerror(errno), errno);
close(socket);
return 5;
}
@@ -129,7 +141,7 @@ int rollingRead(const int socket) {
break;
if (sizeWritten > 0) {
std::cout << std::string(buffer.data(), sizeWritten);
std::println("{}", std::string(buffer.data(), sizeWritten));
buffer.fill('\0');
}
@@ -162,7 +174,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;
@@ -232,7 +244,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";
@@ -270,11 +282,10 @@ 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/");
}
if (json)
RE2::GlobalReplace(&commands, ";\\s*", ";j/");
std::string rq = "[[BATCH]]" + commands;
request(rq);
@@ -310,11 +321,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;
}
@@ -323,7 +334,7 @@ int main(int argc, char** argv) {
bool parseArgs = true;
if (argc < 2) {
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
return 1;
}
@@ -360,7 +371,7 @@ int main(int argc, char** argv) {
++i;
if (i >= ARGS.size()) {
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
return 1;
}
@@ -371,24 +382,24 @@ int main(int argc, char** argv) {
const std::string& cmd = ARGS[0];
if (cmd == "hyprpaper") {
std::cout << HYPRPAPER_HELP << std::endl;
std::println("{}", HYPRPAPER_HELP);
} else if (cmd == "notify") {
std::cout << NOTIFY_HELP << std::endl;
std::println("{}", NOTIFY_HELP);
} else if (cmd == "output") {
std::cout << OUTPUT_HELP << std::endl;
std::println("{}", OUTPUT_HELP);
} else if (cmd == "plugin") {
std::cout << PLUGIN_HELP << std::endl;
std::println("{}", PLUGIN_HELP);
} else if (cmd == "setprop") {
std::cout << SETPROP_HELP << std::endl;
std::println("{}", SETPROP_HELP);
} else if (cmd == "switchxkblayout") {
std::cout << SWITCHXKBLAYOUT_HELP << std::endl;
std::println("{}", SWITCHXKBLAYOUT_HELP);
} else {
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
}
return 1;
} else {
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
return 1;
}
@@ -399,7 +410,7 @@ int main(int argc, char** argv) {
}
if (fullRequest.empty()) {
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
return 1;
}
@@ -476,7 +487,7 @@ int main(int argc, char** argv) {
else if (fullRequest.contains("/decorations"))
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/--help"))
std::cout << USAGE << std::endl;
std::println("{}", USAGE);
else if (fullRequest.contains("/rollinglog") && needRoll)
exitStatus = request(fullRequest, 0, true);
else {

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,11 @@ 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)
add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::deps)
target_link_libraries(hyprpm PUBLIC PkgConfig::hyprpm_deps)
# binary
install(TARGETS hyprpm)

View File

@@ -1,6 +1,6 @@
#include "DataState.hpp"
#include <toml++/toml.hpp>
#include <iostream>
#include <print>
#include <filesystem>
#include <fstream>
#include "PluginManager.hpp"
@@ -8,7 +8,7 @@
std::string DataState::getDataStatePath() {
const auto HOME = getenv("HOME");
if (!HOME) {
std::cerr << "DataState: no $HOME\n";
std::println(stderr, "DataState: no $HOME");
throw std::runtime_error("no $HOME");
return "";
}
@@ -242,4 +242,4 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
}
return false;
}
}

View File

@@ -1,12 +1,15 @@
#include "PluginManager.hpp"
#include "../helpers/Colors.hpp"
#include "../helpers/StringUtils.hpp"
#include "../progress/CProgressBar.hpp"
#include "Manifest.hpp"
#include "DataState.hpp"
#include <cstdio>
#include <iostream>
#include <array>
#include <filesystem>
#include <print>
#include <thread>
#include <fstream>
#include <algorithm>
@@ -20,37 +23,56 @@
#include <toml++/toml.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 "";
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::cout << Colors::BLUE << "[v] " << Colors::RESET << "version returned: " << HLVERCALL << "\n";
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
if (!HLVERCALL.contains("Tag:")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " You don't seem to be running Hyprland.";
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
return SHyprlandVersion{};
}
@@ -76,14 +98,20 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
} catch (...) { ; }
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << " on " << hldate << ", commits " << commits << "\n";
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
if (running)
verRunning = ver;
else
verInstalled = ver;
ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
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))
@@ -102,20 +130,21 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HLVER = getHyprlandVersion();
if (!hasDeps()) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio\n";
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
return false;
}
if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
return false;
}
auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) {
std::cout << Colors::YELLOW << "!" << Colors::RED << " Disclaimer:\n " << Colors::RESET
<< "plugins, especially not official, have no guarantee of stability, availablity or security.\n Run them at your own risk.\n "
<< "This message will not appear again.\n";
std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET);
std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n"
"Run them at your own risk.\n"
"This message will not appear again.");
GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE);
}
@@ -129,7 +158,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
std::println(stderr, "Aborting.");
return false;
}
@@ -140,72 +169,72 @@ 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")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for hyprpm\n";
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::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for repo\n";
std::println(stderr, "\n{}", failureString("Could not prepare working dir for repo"));
return false;
}
progress.printMessageAbove(std::string{Colors::RESET} + "Cloning " + url);
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::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. shell returned:\n" << ret << "\n";
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
return false;
}
if (!rev.empty()) {
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev);
if (ret.compare(0, 6, "fatal:") == 0) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not check out revision " << rev << ". shell returned:\n" << ret << "\n";
std::println(stderr, "\n{}", failureString("Could not check out revision {}. shell returned:\n{}", rev, ret));
return false;
}
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n";
std::println("{}", verboseString("git submodule update --init returned: {}", ret));
}
progress.m_iSteps = 1;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.printMessageAbove(successString("cloned"));
progress.m_szCurrentMessage = "Reading the manifest";
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
progress.printMessageAbove(successString("found hyprpm manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
progress.printMessageAbove(successString("found hyprload manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
return false;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
return false;
}
progress.m_iSteps = 2;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:");
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
for (auto const& pl : pManifest->m_vPlugins) {
std::string message = std::string{Colors::RESET} + " " + pl.name + " by ";
std::string message = "" + pl.name + " by ";
for (auto const& a : pl.authors) {
message += a + ", ";
}
@@ -220,19 +249,19 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins
progress.printMessageAbove(std::string{Colors::RESET} + "Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking");
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n";
std::println("{}", verboseString("git submodule update --init returned: {}", ret));
break;
}
@@ -244,12 +273,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HEADERSSTATUS = headersValid();
if (HEADERSSTATUS != HEADERS_OK) {
std::cerr << "\n" << headerError(HEADERSSTATUS);
std::println("\n{}", headerError(HEADERSSTATUS));
return false;
}
progress.m_iSteps = 3;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " Hyprland headers OK");
progress.printMessageAbove(successString("Hyprland headers OK"));
progress.m_szCurrentMessage = "Building plugin(s)";
progress.print();
@@ -257,35 +286,36 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Not building " + p.name + ": your Hyprland version is too old.\n");
progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + "Building " + p.name);
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto const& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
std::println("{}", verboseString("shell returned: {}", out));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " failed to build.\n" +
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n If you are on -git, update "
"first.\n Try re-running with -v to see "
"more verbose output.\n");
progress.printMessageAbove(failureString("Plugin {} failed to build.\n"
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
" If you are on -git, update first\n"
" Try re-running with -v to see more verbose output.\n",
p.name));
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " all plugins built");
progress.printMessageAbove(successString("all plugins built"));
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing repository";
progress.print();
@@ -304,13 +334,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
}
DataState::addNewPluginRepo(repo);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed repository");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " you can now enable the plugin(s) with hyprpm enable");
progress.printMessageAbove(successString("installed repository"));
progress.printMessageAbove(successString("you can now enable the plugin(s) with hyprpm enable"));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
// remove build files
std::filesystem::remove_all(m_szWorkingPluginDirectory);
@@ -320,7 +350,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
if (!DataState::pluginRepoExists(urlOrName)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not remove the repository. Repository is not installed.\n";
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
return false;
}
@@ -331,7 +361,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
std::println("Aborting.");
return false;
}
@@ -341,21 +371,21 @@ 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
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str());
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());
if (!headers.contains("-I/"))
return HEADERS_MISSING;
headers.pop_back(); // pop newline
std::string verHeader = "";
std::string verHeader;
while (!headers.empty()) {
const auto PATH = headers.substr(0, headers.find(" -I/", 3));
@@ -403,20 +433,20 @@ bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion();
const auto HLVER = getHyprlandVersion(false);
if (!hasDeps()) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio\n";
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) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Headers up to date.\n";
std::println("\n{}", successString("Headers up to date."));
return true;
}
@@ -427,77 +457,76 @@ 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::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for hl\n";
std::println("\n{}", failureString("Could not prepare working dir for hl"));
return false;
}
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment.");
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning https://github.com/hyprwm/Hyprland, this might take a moment."));
const bool bShallow = (HLVER.branch == "main" || HLVER.branch == "") && !m_bNoShallow;
const bool bShallow = (HLVER.branch == "main") && !m_bNoShallow;
// let us give a bit of leg-room for shallowing
// due to timezones, etc.
const std::string SHALLOW_DATE =
trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
const std::string SHALLOW_DATE = trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+%a %b %d %H:%M:%S %Y'");
if (m_bVerbose && bShallow)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE);
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(std::string{Colors::RED} + "" + Colors::RESET + " Clone failed. Retrying without shallow.");
ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME);
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/hyprland hyprland-{}", getTempRoot(), USERNAME));
}
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the hyprland repository. shell returned:\n" << ret << "\n";
std::println(stderr, "\n{}", failureString("Could not clone the Hyprland repository. shell returned:\n{}", ret));
return false;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.printMessageAbove(successString("Hyprland cloned"));
progress.m_iSteps = 2;
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will run: " + "cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
progress.printMessageAbove(verboseString("will run: cd {} && git checkout {} 2>&1", WORKINGDIR, HLVER.hash));
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
if (ret.contains("fatal: unable to read tree")) {
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET
<< " Could not checkout the running Hyprland commit. If you are on -git, try updating.\nYou can also try re-running hyprpm update with --no-shallow.\n";
std::println(stderr, "\n{}",
failureString("Could not checkout the running Hyprland commit. If you are on -git, try updating.\n"
"You can also try re-running hyprpm update with --no-shallow."));
return false;
}
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (co): " + ret);
progress.printMessageAbove(verboseString("git returned (co): {}", ret));
ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash);
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (rs): " + ret);
progress.printMessageAbove(verboseString("git returned (rs): {}", ret));
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " checked out to running ver");
progress.printMessageAbove(successString("checked out to running ver"));
progress.m_iSteps = 3;
progress.m_szCurrentMessage = "Building Hyprland";
progress.print();
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland");
progress.printMessageAbove(statusString("!", Colors::YELLOW, "configuring Hyprland"));
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath());
progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR,
DataState::getHeadersPath()));
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret);
progress.printMessageAbove(verboseString("cmake returned: {}", ret));
if (ret.contains("CMake Error at")) {
// missing deps, let the user know.
@@ -506,48 +535,46 @@ bool CPluginManager::updateHeaders(bool force) {
missing = missing.substr(0, missing.find("-- Configuring incomplete"));
missing = missing.substr(0, missing.find_last_of('\n'));
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n"
<< missing << "\n\nThis likely means that you are missing the above dependencies or they are out of date.\n";
std::println(stderr, "\n{}",
failureString("Could not configure the hyprland source, cmake complained:\n{}\n\n"
"This likely means that you are missing the above dependencies or they are out of date.",
missing));
return false;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.printMessageAbove(successString("configured Hyprland"));
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources";
progress.print();
std::string cmd =
const std::string& cmd =
std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd);
progress.printMessageAbove(verboseString("installation will run: {}", cmd));
ret = execAndGet(cmd);
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n";
std::println("{}", verboseString("installer returned: {}", ret));
// remove build files
std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed headers");
progress.printMessageAbove(successString("installed headers"));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
} else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID) + " (" +
headerErrorShort(HEADERSVALID) + ")");
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
std::cout << "\n";
std::cerr << "\n" << headerError(HEADERSVALID);
std::print(stderr, "\n\n{}", headerError(HEADERSVALID));
return false;
}
@@ -557,18 +584,18 @@ bool CPluginManager::updateHeaders(bool force) {
bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (headersValid() != HEADERS_OK) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
std::println("{}", failureString("headers are not up-to-date, please run hyprpm update."));
return false;
}
const auto REPOS = DataState::getAllRepositories();
if (REPOS.size() < 1) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " No repos to update.\n";
std::println("{}", failureString("No repos to update."));
return true;
}
const auto HLVER = getHyprlandVersion();
const auto HLVER = getHyprlandVersion(false);
CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
@@ -577,7 +604,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;
@@ -586,25 +613,26 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Updating " + repo.name;
progress.print();
progress.printMessageAbove(std::string{Colors::RESET} + "checking for updates for " + repo.name);
progress.printMessageAbove(infoString("checking for updates for {}", repo.name));
createSafeDirectory(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::RESET} + "Cloning " + repo.url);
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::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not clone repo: shell returned:\n" + ret;
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
return false;
}
if (!repo.rev.empty()) {
progress.printMessageAbove(std::string{Colors::RESET} + "Plugin has revision set, resetting: " + repo.rev);
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
if (ret.compare(0, 6, "fatal:") == 0) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not check out revision " + repo.rev + ": shell returned:\n" + ret;
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
return false;
}
}
@@ -620,7 +648,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!update) {
std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " is up-to-date.");
progress.printMessageAbove(successString("repository {} is up-to-date.", repo.name));
progress.m_iSteps++;
progress.print();
continue;
@@ -628,41 +656,41 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
// we need to update
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " has updates.");
progress.printMessageAbove(std::string{Colors::RESET} + "Building " + repo.name);
progress.printMessageAbove(successString("repository {} has updates.", repo.name));
progress.printMessageAbove(infoString("Building {}", repo.name));
progress.m_iSteps++;
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
progress.printMessageAbove(successString("found hyprpm manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
progress.printMessageAbove(successString("found hyprload manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
continue;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
continue;
}
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
// check commit pins unless a revision is specified
progress.printMessageAbove(std::string{Colors::RESET} + "Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking");
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
}
@@ -672,32 +700,33 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Not building " + p.name + ": your Hyprland version is too old.\n");
progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + "Building " + p.name);
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto const& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
std::println("{}", verboseString("shell returned: {}", out));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n"
<< " This likely means that the plugin is either outdated, not yet available for your version, or broken.\n If you are on -git, update first.\n Try "
"re-running with -v to see more verbose "
"output.\n";
std::println(stderr,
"\n{}\n"
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
"If you are on -git, update first.\n"
"Try re-running with -v to see more verbose output.",
failureString("Plugin {} failed to build.", p.name));
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
}
// add repo toml to DataState
@@ -718,7 +747,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
progress.printMessageAbove(successString("updated {}", repo.name));
}
progress.m_iSteps++;
@@ -733,7 +762,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
return true;
}
@@ -741,27 +770,27 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
bool CPluginManager::enablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, true);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Enabled " << name << "\n";
std::println("{}", successString("Enabled {}", name));
return ret;
}
bool CPluginManager::disablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, false);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Disabled " << name << "\n";
std::println("{}", successString("Disabled {}", name));
return ret;
}
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (headersValid() != HEADERS_OK) {
std::cerr << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
return LOADSTATE_HEADERS_OUTDATED;
}
const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) {
std::cerr << "PluginManager: no $HOME or HIS\n";
std::println(stderr, "PluginManager: no $HOME or HIS");
return LOADSTATE_FAIL;
}
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
@@ -770,14 +799,14 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
std::vector<std::string> loadedPlugins;
std::cout << Colors::GREEN << "" << Colors::RESET << " Ensuring plugin load state\n";
std::println("{}", successString("Ensuring plugin load state"));
// iterate line by line
while (!pluginLines.empty()) {
auto plLine = pluginLines.substr(0, pluginLines.find("\n"));
auto plLine = pluginLines.substr(0, pluginLines.find('\n'));
if (pluginLines.find("\n") != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find("\n") + 1);
if (pluginLines.find('\n') != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
else
pluginLines = "";
@@ -815,12 +844,20 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
return "";
};
// 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
for (auto const& p : loadedPlugins) {
if (!enabled(p)) {
// unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::cout << Colors::GREEN << "" << Colors::RESET << " Unloaded " << p << "\n";
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));
}
}
@@ -833,17 +870,28 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
continue;
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
std::cout << Colors::GREEN << "" << Colors::RESET << " Loaded " << p.name << "\n";
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::cout << Colors::GREEN << "" << Colors::RESET << " Plugin load state ensured\n";
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
@@ -856,16 +904,17 @@ void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories();
for (auto const& r : REPOS) {
std::cout << std::string{Colors::RESET} + "Repository " + r.name + ":\n";
std::println("{}", infoString("Repository {}:", r.name));
for (auto const& p : r.plugins) {
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name;
std::println(" │ Plugin {}", p.name);
if (!p.failed)
std::cout << "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") << Colors::RESET << "\n";
std::println(" └─ enabled: {}", (p.enabled ? std::string{Colors::GREEN} + "true" : std::string{Colors::RED} + "false"));
else
std::cout << "\n └─ enabled: " << Colors::RED << "Plugin failed to build" << Colors::RESET << "\n";
std::println(" └─ enabled: {}Plugin failed to build", Colors::RED);
std::println("{}", Colors::RESET);
}
}
}
@@ -876,19 +925,19 @@ void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int duratio
std::string CPluginManager::headerError(const eHeadersErrors err) {
switch (err) {
case HEADERS_CORRUPTED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers corrupted. Please run hyprpm update to fix those.\n";
case HEADERS_MISMATCHED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers version mismatch. Please run hyprpm update to fix those.\n";
case HEADERS_NOT_HYPRLAND: return std::string{Colors::RED} + "" + Colors::RESET + " It doesn't seem you are running on hyprland.\n";
case HEADERS_MISSING: return std::string{Colors::RED} + "" + Colors::RESET + " Headers missing. Please run hyprpm update to fix those.\n";
case HEADERS_CORRUPTED: return failureString("Headers corrupted. Please run hyprpm update to fix those.\n");
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
case HEADERS_DUPLICATED: {
return std::string{Colors::RED} + "" + Colors::RESET + " Headers duplicated!!! This is a very bad sign.\n" +
" This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" +
" If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n";
return failureString("Headers duplicated!!! This is a very bad sign.\n"
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"
"If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n");
}
default: break;
}
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
return failureString("Unknown header error. Please run hyprpm update to fix those.\n");
}
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {

View File

@@ -2,6 +2,7 @@
#include <memory>
#include <string>
#include <utility>
enum eHeadersErrors {
HEADERS_OK = 0,
@@ -26,7 +27,8 @@ enum ePluginLoadStateReturn {
LOADSTATE_OK = 0,
LOADSTATE_FAIL,
LOADSTATE_PARTIAL_FAIL,
LOADSTATE_HEADERS_OUTDATED
LOADSTATE_HEADERS_OUTDATED,
LOADSTATE_HYPRLAND_UPDATED
};
struct SHyprlandVersion {
@@ -52,7 +54,7 @@ class CPluginManager {
ePluginLoadStateReturn ensurePluginsLoadState();
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);
@@ -68,7 +70,7 @@ class CPluginManager {
std::string headerError(const eHeadersErrors err);
std::string headerErrorShort(const eHeadersErrors err);
std::string m_szWorkingPluginDirectory = "";
std::string m_szWorkingPluginDirectory;
};
inline std::unique_ptr<CPluginManager> g_pPluginManager;

View File

@@ -0,0 +1,32 @@
#pragma once
#include <format>
#include <string>
#include "Colors.hpp"
template <typename... Args>
std::string statusString(const std::string_view emoji, const std::string_view color, const std::string_view fmt, Args&&... args) {
std::string ret = std::format("{}{}{} ", color, emoji, Colors::RESET);
ret += std::vformat(fmt, std::make_format_args(args...));
return ret;
}
template <typename... Args>
std::string successString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::GREEN, fmt, args...);
}
template <typename... Args>
std::string failureString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::RED, fmt, args...);
}
template <typename... Args>
std::string verboseString(const std::string_view fmt, Args&&... args) {
return statusString("[v]", Colors::BLUE, fmt, args...);
}
template <typename... Args>
std::string infoString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::RESET, fmt, args...);
}

View File

@@ -1,15 +1,16 @@
#include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp"
#include "helpers/StringUtils.hpp"
#include "core/PluginManager.hpp"
#include "core/DataState.hpp"
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <print>
#include <chrono>
#include <thread>
const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
add [url] [git rev] Install a new plugin repository from git. Git revision
is optional, when set, commit locks are ignored.
@@ -22,7 +23,8 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
Flags:
--notify | -n Send a hyprland notification for important events (e.g. load fail)
--notify | -n Send a hyprland notification for important events (including both successes and fail events)
--notify-fail | -nn Send a hyprland notification for fail events only
--help | -h Show this menu
--verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f)
@@ -30,36 +32,38 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
)#";
int main(int argc, char** argv, char** envp) {
int main(int argc, char** argv, char** envp) {
std::vector<std::string> ARGS{argc};
for (int i = 0; i < argc; ++i) {
ARGS[i] = std::string{argv[i]};
}
if (ARGS.size() < 2) {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 1;
}
std::vector<std::string> command;
bool notify = false, verbose = false, force = false, noShallow = false;
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
if (ARGS[i] == "--help" || ARGS[i] == "-h") {
std::cout << HELP;
std::println("{}", HELP);
return 0;
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true;
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
notifyFail = notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
noShallow = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
} else {
std::cerr << "Unrecognized option " << ARGS[i] << "\n";
std::println(stderr, "Unrecognized option {}", ARGS[i]);
return 1;
}
} else {
@@ -68,7 +72,7 @@ int main(int argc, char** argv, char** envp) {
}
if (command.empty()) {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 0;
}
@@ -78,7 +82,7 @@ int main(int argc, char** argv, char** envp) {
if (command[0] == "add") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for add.\n";
std::println(stderr, "{}", failureString("Not enough args for add."));
return 1;
}
@@ -90,7 +94,7 @@ int main(int argc, char** argv, char** envp) {
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
} else if (command[0] == "remove") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for remove.\n";
std::println(stderr, "{}", failureString("Not enough args for remove."));
return 1;
}
@@ -99,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;
@@ -110,32 +114,39 @@ int main(int argc, char** argv, char** envp) {
auto ret2 = g_pPluginManager->ensurePluginsLoadState();
if (ret2 == LOADSTATE_HYPRLAND_UPDATED)
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Updated plugins, but Hyprland was updated. Please restart Hyprland.");
if (ret2 != LOADSTATE_OK)
return 1;
} else if (notify)
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
} else if (command[0] == "enable") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for enable.\n";
std::println(stderr, "{}", failureString("Not enough args for enable."));
return 1;
}
if (!g_pPluginManager->enablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't enable plugin (missing?)\n";
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
return 1;
}
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") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for disable.\n";
std::println(stderr, "{}", failureString("Not enough args for disable."));
return 1;
}
if (!g_pPluginManager->disablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't disable plugin (missing?)\n";
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
return 1;
}
@@ -154,15 +165,15 @@ int main(int argc, char** argv, char** envp) {
break;
default: break;
}
} else if (notify) {
} else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}
} else if (command[0] == "list") {
g_pPluginManager->listAllPlugins();
} else {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 1;
}
return 0;
}
}

View File

@@ -1,11 +1,11 @@
#include "CProgressBar.hpp"
#include <iostream>
#include <sys/ioctl.h>
#include <algorithm>
#include <cmath>
#include <format>
#include <sys/ioctl.h>
#include <print>
#include <stdio.h>
#include <unistd.h>
@@ -16,11 +16,12 @@ void CProgressBar::printMessageAbove(const std::string& msg) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r" << msg << "\n";
std::println("\r{}\r{}", spaces, msg);
print();
}
@@ -29,15 +30,16 @@ void CProgressBar::print() {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (m_bFirstPrint)
std::cout << "\n";
std::print("\n");
m_bFirstPrint = false;
std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r";
std::print("\r{}\r", spaces);
std::string message = "";
@@ -74,7 +76,7 @@ void CProgressBar::print() {
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
// draw message
std::cout << message + " " + m_szCurrentMessage;
std::print("{} {}", message, m_szCurrentMessage);
std::fflush(stdout);
}
}

View File

@@ -14,4 +14,4 @@ class CProgressBar {
private:
bool m_bFirstPrint = true;
};
};

View File

@@ -21,6 +21,7 @@ add_project_arguments(
'-Wno-missing-field-initializers',
'-Wno-narrowing',
'-Wno-pointer-arith', datarootdir,
'-DHYPRLAND_VERSION="' + meson.project_version() + '"',
],
language: 'cpp',
)
@@ -30,8 +31,16 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
aquamarine = dependency('aquamarine')
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,9 +59,17 @@ endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
re2 = dependency('re2', required: true)
# Handle options
if get_option('systemd').enabled()
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
if get_option('legacy_renderer').enabled()
@@ -79,6 +96,8 @@ if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized'
warning('Profiling builds should set -- buildtype = debugoptimized')
endif
subdir('protocols')
subdir('src')
subdir('hyprctl')

View File

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

View File

@@ -12,7 +12,9 @@
cairo,
git,
hyprcursor,
hyprgraphics,
hyprland-protocols,
hyprland-qtutils,
hyprlang,
hyprutils,
hyprwayland-scanner,
@@ -25,6 +27,7 @@
mesa,
pango,
pciutils,
re2,
systemd,
tomlplusplus,
udis86-hyprland,
@@ -56,6 +59,7 @@
adapters = flatten [
stdenvAdapters.useMoldLinker
(lib.optional debug stdenvAdapters.keepDebugInfo)
];
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
@@ -63,12 +67,12 @@ 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;
src = cleanSourceWith {
filter = name: type: let
filter = name: _type: let
baseName = baseNameOf (toString name);
in
! (hasSuffix ".nix" baseName);
@@ -87,6 +91,7 @@ in
DATE = date;
DIRTY = optionalString (commit == "") "dirty";
HASH = commit;
TAG = "v${builtins.readFile "${finalAttrs.src}/VERSION"}";
depsBuildBuild = [
pkg-config
@@ -112,6 +117,7 @@ in
cairo
git
hyprcursor
hyprgraphics
hyprland-protocols
hyprlang
hyprutils
@@ -123,6 +129,7 @@ in
mesa
pango
pciutils
re2
tomlplusplus
udis86-hyprland
wayland
@@ -147,14 +154,11 @@ in
then "debugoptimized"
else "release";
# we want as much debug info as possible
dontStrip = debug;
mesonFlags = flatten [
(mapAttrsToList mesonEnable {
"xwayland" = enableXWayland;
"legacy_renderer" = legacyRenderer;
"systemd" = withSystemd;
"uwsm" = false;
})
(mapAttrsToList mesonBool {
"b_pch" = false;
@@ -167,6 +171,7 @@ in
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${makeBinPath [
binutils
hyprland-qtutils
pciutils
pkgconf
]}
@@ -182,4 +187,4 @@ in
platforms = lib.platforms.linux;
mainProgram = "Hyprland";
};
}
})

64
nix/formatter.nix Normal file
View File

@@ -0,0 +1,64 @@
{
writeShellApplication,
deadnix,
statix,
alejandra,
llvmPackages_19,
fd,
}:
writeShellApplication {
name = "hyprland-treewide-formatter";
runtimeInputs = [
deadnix
statix
alejandra
llvmPackages_19.clang-tools
fd
];
text = ''
# shellcheck disable=SC2148
# common excludes
excludes="subprojects"
nix_format() {
if [ "$*" = 0 ]; then
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
elif [ -d "$1" ]; then
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
else
statix fix -- "$1"
deadnix -e "$1"
alejandra "$1"
fi
}
cpp_format() {
if [ "$*" = 0 ] || [ "$1" = "." ]; then
fd '.*\.cpp' . -E "$excludes" | xargs clang-format --verbose -i
elif [ -d "$1" ]; then
fd '.*\.cpp' "$1" -E "$excludes" | xargs clang-format --verbose -i
else
clang-format --verbose -i "$1"
fi
}
for i in "$@"; do
case ''${i##*.} in
"nix")
nix_format "$i"
;;
"cpp")
cpp_format "$i"
;;
*)
nix_format "$i"
cpp_format "$i"
;;
esac
done
'';
}

View File

@@ -22,14 +22,16 @@ 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
self.overlays.udis86
# Hyprland packages themselves
(final: prev: let
(final: _prev: let
date = mkDate (self.lastModifiedDate or "19700101");
in {
hyprland = final.callPackage ./default.nix {
@@ -40,7 +42,13 @@ in {
inherit date;
};
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
hyprland-debug = final.hyprland.override {debug = true;};
# Build major libs with debug to get as much info as possible in a stacktrace
hyprland-debug = final.hyprland.override {
aquamarine = final.aquamarine.override {debug = true;};
hyprutils = final.hyprutils.override {debug = true;};
debug = true;
};
hyprland-legacy-renderer = final.hyprland.override {legacyRenderer = true;};
# deprecated packages
@@ -63,14 +71,14 @@ in {
# Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility.
hyprland-extras = lib.composeManyExtensions [
inputs.xdph.overlays.xdg-desktop-portal-hyprland
inputs.xdph.overlays.default
];
# udis86 from nixpkgs is too old, and also does not provide a .pc file
# this version is the one used in the git submodule, and allows us to
# fetch the source without '?submodules=1'
udis86 = final: prev: {
udis86-hyprland = prev.udis86.overrideAttrs (self: super: {
udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: {
src = final.fetchFromGitHub {
owner = "canihavesomecoffee";
repo = "udis86";

View File

@@ -7,7 +7,7 @@ wayland_protos = dependency(
hyprland_protos = dependency(
'hyprland-protocols',
version: '>=0.2',
version: '>=0.4',
fallback: 'hyprland-protocols',
)
@@ -36,6 +36,7 @@ protocols = [
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',
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',
@@ -64,6 +65,7 @@ protocols = [
wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
]
wl_protocols = []

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
#pragma once
#include <memory>
#include <deque>
#include <list>
#include <sys/resource.h>
@@ -35,7 +34,7 @@
class CWLSurfaceResource;
enum eManagersInitStage {
enum eManagersInitStage : uint8_t {
STAGE_PRIORITY = 0,
STAGE_BASICINIT,
STAGE_LATE
@@ -59,8 +58,8 @@ class CCompositor {
std::string m_szInstancePath = "";
std::string m_szCurrentSplash = "error";
std::vector<SP<CMonitor>> m_vMonitors;
std::vector<SP<CMonitor>> m_vRealMonitors; // for all monitors, even those turned off
std::vector<PHLMONITOR> m_vMonitors;
std::vector<PHLMONITOR> m_vRealMonitors; // for all monitors, even those turned off
std::vector<PHLWINDOW> m_vWindows;
std::vector<PHLLS> m_vLayers;
std::vector<PHLWORKSPACE> m_vWorkspaces;
@@ -80,7 +79,7 @@ class CCompositor {
WP<CWLSurfaceResource> m_pLastFocus;
PHLWINDOWREF m_pLastWindow;
WP<CMonitor> m_pLastMonitor;
PHLMONITORREF m_pLastMonitor;
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
@@ -89,7 +88,7 @@ class CCompositor {
bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
PHLMONITORREF m_pUnsafeOutput; // fallback output for the unsafe state
bool m_bIsShuttingDown = false;
bool m_bFinalRequests = false;
bool m_bDesktopEnvSet = false;
@@ -97,39 +96,29 @@ class CCompositor {
// ------------------------------------------------- //
CMonitor* getMonitorFromID(const MONITORID&);
CMonitor* getMonitorFromName(const std::string&);
CMonitor* getMonitorFromDesc(const std::string&);
CMonitor* getMonitorFromCursor();
CMonitor* getMonitorFromVector(const Vector2D&);
PHLMONITOR getMonitorFromID(const MONITORID&);
PHLMONITOR getMonitorFromName(const std::string&);
PHLMONITOR getMonitorFromDesc(const std::string&);
PHLMONITOR getMonitorFromCursor();
PHLMONITOR getMonitorFromVector(const Vector2D&);
void removeWindowFromVectorSafe(PHLWINDOW);
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr);
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
bool monitorExists(CMonitor*);
bool monitorExists(PHLMONITOR);
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*);
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*);
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, PHLMONITOR monitor, Vector2D*, PHLLS*);
SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
CMonitor* getMonitorFromOutput(SP<Aquamarine::IOutput>);
CMonitor* getRealMonitorFromOutput(SP<Aquamarine::IOutput>);
PHLMONITOR getMonitorFromOutput(SP<Aquamarine::IOutput>);
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);
@@ -138,25 +127,24 @@ class CCompositor {
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
WORKSPACEID getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CMonitor* getMonitorInDirection(const char&);
CMonitor* getMonitorInDirection(CMonitor*, const char&);
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, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);
PHLMONITOR getMonitorFromString(const std::string&);
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 setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state);
void changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
void scheduleFrameForMonitor(PHLMONITOR, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
void addToFadingOutSafe(PHLLS);
void removeFromFadingOutSafe(PHLLS);
void addToFadingOutSafe(PHLWINDOW);
@@ -165,11 +153,9 @@ 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(CMonitor*);
void setActiveMonitor(PHLMONITOR);
bool isWorkspaceSpecial(const WORKSPACEID&);
WORKSPACEID getNewSpecialID();
void performUserChecks();

View File

@@ -5,9 +5,7 @@
#include <any>
#include <hyprutils/math/Box.hpp>
using namespace Hyprutils::Math;
enum eIcons {
enum eIcons : uint8_t {
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
@@ -17,7 +15,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 +27,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 +39,7 @@ struct SCallbackInfo {
bool cancelled = false; /* on cancellable events, will cancel the event. */
};
enum eHyprCtlOutputFormat {
enum eHyprCtlOutputFormat : uint8_t {
FORMAT_NORMAL = 0,
FORMAT_JSON
};

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
@@ -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

@@ -104,6 +104,30 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{0, 0, 4},
},
SConfigOptionDescription{
.value = "general:snap:enabled",
.description = "enable snapping for floating windows",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "general:snap:window_gap",
.description = "minimum gap in pixels between windows before snapping",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{10, 0, 100},
},
SConfigOptionDescription{
.value = "general:snap:monitor_gap",
.description = "minimum gap in pixels between window and monitor edges before snapping",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{10, 0, 100},
},
SConfigOptionDescription{
.value = "general:snap:border_overlap",
.description = "if true, windows snap such that only one border's worth of space is between them",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* decoration:
@@ -134,49 +158,55 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SFloatData{1, 0, 1},
},
SConfigOptionDescription{
.value = "decoration:drop_shadow",
.value = "decoration:shadow:enabled",
.description = "enable drop shadows on windows",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "decoration:shadow_range",
.value = "decoration:shadow:range",
.description = "Shadow range (size) in layout px",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{4, 0, 100},
},
SConfigOptionDescription{
.value = "decoration:shadow_render_power",
.value = "decoration:shadow:render_power",
.description = "in what power to render the falloff (more power, the faster the falloff) [1 - 4]",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{3, 1, 4},
},
SConfigOptionDescription{
.value = "decoration:shadow_ignore_window",
.value = "decoration:shadow:sharp",
.description = "whether the shadow should be sharp or not. Akin to an infinitely high render power.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "decoration:shadow:ignore_window",
.description = "if true, the shadow will not be rendered behind the window itself, only around it.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "decoration:col.shadow",
.value = "decoration:shadow:color",
.description = "shadow's color. Alpha dictates shadow's opacity.",
.type = CONFIG_OPTION_COLOR,
.data = SConfigOptionDescription::SColorData{0xee1a1a1a},
},
SConfigOptionDescription{
.value = "decoration:col.shadow_inactive",
.value = "decoration:shadow:color_inactive",
.description = "inactive shadow color. (if not set, will fall back to col.shadow)",
.type = CONFIG_OPTION_COLOR,
.data = SConfigOptionDescription::SColorData{}, //##TODO UNSET?
.data = SConfigOptionDescription::SColorData{}, //TODO: UNSET?
},
SConfigOptionDescription{
.value = "decoration:shadow_offset",
.value = "decoration:shadow:offset",
.description = "shadow's rendering offset.",
.type = CONFIG_OPTION_VECTOR,
.data = SConfigOptionDescription::SVectorData{{}, {-250, -250}, {250, 250}},
},
SConfigOptionDescription{
.value = "decoration:shadow_scale",
.value = "decoration:shadow:scale",
.description = "shadow's scale. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{1, 0, 1},
@@ -238,7 +268,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",
@@ -301,6 +331,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:
@@ -590,16 +632,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.",
@@ -748,6 +796,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:merge_groups_on_groupbar",
.description = "whether one group will be merged with another when dragged into its groupbar",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "general:col.border_active",
.description = "border color for inactive windows",
@@ -778,6 +832,24 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:drag_into_group",
.description = "whether dragging a window into a unlocked group will merge them. Options: 0 (disabled), 1 (enabled), 2 (only when dragging into the groupbar)",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "disabled,enabled,only when dragging into the groupbar"},
},
SConfigOptionDescription{
.value = "group:merge_floated_into_tiled_on_groupbar",
.description = "whether dragging a floating window into a tiled window groupbar will merge them",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "group:group_on_movetoworkspace",
.description = "whether using movetoworkspace[silent] will merge the window into the workspace's solitary unlocked group",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* group:groupbar:
@@ -1061,6 +1133,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:
@@ -1116,6 +1200,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.",
@@ -1128,6 +1218,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:
@@ -1208,8 +1304,8 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
SConfigOptionDescription{
.value = "cursor:no_hardware_cursors",
.description = "disables hardware cursors",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Auto"},
},
SConfigOptionDescription{
.value = "cursor:no_break_fs_vrr",
@@ -1249,9 +1345,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",
@@ -1290,8 +1386,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},
},
@@ -1434,12 +1530,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{1, 0.1, 3},
},
SConfigOptionDescription{
.value = "dwindle:no_gaps_when_only",
.description = "whether to apply gaps when there is only one window on a workspace, aka. smart gaps. (default: disabled - 0) no border - 1, with border - 2 [0/1/2]",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "disabled,no border,with border"},
},
SConfigOptionDescription{
.value = "dwindle:use_active_for_splits",
.description = "whether to prefer the active window or the mouse position for splits",
@@ -1500,12 +1590,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_STRING_SHORT,
.data = SConfigOptionDescription::SStringData{"none"},
},
SConfigOptionDescription{
.value = "master:no_gaps_when_only",
.description = "whether to apply gaps when there is only one window on a workspace, aka. smart gaps. (default: disabled - 0) no border - 1, with border - 2 [0/1/2]",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "disabled,no border,with border"},
},
SConfigOptionDescription{
.value = "master:orientation",
.description = "default placement of the master area, can be left, right, top, bottom or center",
@@ -1524,6 +1608,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "master:center_ignores_reserved",
.description = "centers the master window on monitor ignoring reserved areas",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "master:smart_resizing",
.description =

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,7 @@
#include "../defines.hpp"
#include <variant>
#include <vector>
#include <deque>
#include <algorithm>
#include <regex>
#include <optional>
#include <functional>
#include <xf86drmMode.h>
@@ -69,13 +67,13 @@ struct SAnimationPropertyConfig {
};
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 +82,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 +94,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 +117,7 @@ struct SConfigOptionDescription {
};
struct SColorData {
CColor color;
CHyprColor color;
};
struct SChoiceData {
@@ -167,18 +165,18 @@ class CConfigManager {
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 getConfigString();
SMonitorRule getMonitorRuleFor(const SP<CMonitor>);
SMonitorRule getMonitorRuleFor(const PHLMONITOR);
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
std::string getDefaultWorkspaceFor(const std::string&);
CMonitor* getBoundMonitorForWS(const std::string&);
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);
const std::vector<SConfigOptionDescription>& getAllDescriptions();
@@ -198,7 +196,9 @@ class CConfigManager {
void appendMonitorRule(const SMonitorRule&);
bool replaceMonitorRule(const SMonitorRule&);
void ensureMonitorStatus();
void ensureVRR(CMonitor* pMonitor = nullptr);
void ensureVRR(PHLMONITOR pMonitor = nullptr);
bool shouldUseSoftwareCursors();
std::string parseKeyword(const std::string&, const std::string&);
@@ -212,52 +212,58 @@ class CConfigManager {
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> 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::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; }},
};
std::unordered_map<std::string, std::function<CWindowOverridableVar<float>*(PHLWINDOW)>> mfWindowProperties = {
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
@@ -267,7 +273,7 @@ class CConfigManager {
private:
std::unique_ptr<Hyprlang::CConfig> m_pConfig;
std::deque<std::string> configPaths; // stores all the config paths
std::vector<std::string> configPaths; // stores all the config paths
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
std::unordered_map<std::string, SAnimationPropertyConfig> animationConfig; // stores all the animations with their set values
@@ -282,16 +288,16 @@ class CConfigManager {
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;
std::vector<std::string> firstExecRequests;
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 = "";

View File

@@ -13,6 +13,7 @@ class CConfigValue {
CConfigValue(const std::string& val) {
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
// NOLINTNEXTLINE
p_ = PVHYPRLANG->getDataStaticPtr();
#ifdef HYPRLAND_DEBUG

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.
# #######################################################################################
@@ -13,7 +13,7 @@ autogenerated = 1 # remove this line to remove the warning
# This is an example Hyprland config file.
# Refer to the wiki for more information.
# https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
# https://wiki.hyprland.org/Configuring/
# Please note not all available settings / options are set here.
# For a full list, see the wiki
@@ -99,10 +99,12 @@ decoration {
active_opacity = 1.0
inactive_opacity = 1.0
drop_shadow = true
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
shadow {
enabled = true
range = 4
render_power = 3
color = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
@@ -116,20 +118,44 @@ decoration {
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = true
enabled = yes, please :)
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = myBezier, 0.05, 0.9, 0.1, 1.05
bezier = easeOutQuint,0.23,1,0.32,1
bezier = easeInOutCubic,0.65,0.05,0.36,1
bezier = linear,0,0,1,1
bezier = almostLinear,0.5,0.5,0.75,1.0
bezier = quick,0.15,0,0.1,1
animation = windows, 1, 7, myBezier
animation = windowsOut, 1, 7, default, popin 80%
animation = border, 1, 10, default
animation = borderangle, 1, 8, default
animation = fade, 1, 7, default
animation = workspaces, 1, 6, default
animation = global, 1, 10, default
animation = border, 1, 5.39, easeOutQuint
animation = windows, 1, 4.79, easeOutQuint
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
animation = windowsOut, 1, 1.49, linear, popin 87%
animation = fadeIn, 1, 1.73, almostLinear
animation = fadeOut, 1, 1.46, almostLinear
animation = fade, 1, 3.03, quick
animation = layers, 1, 3.81, easeOutQuint
animation = layersIn, 1, 4, easeOutQuint, fade
animation = layersOut, 1, 1.5, linear, fade
animation = fadeLayersIn, 1, 1.79, almostLinear
animation = fadeLayersOut, 1, 1.39, almostLinear
animation = workspaces, 1, 1.94, almostLinear, fade
animation = workspacesIn, 1, 1.21, almostLinear, fade
animation = workspacesOut, 1, 1.94, almostLinear, fade
}
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@@ -268,5 +294,9 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
)#";

View File

@@ -2,8 +2,8 @@
#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>
@@ -31,10 +31,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 +42,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 +61,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 +86,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 +100,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: ";
@@ -122,9 +122,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,7 +159,7 @@ 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");
#endif
@@ -241,5 +241,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>
@@ -36,6 +39,7 @@ using namespace Hyprutils::String;
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../version.h"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@@ -54,7 +58,7 @@ static std::string formatToString(uint32_t drmFormat) {
return "Invalid";
}
static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) {
static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFormat format) {
std::string result;
for (auto const& m : pMonitor->output->modes) {
@@ -105,8 +109,10 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"vrr": {},
"solitary": "{:x}",
"activelyTearing": {},
"directScanoutTo": "{:x}",
"disabled": {},
"currentFormat": "{}",
"mirrorOf": "{}",
"availableModes": [{}]
}},)#",
@@ -116,19 +122,21 @@ 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),
availableModesForOutput(m.get(), 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\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), availableModesForOutput(m.get(), format));
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;
@@ -237,26 +245,27 @@ 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,
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"),
(int64_t)w->m_iMonitorID, escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass),
(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",
"{}\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->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(),
(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));
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w), (int)g_pInputManager->isWindowInhibiting(w, false));
}
}
@@ -288,7 +297,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
const auto PMONITOR = w->m_pMonitor.lock();
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#({{
"id": {},
@@ -301,12 +310,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 : "");
}
}
@@ -529,7 +538,7 @@ std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request
CVarList errLines(currErrors, 0, '\n');
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto line : errLines) {
for (const auto& line : errLines) {
result += std::format(
R"#(
"{}",)#",
@@ -539,7 +548,7 @@ 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);
}
}
@@ -549,6 +558,15 @@ std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request
std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
auto getModState = [](SP<IKeyboard> keyboard, const char* xkbModName) -> bool {
auto IDX = xkb_keymap_mod_get_index(keyboard->xkbKeymap, xkbModName);
if (IDX == XKB_MOD_INVALID)
return false;
return (keyboard->modifiersState.locked & (1 << IDX)) > 0;
};
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
result += "\"mice\": [\n";
@@ -580,11 +598,13 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"variant": "{}",
"options": "{}",
"active_keymap": "{}",
"capsLock": {},
"numLock": {},
"main": {}
}},)#",
(uintptr_t)k.get(), escapeJSONStrings(k->hlName), escapeJSONStrings(k->currentRules.rules), escapeJSONStrings(k->currentRules.model),
escapeJSONStrings(k->currentRules.layout), escapeJSONStrings(k->currentRules.variant), escapeJSONStrings(k->currentRules.options), escapeJSONStrings(KM),
(k->active ? "true" : "false"));
(getModState(k, XKB_MOD_NAME_CAPS) ? "true" : "false"), (getModState(k, XKB_MOD_NAME_NUM) ? "true" : "false"), (k->active ? "true" : "false"));
}
trimTrailingComma(result);
@@ -668,9 +688,11 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
for (auto const& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout();
result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tmain: {}\n",
(uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant,
k->currentRules.options, KM, (k->active ? "yes" : "no"));
result +=
std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tcapsLock: "
"{}\n\t\t\tnumLock: {}\n\t\t\tmain: {}\n",
(uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant, k->currentRules.options,
KM, (getModState(k, XKB_MOD_NAME_CAPS) ? "yes" : "no"), (getModState(k, XKB_MOD_NAME_NUM) ? "yes" : "no"), (k->active ? "yes" : "no"));
}
result += "\n\nTablets:\n";
@@ -796,28 +818,28 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
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"#(
{{
@@ -825,6 +847,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"mouse": {},
"release": {},
"repeat": {},
"longPress": {},
"non_consuming": {},
"has_description": {},
"modmask": {},
@@ -836,9 +859,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 += "]";
@@ -853,40 +876,52 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + ", commits: " + GIT_COMMITS + std::string{"\nbuilt against aquamarine "} + AQUAMARINE_VERSION + "\n" +
"\n\nflags: (if any)\n";
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
"Date: {}\n"
"Tag: {}, commits: {}\n"
"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))
result += "no flags were set\n";
#else
result += "flags set:\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
#endif
#ifndef ISDEBUG
#ifdef ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
result += "no xwayland\n";
#endif
#endif
return result;
} else {
std::string result = std::format(
R"#({{
"branch": "{}",
"commit": "{}",
"version": "{}",
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"buildHyprlang": "{}",
"buildHyprutils": "{}",
"buildHyprcursor": "{}",
"buildHyprgraphics": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS,
AQUAMARINE_VERSION);
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
#endif
#ifndef ISDEBUG
#ifdef ISDEBUG
result += "\"debug\",";
#endif
#ifdef NO_XWAYLAND
@@ -920,18 +955,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");
#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) {
@@ -1021,9 +1089,10 @@ std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
}
// decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
COMMAND.starts_with("windowrule")) {
for (auto const& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get());
g_pHyprRenderer->damageMonitor(m);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
}
}
@@ -1226,7 +1295,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] + ' ';
@@ -1242,101 +1311,8 @@ std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
}
std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 4)
return "not enough args";
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
if (!PWINDOW)
return "window not found";
const auto PROP = vars[2];
const auto VAL = vars[3];
bool noFocus = PWINDOW->m_sWindowData.noFocus.valueOrDefault();
try {
if (PROP == "animationstyle") {
PWINDOW->m_sWindowData.animationStyle = CWindowOverridableVar(VAL, PRIORITY_SET_PROP);
} else if (PROP == "maxsize") {
PWINDOW->m_sWindowData.maxSize = CWindowOverridableVar(configStringToVector2D(VAL + " " + vars[4]), PRIORITY_SET_PROP);
PWINDOW->m_vRealSize = Vector2D(std::min((double)PWINDOW->m_sWindowData.maxSize.value().x, PWINDOW->m_vRealSize.goal().x),
std::min((double)PWINDOW->m_sWindowData.maxSize.value().y, PWINDOW->m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal());
PWINDOW->setHidden(false);
} else if (PROP == "minsize") {
PWINDOW->m_sWindowData.minSize = CWindowOverridableVar(configStringToVector2D(VAL + " " + vars[4]), PRIORITY_SET_PROP);
PWINDOW->m_vRealSize = Vector2D(std::max((double)PWINDOW->m_sWindowData.minSize.value().x, PWINDOW->m_vRealSize.goal().x),
std::max((double)PWINDOW->m_sWindowData.minSize.value().y, PWINDOW->m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal());
PWINDOW->setHidden(false);
} else if (PROP == "alpha") {
PWINDOW->m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alpha.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP);
} else if (PROP == "alphainactive") {
PWINDOW->m_sWindowData.alphaInactive =
CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alphaInactive.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP);
} else if (PROP == "alphafullscreen") {
PWINDOW->m_sWindowData.alphaFullscreen =
CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alphaFullscreen.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP);
} else if (PROP == "alphaoverride") {
PWINDOW->m_sWindowData.alpha =
CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alpha.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL)}, PRIORITY_SET_PROP);
} else if (PROP == "alphainactiveoverride") {
PWINDOW->m_sWindowData.alphaInactive =
CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alphaInactive.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL)}, PRIORITY_SET_PROP);
} else if (PROP == "alphafullscreenoverride") {
PWINDOW->m_sWindowData.alphaFullscreen =
CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alphaFullscreen.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL)}, PRIORITY_SET_PROP);
} else if (PROP == "activebordercolor" || PROP == "inactivebordercolor") {
CGradientValueData colorData = {};
if (vars.size() > 4) {
for (int i = 3; i < static_cast<int>(vars.size()); ++i) {
const auto TOKEN = vars[i];
if (TOKEN.ends_with("deg"))
colorData.m_fAngle = std::stoi(TOKEN.substr(0, TOKEN.size() - 3)) * (PI / 180.0);
else
colorData.m_vColors.push_back(configStringToInt(TOKEN));
}
} else if (VAL != "-1")
colorData.m_vColors.push_back(configStringToInt(VAL));
if (PROP == "activebordercolor")
PWINDOW->m_sWindowData.activeBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
else
PWINDOW->m_sWindowData.inactiveBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
} else if (auto search = g_pConfigManager->mbWindowProperties.find(PROP); search != g_pConfigManager->mbWindowProperties.end()) {
auto pWindowDataElement = search->second(PWINDOW);
if (VAL == "toggle")
*pWindowDataElement = CWindowOverridableVar(!pWindowDataElement->valueOrDefault(), PRIORITY_SET_PROP);
else if (VAL == "unset")
pWindowDataElement->unset(PRIORITY_SET_PROP);
else
*pWindowDataElement = CWindowOverridableVar((bool)configStringToInt(VAL), PRIORITY_SET_PROP);
} else if (auto search = g_pConfigManager->miWindowProperties.find(PROP); search != g_pConfigManager->miWindowProperties.end()) {
if (VAL == "unset")
search->second(PWINDOW)->unset(PRIORITY_SET_PROP);
else
*(search->second(PWINDOW)) = CWindowOverridableVar((int)configStringToInt(VAL), PRIORITY_SET_PROP);
} else {
return "prop not found";
}
} catch (std::exception& e) { return "error in parsing prop value: " + std::string(e.what()); }
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
if (!(PWINDOW->m_sWindowData.noFocus.valueOrDefault() == noFocus)) {
g_pCompositor->focusWindow(nullptr);
g_pCompositor->focusWindow(PWINDOW);
g_pCompositor->focusWindow(PLASTWINDOW);
}
for (auto const& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
return "ok";
auto result = g_pKeybindManager->m_mDispatchers["setprop"](request.substr(request.find_first_of(' ') + 1, -1));
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
}
std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
@@ -1380,17 +1356,16 @@ 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)";
@@ -1505,17 +1480,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";
}
@@ -1539,9 +1537,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;
@@ -1549,10 +1546,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);
@@ -1771,8 +1771,16 @@ std::string CHyprCtl::getReply(std::string request) {
rd.blurFBDirty = true;
}
for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || !w->m_pWorkspace || !w->m_pWorkspace->isVisible())
continue;
w->updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(w);
}
for (auto const& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get());
g_pHyprRenderer->damageMonitor(m);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
}
}
@@ -1802,13 +1810,13 @@ void runWritingDebugLogThread(const int conn) {
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;
@@ -1816,7 +1824,7 @@ void runWritingDebugLogThread(const int conn) {
std::this_thread::sleep_for(100ms);
}
close(conn);
Debug::RollingLogFollow::Get().StopFor(conn);
Debug::SRollingLogFollow::get().stopFor(conn);
}).detach();
}
@@ -1875,9 +1883,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);

View File

@@ -7,6 +7,7 @@
// exposed for main.cpp
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request);
std::string versionRequest(eHyprCtlOutputFormat format, std::string request);
class CHyprCtl {
public:

View File

@@ -7,8 +7,13 @@ CHyprDebugOverlay::CHyprDebugOverlay() {
m_pTexture = makeShared<CTexture>();
}
void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float durationUs) {
m_dLastRenderTimes.push_back(durationUs / 1000.f);
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
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();
@@ -17,8 +22,13 @@ void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float durationUs)
m_pMonitor = pMonitor;
}
void CHyprMonitorDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float durationUs) {
m_dLastRenderTimesNoOverlay.push_back(durationUs / 1000.f);
void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
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();
@@ -27,8 +37,13 @@ void CHyprMonitorDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float dur
m_pMonitor = pMonitor;
}
void CHyprMonitorDebugOverlay::frameData(CMonitor* pMonitor) {
m_dLastFrametimes.push_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f);
void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
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();
@@ -39,7 +54,7 @@ void CHyprMonitorDebugOverlay::frameData(CMonitor* pMonitor) {
m_pMonitor = pMonitor;
// anim data too
const auto PMONITORFORTICKS = g_pHyprRenderer->m_pMostHzMonitor ? g_pHyprRenderer->m_pMostHzMonitor : g_pCompositor->m_pLastMonitor.get();
const auto PMONITORFORTICKS = g_pHyprRenderer->m_pMostHzMonitor ? g_pHyprRenderer->m_pMostHzMonitor.lock() : g_pCompositor->m_pLastMonitor.lock();
if (PMONITORFORTICKS) {
if (m_dLastAnimationTicks.size() > (long unsigned int)PMONITORFORTICKS->refreshRate)
m_dLastAnimationTicks.pop_front();
@@ -188,21 +203,36 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
return posY - offset;
}
void CHyprDebugOverlay::renderData(CMonitor* pMonitor, float durationUs) {
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(CMonitor* pMonitor, float 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(CMonitor* pMonitor) {
void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
if (!*PDEBUGOVERLAY)
return;
m_mMonitorOverlays[pMonitor].frameData(pMonitor);
}
void CHyprDebugOverlay::draw() {
const auto PMONITOR = g_pCompositor->m_vMonitors.front().get();
const auto PMONITOR = g_pCompositor->m_vMonitors.front();
if (!m_pCairoSurface || !m_pCairo) {
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y);
@@ -218,7 +248,7 @@ void CHyprDebugOverlay::draw() {
// draw the things
int offsetY = 0;
for (auto const& m : g_pCompositor->m_vMonitors) {
offsetY += m_mMonitorOverlays[m.get()].draw(offsetY);
offsetY += m_mMonitorOverlays[m].draw(offsetY);
offsetY += 5; // for padding between mons
}

View File

@@ -3,9 +3,9 @@
#include "../defines.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Texture.hpp"
#include <deque>
#include <cairo/cairo.h>
#include <unordered_map>
#include <map>
#include <deque>
class CHyprRenderer;
@@ -13,9 +13,9 @@ class CHyprMonitorDebugOverlay {
public:
int draw(int offset);
void renderData(CMonitor* pMonitor, float durationUs);
void renderDataNoOverlay(CMonitor* pMonitor, float durationUs);
void frameData(CMonitor* pMonitor);
void renderData(PHLMONITOR pMonitor, float durationUs);
void renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs);
void frameData(PHLMONITOR pMonitor);
private:
std::deque<float> m_dLastFrametimes;
@@ -23,7 +23,7 @@ class CHyprMonitorDebugOverlay {
std::deque<float> m_dLastRenderTimesNoOverlay;
std::deque<float> m_dLastAnimationTicks;
std::chrono::high_resolution_clock::time_point m_tpLastFrame;
CMonitor* m_pMonitor = nullptr;
PHLMONITORREF m_pMonitor;
CBox m_wbLastDrawnBox;
friend class CHyprRenderer;
@@ -33,17 +33,17 @@ class CHyprDebugOverlay {
public:
CHyprDebugOverlay();
void draw();
void renderData(CMonitor*, float durationUs);
void renderDataNoOverlay(CMonitor*, float durationUs);
void frameData(CMonitor*);
void renderData(PHLMONITOR, float durationUs);
void renderDataNoOverlay(PHLMONITOR, float durationUs);
void frameData(PHLMONITOR);
private:
std::unordered_map<CMonitor*, CHyprMonitorDebugOverlay> m_mMonitorOverlays;
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_mMonitorOverlays;
cairo_surface_t* m_pCairoSurface = nullptr;
cairo_t* m_pCairo = nullptr;
cairo_surface_t* m_pCairoSurface = nullptr;
cairo_t* m_pCairo = nullptr;
SP<CTexture> m_pTexture;
SP<CTexture> m_pTexture;
friend class CHyprMonitorDebugOverlay;
friend class CHyprRenderer;

View File

@@ -18,7 +18,7 @@ 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);
@@ -34,34 +34,34 @@ 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(std::make_unique<SNotification>()).get();
PNOTIF->text = text;
PNOTIF->color = color == CColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->text = icon != eIcons::ICON_NONE ? " " + text /* tiny bit of padding otherwise icon touches text */ : text;
PNOTIF->color = color == CHyprColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->started.reset();
PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon;
PNOTIF->fontSize = fontSize;
for (auto const& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get());
g_pCompositor->scheduleFrameForMonitor(m);
}
}
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());
}
}
}
CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
static constexpr auto ANIM_DURATION_MS = 600.0;
static constexpr auto ANIM_LAG_MS = 100.0;
static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0;
@@ -87,7 +87,7 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* 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,12 +182,12 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* 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};
}
void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
const auto MONSIZE = pMonitor->vecTransformedSize;
@@ -205,7 +205,7 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
}
// Draw the notifications
if (m_dNotifications.size() == 0)
if (m_vNotifications.size() == 0)
return;
// Render to the monitor
@@ -246,5 +246,5 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
}
bool CHyprNotificationOverlay::hasAny() {
return !m_dNotifications.empty();
return !m_vNotifications.empty();
}

View File

@@ -6,11 +6,11 @@
#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 +19,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;
@@ -41,24 +41,24 @@ class CHyprNotificationOverlay {
CHyprNotificationOverlay();
~CHyprNotificationOverlay();
void draw(CMonitor* pMonitor);
void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE, const float fontSize = 13.f);
void draw(PHLMONITOR pMonitor);
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(CMonitor* pMonitor);
CBox m_bLastDamage;
CBox drawNotifications(PHLMONITOR pMonitor);
CBox m_bLastDamage;
std::deque<std::unique_ptr<SNotification>> m_dNotifications;
std::vector<std::unique_ptr<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;
CMonitor* m_pLastMonitor = nullptr;
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;

View File

@@ -4,7 +4,7 @@
#include "RollingLogFollow.hpp"
#include <fstream>
#include <iostream>
#include <print>
#include <fcntl.h>
void Debug::init(const std::string& IS) {
@@ -18,7 +18,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 +26,7 @@ void Debug::log(LogLevel level, std::string str) {
return;
std::string coloredStr = str;
//NOLINTBEGIN
switch (level) {
case LOG:
str = "[LOG] " + str;
@@ -53,13 +54,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
@@ -69,5 +71,5 @@ void Debug::log(LogLevel level, std::string str) {
// log it to the stdout too.
if (!disableStdout)
std::cout << ((coloredLogs && !**coloredLogs) ? str : coloredStr) << "\n";
std::println("{}", ((coloredLogs && !**coloredLogs) ? str : coloredStr));
}

View File

@@ -11,7 +11,7 @@
#define LOGMESSAGESIZE 1024
#define ROLLING_LOG_SIZE 4096
enum LogLevel {
enum eLogLevel : int8_t {
NONE = -1,
LOG = 0,
WARN,
@@ -21,6 +21,7 @@ enum LogLevel {
TRACE
};
// NOLINTNEXTLINE(readability-identifier-naming)
namespace Debug {
inline std::string logFile;
inline std::ofstream logOfs;
@@ -38,10 +39,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

@@ -3,6 +3,7 @@
class CWorkspace;
class CWindow;
class CLayerSurface;
class CMonitor;
/* Shared pointer to a workspace */
typedef SP<CWorkspace> PHLWORKSPACE;
@@ -18,3 +19,8 @@ typedef WP<CWindow> PHLWINDOWREF;
typedef SP<CLayerSurface> PHLLS;
/* Weak pointer to a layer surface */
typedef WP<CLayerSurface> PHLLSREF;
/* Shared pointer to a monitor */
typedef SP<CMonitor> PHLMONITOR;
/* Weak pointer to a monitor */
typedef WP<CMonitor> PHLMONITORREF;

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

@@ -0,0 +1,37 @@
#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;
}
}

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

@@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <cstdint>
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;
};

View File

@@ -6,9 +6,9 @@
#include "../managers/SeatManager.hpp"
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
CMonitor* 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);
@@ -18,7 +18,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
}
if (pMonitor->pMirrorOf)
pMonitor = g_pCompositor->m_vMonitors.front().get();
pMonitor = g_pCompositor->m_vMonitors.front();
pLS->self = pLS;
@@ -26,7 +26,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
pLS->layer = resource->current.layer;
pLS->popupHead = std::make_unique<CPopup>(pLS);
pLS->monitorID = pMonitor->ID;
pLS->monitor = pMonitor;
pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS);
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
@@ -50,7 +50,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
void CLayerSurface::registerCallbacks() {
alpha.setUpdateCallback([this](void*) {
if (dimAround)
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(monitorID));
g_pHyprRenderer->damageMonitor(monitor.lock());
});
}
@@ -82,9 +82,9 @@ CLayerSurface::~CLayerSurface() {
void CLayerSurface::onDestroy() {
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface.get());
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
const auto PMONITOR = monitor.lock();
if (!g_pCompositor->getMonitorFromID(monitorID))
if (!PMONITOR)
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
if (!fadingOut) {
@@ -137,7 +137,7 @@ void CLayerSurface::onMap() {
g_pCompositor->removeFromFadingOutSafe(self.lock());
// fix if it changed its mon
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
const auto PMONITOR = monitor.lock();
if (!PMONITOR)
return;
@@ -175,8 +175,7 @@ void CLayerSurface::onMap() {
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = PMONITOR->activeWorkspace;
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FSMODE_FULLSCREEN;
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));
readyToDelete = false;
@@ -197,7 +196,7 @@ void CLayerSurface::onUnmap() {
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
if (!g_pCompositor->getMonitorFromID(monitorID) || g_pCompositor->m_bUnsafeState) {
if (!monitor || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
g_pCompositor->addToFadingOutSafe(self.lock());
@@ -221,9 +220,9 @@ void CLayerSurface::onUnmap() {
g_pCompositor->addToFadingOutSafe(self.lock());
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
const auto PMONITOR = monitor.lock();
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == surface->resource() || g_pSeatManager->state.pointerFocus == surface->resource();
if (!PMONITOR)
return;
@@ -232,7 +231,7 @@ void CLayerSurface::onUnmap() {
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable()))
g_pInputManager->refocusLastWindow(PMONITOR);
else if (g_pCompositor->m_pLastFocus)
else if (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus != surface->resource())
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
@@ -256,13 +255,13 @@ void CLayerSurface::onCommit() {
if (layerSurface->surface && !layerSurface->surface->current.texture) {
fadingOut = false;
geometry = {};
g_pHyprRenderer->arrangeLayersForMonitor(monitorID);
g_pHyprRenderer->arrangeLayersForMonitor(monitorID());
}
return;
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
const auto PMONITOR = monitor.lock();
if (!PMONITOR)
return;
@@ -321,8 +320,18 @@ void CLayerSurface::onCommit() {
realSize.setValueAndWarp(geometry.size());
}
if (mapped) {
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource();
if (mapped && (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
bool WASLASTFOCUS = false;
layerSurface->surface->breadthfirst(
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->state.keyboardFocus == surf; },
nullptr);
if (!WASLASTFOCUS && popupHead) {
popupHead->breadthfirst(
[&WASLASTFOCUS](CPopup* popup, void* data) {
WASLASTFOCUS = WASLASTFOCUS || (popup->m_pWLSurface && g_pSeatManager->state.keyboardFocus == popup->m_pWLSurface->resource());
},
nullptr);
}
const bool WASEXCLUSIVE = interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
@@ -332,11 +341,13 @@ void CLayerSurface::onCommit() {
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
// if the surface was focused and interactive but now isn't, refocus
if (WASLASTFOCUS && !layerSurface->current.interactivity) {
if (WASLASTFOCUS && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
// so unfocus the surface here.
g_pCompositor->focusSurface(nullptr);
g_pInputManager->refocusLastWindow(g_pCompositor->getMonitorFromID(monitorID));
g_pInputManager->refocusLastWindow(monitor.lock());
} else if (WASLASTFOCUS && WASEXCLUSIVE && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
g_pInputManager->simulateMouseMovement();
} else if (!WASEXCLUSIVE && ISEXCLUSIVE) {
// if now exclusive and not previously
g_pSeatManager->setGrab(nullptr);
@@ -367,38 +378,57 @@ 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;
}
}
}
@@ -545,3 +575,7 @@ int CLayerSurface::popupsCount() {
popupHead->breadthfirst([](CPopup* p, void* data) { *(int*)data += 1; }, &no);
return no;
}
MONITORID CLayerSurface::monitorID() {
return monitor ? monitor->ID : MONITOR_INVALID;
}

View File

@@ -4,11 +4,7 @@
#include "../defines.hpp"
#include "WLSurface.hpp"
#include "../helpers/AnimatedVariable.hpp"
struct SLayerRule {
std::string targetNamespace = "";
std::string rule = "";
};
#include "LayerRule.hpp"
class CLayerShellResource;
@@ -42,7 +38,7 @@ class CLayerSurface {
bool mapped = false;
uint32_t layer = 0;
MONITORID monitorID = -1;
PHLMONITORREF monitor;
bool fadingOut = false;
bool readyToDelete = false;
@@ -70,6 +66,7 @@ class CLayerSurface {
void onMap();
void onUnmap();
void onCommit();
MONITORID monitorID();
private:
struct {
@@ -83,6 +80,6 @@ class CLayerSurface {
// For the list lookup
bool operator==(const CLayerSurface& rhs) const {
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
return layerSurface == rhs.layerSurface && monitor == rhs.monitor;
}
};

View File

@@ -4,6 +4,8 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include <ranges>
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
@@ -14,13 +16,10 @@ 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, 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();
@@ -106,6 +105,8 @@ void CPopup::onUnmap() {
return;
}
m_bMapped = false;
m_vLastSize = m_pResource->surface->surface->current.size;
const auto COORDS = coordsGlobal();
@@ -116,8 +117,6 @@ void CPopup::onUnmap() {
m_pSubsurfaceHead.reset();
g_pInputManager->simulateMouseMovement();
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
@@ -131,6 +130,11 @@ void CPopup::onUnmap() {
g_pHyprRenderer->damageBox(&box);
},
nullptr);
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
if (WASLASTFOCUS)
g_pInputManager->simulateMouseMovement();
}
void CPopup::onCommit(bool ignoreSiblings) {
@@ -288,12 +292,13 @@ bool CPopup::visible() {
return false;
}
void CPopup::bfHelper(std::vector<CPopup*> nodes, std::function<void(CPopup*, void*)> fn, void* data) {
void CPopup::bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data) {
for (auto const& n : nodes) {
fn(n, data);
}
std::vector<CPopup*> nodes2;
nodes2.reserve(nodes.size() * 2);
for (auto const& n : nodes) {
for (auto const& c : n->m_vChildren) {
@@ -316,7 +321,7 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups);
for (auto const& p : popups | std::views::reverse) {
if (!p->m_pResource)
if (!p->m_pResource || !p->m_bMapped)
continue;
if (!allowsInput) {

View File

@@ -41,6 +41,7 @@ class CPopup {
//
SP<CWLSurface> m_pWLSurface;
bool m_bMapped = false;
private:
// T1 owners, each popup has to have one of these
@@ -57,8 +58,7 @@ class CPopup {
bool m_bRequestedReposition = false;
bool m_bInert = false;
bool m_bMapped = false;
bool m_bInert = false;
//
std::vector<SP<CPopup>> m_vChildren;
@@ -81,5 +81,5 @@ class CPopup {
Vector2D localToGlobal(const Vector2D& rel);
Vector2D t1ParentCoords();
static void bfHelper(std::vector<CPopup*> nodes, std::function<void(CPopup*, void*)> fn, void* data);
static void bfHelper(std::vector<CPopup*> const& nodes, std::function<void(CPopup*, void*)> fn, void* data);
};

View File

@@ -1,3 +1,5 @@
#include <re2/re2.h>
#include <any>
#include <bit>
#include <string_view>
@@ -113,7 +115,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
const int BORDERSIZE = getRealBorderSize();
if (m_sWindowData.dimAround.valueOrDefault()) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR)
return {{m_vRealPosition.value().x - PMONITOR->vecPosition.x, m_vRealPosition.value().y - PMONITOR->vecPosition.y},
{PMONITOR->vecSize.x - (m_vRealPosition.value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.value().y - PMONITOR->vecPosition.y)}};
}
@@ -173,7 +175,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
CBox CWindow::getFullWindowBoundingBox() {
if (m_sWindowData.dimAround.valueOrDefault()) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR)
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
@@ -186,7 +188,7 @@ CBox CWindow::getFullWindowBoundingBox() {
}
CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PMONITOR = m_pMonitor.lock();
if (!PMONITOR)
return {m_vPosition, m_vSize};
@@ -221,7 +223,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
CBox CWindow::getWindowBoxUnified(uint64_t properties) {
if (m_sWindowData.dimAround.valueOrDefault()) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PMONITOR = m_pMonitor.lock();
if (PMONITOR)
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
@@ -240,10 +242,6 @@ CBox CWindow::getWindowBoxUnified(uint64_t properties) {
return box;
}
CBox CWindow::getWindowMainSurfaceBox() {
return {m_vRealPosition.value().x, m_vRealPosition.value().y, m_vRealSize.value().x, m_vRealSize.value().y};
}
SBoxExtents CWindow::getFullWindowReservedArea() {
return g_pDecorationPositioner->getWindowDecorationReserved(m_pSelf.lock());
}
@@ -356,9 +354,9 @@ void CWindow::updateSurfaceScaleTransformDetails(bool force) {
const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID);
m_iLastSurfaceMonitorID = m_iMonitorID;
m_iLastSurfaceMonitorID = monitorID();
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PNEWMONITOR = m_pMonitor.lock();
if (!PNEWMONITOR)
return;
@@ -370,10 +368,10 @@ void CWindow::updateSurfaceScaleTransformDetails(bool force) {
m_pWLSurface->resource()->breadthfirst([PNEWMONITOR](SP<CWLSurfaceResource> s, const Vector2D& offset, void* d) { s->enter(PNEWMONITOR->self.lock()); }, nullptr);
}
m_pWLSurface->resource()->breadthfirst(
[this](SP<CWLSurfaceResource> s, const Vector2D& offset, void* d) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PMONITOR = m_pMonitor.lock();
m_pWLSurface->resource()->breadthfirst(
[PMONITOR](SP<CWLSurfaceResource> s, const Vector2D& offset, void* d) {
const auto PSURFACE = CWLSurface::fromResource(s);
if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale)
return;
@@ -408,7 +406,7 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
const auto OLDWORKSPACE = m_pWorkspace;
m_iMonitorMovedFrom = OLDWORKSPACE ? OLDWORKSPACE->m_iMonitorID : -1;
m_iMonitorMovedFrom = OLDWORKSPACE ? OLDWORKSPACE->monitorID() : -1;
m_fMovingToWorkspaceAlpha.setValueAndWarp(1.F);
m_fMovingToWorkspaceAlpha = 0.F;
m_fMovingToWorkspaceAlpha.setCallbackOnEnd([this](void* thisptr) { m_iMonitorMovedFrom = -1; });
@@ -417,13 +415,13 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
setAnimationsToMove();
g_pCompositor->updateWorkspaceWindows(OLDWORKSPACE->m_iID);
g_pCompositor->updateWorkspaceWindowData(OLDWORKSPACE->m_iID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(OLDWORKSPACE->m_iMonitorID);
OLDWORKSPACE->updateWindows();
OLDWORKSPACE->updateWindowData();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(OLDWORKSPACE->monitorID());
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceWindowData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
pWorkspace->updateWindows();
pWorkspace->updateWindowData();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@@ -435,31 +433,37 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
if (const auto SWALLOWED = m_pSwallowed.lock()) {
SWALLOWED->moveToWorkspace(pWorkspace);
SWALLOWED->m_iMonitorID = m_iMonitorID;
SWALLOWED->m_pMonitor = m_pMonitor;
}
// update xwayland coords
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.value());
if (OLDWORKSPACE && g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE->m_iID) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE->m_iID) == 0 && *PCLOSEONLASTSPECIAL) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(OLDWORKSPACE->m_iMonitorID); PMONITOR)
if (OLDWORKSPACE && g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE->m_iID) && OLDWORKSPACE->getWindows() == 0 && *PCLOSEONLASTSPECIAL) {
if (const auto PMONITOR = OLDWORKSPACE->m_pMonitor.lock(); PMONITOR)
PMONITOR->setSpecialWorkspace(nullptr);
}
}
PHLWINDOW CWindow::X11TransientFor() {
PHLWINDOW CWindow::x11TransientFor() {
if (!m_pXWaylandSurface || !m_pXWaylandSurface->parent)
return nullptr;
auto s = m_pXWaylandSurface->parent;
auto oldParent = s;
auto s = m_pXWaylandSurface->parent;
std::vector<SP<CXWaylandSurface>> visited;
while (s) {
// break cyclic loop of m_pXWaylandSurface being parent of itself, #TODO reject this from even being created?
if (!s->parent || s->parent == oldParent)
// break loops. Some X apps make them, and it seems like it's valid behavior?!?!?!
// TODO: we should reject loops being created in the first place.
if (std::find(visited.begin(), visited.end(), s) != visited.end())
break;
visited.emplace_back(s.lock());
s = s->parent;
}
if (s == m_pXWaylandSurface)
return nullptr; // dead-ass circle
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pXWaylandSurface != s)
continue;
@@ -514,20 +518,22 @@ void CWindow::onUnmap() {
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other.expired() || other.lock().get() == this; });
if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(workspaceID()) == 0 && onSpecialWorkspace()) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (*PCLOSEONLASTSPECIAL && m_pWorkspace && m_pWorkspace->getWindows() == 0 && onSpecialWorkspace()) {
const auto PMONITOR = m_pMonitor.lock();
if (PMONITOR && PMONITOR->activeSpecialWorkspace && PMONITOR->activeSpecialWorkspace == m_pWorkspace)
PMONITOR->setSpecialWorkspace(nullptr);
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PMONITOR = m_pMonitor.lock();
if (PMONITOR && PMONITOR->solitaryClient.lock().get() == this)
PMONITOR->solitaryClient.reset();
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceWindowData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
if (m_pWorkspace) {
m_pWorkspace->updateWindows();
m_pWorkspace->updateWindowData();
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
m_pWorkspace.reset();
@@ -610,156 +616,179 @@ bool CWindow::isHidden() {
return m_bHidden;
}
void CWindow::applyDynamicRule(const SWindowRule& r) {
const eOverridePriority priority = r.szValue == "execRule" ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE;
const CVarList VARS(r.szRule, 0, ' ');
if (r.szRule.starts_with("tag")) {
CVarList vars{r.szRule, 0, 's', true};
void CWindow::applyDynamicRule(const SP<CWindowRule>& r) {
const eOverridePriority priority = r->execRule ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE;
if (vars.size() == 2 && vars[0] == "tag")
m_tags.applyTag(vars[1], true);
else
Debug::log(ERR, "Tag rule invalid: {}", r.szRule);
} else if (r.szRule.starts_with("opacity")) {
try {
CVarList vars(r.szRule, 0, ' ');
switch (r->ruleType) {
case CWindowRule::RULE_TAG: {
CVarList vars{r->szRule, 0, 's', true};
int opacityIDX = 0;
for (auto const& r : vars) {
if (r == "opacity")
continue;
if (r == "override") {
if (opacityIDX == 1)
m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{m_sWindowData.alpha.value().m_fAlpha, true}, priority);
else if (opacityIDX == 2)
m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaInactive.value().m_fAlpha, true}, priority);
else if (opacityIDX == 3)
m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaFullscreen.value().m_fAlpha, true}, priority);
} else {
if (opacityIDX == 0) {
m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else if (opacityIDX == 1) {
m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else if (opacityIDX == 2) {
m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else {
throw std::runtime_error("more than 3 alpha values");
}
opacityIDX++;
}
}
if (opacityIDX == 1) {
m_sWindowData.alphaInactive = m_sWindowData.alpha;
m_sWindowData.alphaFullscreen = m_sWindowData.alpha;
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.starts_with("animation")) {
auto STYLE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
m_sWindowData.animationStyle = CWindowOverridableVar(STYLE, priority);
} else if (r.szRule.starts_with("bordercolor")) {
try {
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(trim(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sWindowData.activeBorderColor = CWindowOverridableVar(CGradientValueData(CColor(configStringToInt(colorsAndAngles[0]))), priority);
m_sWindowData.inactiveBorderColor = CWindowOverridableVar(CGradientValueData(CColor(configStringToInt(colorsAndAngles[1]))), priority);
return;
}
for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_vColors.push_back(configStringToInt(token));
else
inactiveBorderGradient.m_vColors.push_back(configStringToInt(token));
}
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule);
else if (activeBorderGradient.m_vColors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule);
else if (inactiveBorderGradient.m_vColors.empty())
m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
else {
m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
m_sWindowData.inactiveBorderColor = CWindowOverridableVar(inactiveBorderGradient, priority);
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (auto search = g_pConfigManager->mbWindowProperties.find(VARS[0]); search != g_pConfigManager->mbWindowProperties.end()) {
if (VARS[1].empty()) {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(true, priority);
} else {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar((bool)configStringToInt(VARS[1]), priority);
} catch (...) {}
if (vars.size() == 2 && vars[0] == "tag")
m_tags.applyTag(vars[1], true);
else
Debug::log(ERR, "Tag rule invalid: {}", r->szRule);
break;
}
} else if (auto search = g_pConfigManager->miWindowProperties.find(VARS[0]); search != g_pConfigManager->miWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stoi(VARS[1]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.starts_with("idleinhibit")) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
case CWindowRule::RULE_OPACITY: {
try {
CVarList vars(r->szRule, 0, ' ');
if (IDLERULE == "none")
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
else if (IDLERULE == "always")
m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
else if (IDLERULE == "focus")
m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
else if (IDLERULE == "fullscreen")
m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
} else if (r.szRule.starts_with("maxsize")) {
try {
if (!m_bIsFloating)
return;
const auto VEC = configStringToVector2D(r.szRule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for maxsize");
return;
int opacityIDX = 0;
for (auto const& r : vars) {
if (r == "opacity")
continue;
if (r == "override") {
if (opacityIDX == 1)
m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{m_sWindowData.alpha.value().m_fAlpha, true}, priority);
else if (opacityIDX == 2)
m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaInactive.value().m_fAlpha, true}, priority);
else if (opacityIDX == 3)
m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaFullscreen.value().m_fAlpha, true}, priority);
} else {
if (opacityIDX == 0) {
m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else if (opacityIDX == 1) {
m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else if (opacityIDX == 2) {
m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority);
} else {
throw std::runtime_error("more than 3 alpha values");
}
opacityIDX++;
}
}
if (opacityIDX == 1) {
m_sWindowData.alphaInactive = m_sWindowData.alpha;
m_sWindowData.alphaFullscreen = m_sWindowData.alpha;
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_ANIMATION: {
auto STYLE = r->szRule.substr(r->szRule.find_first_of(' ') + 1);
m_sWindowData.animationStyle = CWindowOverridableVar(STYLE, priority);
break;
}
case CWindowRule::RULE_BORDERCOLOR: {
try {
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(trim(r->szRule.substr(r->szRule.find_first_of(' ') + 1)), 0, 's', true);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sWindowData.activeBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), priority);
m_sWindowData.inactiveBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), priority);
return;
}
for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_vColors.push_back(configStringToInt(token).value_or(0));
else
inactiveBorderGradient.m_vColors.push_back(configStringToInt(token).value_or(0));
}
activeBorderGradient.updateColorsOk();
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r->szRule);
else if (activeBorderGradient.m_vColors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r->szRule);
else if (inactiveBorderGradient.m_vColors.empty())
m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
else {
m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
m_sWindowData.inactiveBorderColor = CWindowOverridableVar(inactiveBorderGradient, priority);
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_IDLEINHIBIT: {
auto IDLERULE = r->szRule.substr(r->szRule.find_first_of(' ') + 1);
if (IDLERULE == "none")
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
else if (IDLERULE == "always")
m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
else if (IDLERULE == "focus")
m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
else if (IDLERULE == "fullscreen")
m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
break;
}
case CWindowRule::RULE_MAXSIZE: {
try {
if (!m_bIsFloating)
return;
const auto VEC = configStringToVector2D(r->szRule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for maxsize");
return;
}
m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority);
clampWindowSize(std::nullopt, m_sWindowData.maxSize.value());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_MINSIZE: {
try {
if (!m_bIsFloating)
return;
const auto VEC = configStringToVector2D(r->szRule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for minsize");
return;
}
m_sWindowData.minSize = CWindowOverridableVar(VEC, priority);
clampWindowSize(m_sWindowData.minSize.value(), std::nullopt);
if (m_sGroupData.pNextWindow.expired())
setHidden(false);
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
case CWindowRule::RULE_RENDERUNFOCUSED: {
m_sWindowData.renderUnfocused = CWindowOverridableVar(true, priority);
g_pHyprRenderer->addWindowToRenderUnfocused(m_pSelf.lock());
break;
}
case CWindowRule::RULE_PROP: {
const CVarList VARS(r->szRule, 0, ' ');
if (auto search = g_pConfigManager->miWindowProperties.find(VARS[1]); search != g_pConfigManager->miWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stoi(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
} else if (auto search = g_pConfigManager->mfWindowProperties.find(VARS[1]); search != g_pConfigManager->mfWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stof(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
} else if (auto search = g_pConfigManager->mbWindowProperties.find(VARS[1]); search != g_pConfigManager->mbWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(VARS[2].empty() ? true : (bool)std::stoi(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
}
m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority);
m_vRealSize =
Vector2D(std::min((double)m_sWindowData.maxSize.value().x, m_vRealSize.goal().x), std::min((double)m_sWindowData.maxSize.value().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.starts_with("minsize")) {
try {
if (!m_bIsFloating)
return;
const auto VEC = configStringToVector2D(r.szRule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for minsize");
return;
}
m_sWindowData.minSize = CWindowOverridableVar(VEC, priority);
m_vRealSize =
Vector2D(std::max((double)m_sWindowData.minSize.value().x, m_vRealSize.goal().x), std::max((double)m_sWindowData.minSize.value().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal());
if (m_sGroupData.pNextWindow.expired())
setHidden(false);
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "renderunfocused") {
m_sWindowData.renderUnfocused = CWindowOverridableVar(true, priority);
g_pHyprRenderer->addWindowToRenderUnfocused(m_pSelf.lock());
break;
}
default: break;
}
}
@@ -784,13 +813,13 @@ void CWindow::updateDynamicRules() {
m_tags.removeDynamicTags();
m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock());
for (auto const& r : m_vMatchedRules) {
for (const auto& r : m_vMatchedRules) {
applyDynamicRule(r);
}
EMIT_HOOK_EVENT("windowUpdateRules", m_pSelf.lock());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
}
// check if the point is "hidden" under a rounded corner of the window
@@ -855,9 +884,11 @@ void CWindow::createGroup() {
addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(m_pSelf.lock()));
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceWindowData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
if (m_pWorkspace) {
m_pWorkspace->updateWindows();
m_pWorkspace->updateWindowData();
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
g_pEventManager->postEvent(SHyprIPCEvent{"togglegroup", std::format("1,{:x}", (uintptr_t)this)});
@@ -873,9 +904,11 @@ void CWindow::destroyGroup() {
m_sGroupData.pNextWindow.reset();
m_sGroupData.head = false;
updateWindowDecos();
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceWindowData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
if (m_pWorkspace) {
m_pWorkspace->updateWindows();
m_pWorkspace->updateWindowData();
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
g_pEventManager->postEvent(SHyprIPCEvent{"togglegroup", std::format("0,{:x}", (uintptr_t)this)});
@@ -909,9 +942,11 @@ void CWindow::destroyGroup() {
}
g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV;
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceWindowData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
if (m_pWorkspace) {
m_pWorkspace->updateWindows();
m_pWorkspace->updateWindowData();
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
if (!addresses.empty())
@@ -1087,7 +1122,7 @@ void CWindow::updateGroupOutputs() {
const auto WS = m_pWorkspace;
while (curr.get() != this) {
curr->m_iMonitorID = m_iMonitorID;
curr->m_pMonitor = m_pMonitor;
curr->moveToWorkspace(WS);
curr->m_vRealPosition = m_vRealPosition.goal();
@@ -1168,6 +1203,16 @@ int CWindow::getRealBorderSize() {
return m_sWindowData.borderSize.valueOr(*PBORDERSIZE);
}
float CWindow::getScrollMouse() {
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
return m_sWindowData.scrollMouse.valueOr(*PINPUTSCROLLFACTOR);
}
float CWindow::getScrollTouchpad() {
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor");
return m_sWindowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR);
}
bool CWindow::canBeTorn() {
static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing");
return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING;
@@ -1184,7 +1229,7 @@ void CWindow::setSuspended(bool suspend) {
m_bSuspended = suspend;
}
bool CWindow::visibleOnMonitor(CMonitor* pMonitor) {
bool CWindow::visibleOnMonitor(PHLMONITOR pMonitor) {
CBox wbox = {m_vRealPosition.value(), m_vRealSize.value()};
return !wbox.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty();
@@ -1209,7 +1254,7 @@ void CWindow::onWorkspaceAnimUpdate() {
if (!PWORKSPACE)
return;
const auto PWSMON = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID);
const auto PWSMON = m_pMonitor.lock();
if (!PWSMON)
return;
@@ -1253,6 +1298,16 @@ int CWindow::surfacesCount() {
return no;
}
void CWindow::clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize) {
const Vector2D REALSIZE = m_vRealSize.goal();
const Vector2D NEWSIZE = REALSIZE.clamp(minSize.value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), maxSize.value_or(Vector2D{INFINITY, INFINITY}));
const Vector2D DELTA = REALSIZE - NEWSIZE;
m_vRealPosition = m_vRealPosition.goal() + DELTA / 2.0;
m_vRealSize = NEWSIZE;
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), NEWSIZE);
}
bool CWindow::isFullscreen() {
return m_sFullscreenState.internal != FSMODE_NONE;
}
@@ -1265,6 +1320,10 @@ WORKSPACEID CWindow::workspaceID() {
return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace;
}
MONITORID CWindow::monitorID() {
return m_pMonitor ? m_pMonitor->ID : MONITOR_INVALID;
}
bool CWindow::onSpecialWorkspace() {
return m_pWorkspace ? m_pWorkspace->m_bIsSpecialWorkspace : g_pCompositor->isWorkspaceSpecial(m_iLastWorkspace);
}
@@ -1451,7 +1510,7 @@ void CWindow::onX11Configure(CBox box) {
m_pXWaylandSurface->configure(box);
m_vPendingReportedSize = box.size();
m_vReportedSize = box.size();
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR)
m_fX11SurfaceScaledBy = PMONITOR->scale;
return;
}
@@ -1477,7 +1536,7 @@ void CWindow::onX11Configure(CBox box) {
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR) {
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR) {
m_vRealSize.setValueAndWarp(m_vRealSize.goal() / PMONITOR->scale);
m_fX11SurfaceScaledBy = PMONITOR->scale;
}
@@ -1493,7 +1552,7 @@ void CWindow::onX11Configure(CBox box) {
updateWindowDecos();
if (!g_pCompositor->isWorkspaceVisible(m_pWorkspace))
if (!m_pWorkspace || !m_pWorkspace->isVisible())
return; // further things are only for visible windows
m_pWorkspace = g_pCompositor->getMonitorFromVector(m_vRealPosition.value() + m_vRealSize.value() / 2.f)->activeWorkspace;
@@ -1505,15 +1564,15 @@ void CWindow::onX11Configure(CBox box) {
g_pHyprRenderer->damageWindow(m_pSelf.lock());
}
void CWindow::warpCursor() {
void CWindow::warpCursor(bool force) {
static auto PERSISTENTWARPS = CConfigValue<Hyprlang::INT>("cursor:persistent_warps");
const auto coords = m_vRelativeCursorCoordsOnLastWarp;
m_vRelativeCursorCoordsOnLastWarp.x = -1; // reset m_vRelativeCursorCoordsOnLastWarp
if (*PERSISTENTWARPS && coords.x > 0 && coords.y > 0 && coords < m_vSize) // don't warp cursor outside the window
g_pCompositor->warpCursorTo(m_vPosition + coords);
g_pCompositor->warpCursorTo(m_vPosition + coords, force);
else
g_pCompositor->warpCursorTo(middle());
g_pCompositor->warpCursorTo(middle(), force);
}
PHLWINDOW CWindow::getSwallower() {
@@ -1521,7 +1580,7 @@ PHLWINDOW CWindow::getSwallower() {
static auto PSWALLOWEXREGEX = CConfigValue<std::string>("misc:swallow_exception_regex");
static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow");
if (!*PSWALLOW || (*PSWALLOWREGEX).empty())
if (!*PSWALLOW || std::string{*PSWALLOWREGEX} == STRVAL_EMPTY || (*PSWALLOWREGEX).empty())
return nullptr;
// check parent
@@ -1544,13 +1603,13 @@ PHLWINDOW CWindow::getSwallower() {
}
if (!(*PSWALLOWREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return !std::regex_match(other->m_szClass, std::regex(*PSWALLOWREGEX)); });
std::erase_if(candidates, [&](const auto& other) { return !RE2::FullMatch(other->m_szClass, *PSWALLOWREGEX); });
if (candidates.size() <= 0)
return nullptr;
if (!(*PSWALLOWEXREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return std::regex_match(other->m_szTitle, std::regex(*PSWALLOWEXREGEX)); });
std::erase_if(candidates, [&](const auto& other) { return RE2::FullMatch(other->m_szTitle, *PSWALLOWEXREGEX); });
if (candidates.size() <= 0)
return nullptr;
@@ -1578,6 +1637,9 @@ void CWindow::unsetWindowData(eOverridePriority priority) {
for (auto const& element : g_pConfigManager->miWindowProperties) {
element.second(m_pSelf.lock())->unset(priority);
}
for (auto const& element : g_pConfigManager->mfWindowProperties) {
element.second(m_pSelf.lock())->unset(priority);
}
}
bool CWindow::isX11OverrideRedirect() {
@@ -1587,3 +1649,29 @@ bool CWindow::isX11OverrideRedirect() {
bool CWindow::isModal() {
return (m_pXWaylandSurface && m_pXWaylandSurface->modal);
}
Vector2D CWindow::requestedMinSize() {
if ((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && !m_pXDGSurface->toplevel))
return Vector2D(1, 1);
Vector2D minSize = m_bIsX11 ? Vector2D(m_pXWaylandSurface->sizeHints->min_width, m_pXWaylandSurface->sizeHints->min_height) : m_pXDGSurface->toplevel->layoutMinSize();
minSize = minSize.clamp({1, 1});
return minSize;
}
Vector2D CWindow::requestedMaxSize() {
constexpr int NO_MAX_SIZE_LIMIT = 99999;
if (((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && !m_pXDGSurface->toplevel) || m_sWindowData.noMaxSize.valueOrDefault()))
return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT);
Vector2D maxSize = m_bIsX11 ? Vector2D(m_pXWaylandSurface->sizeHints->max_width, m_pXWaylandSurface->sizeHints->max_height) : m_pXDGSurface->toplevel->layoutMaxSize();
if (maxSize.x < 5)
maxSize.x = NO_MAX_SIZE_LIMIT;
if (maxSize.y < 5)
maxSize.y = NO_MAX_SIZE_LIMIT;
return maxSize;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <deque>
#include <vector>
#include <string>
#include "../config/ConfigDataValues.hpp"
@@ -17,18 +17,19 @@
#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 +41,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,7 +51,7 @@ enum eGetWindowProperties {
USE_PROP_TILED = 1 << 5,
};
enum eSuppressEvents {
enum eSuppressEvents : uint8_t {
SUPPRESS_NONE = 0,
SUPPRESS_FULLSCREEN = 1 << 0,
SUPPRESS_MAXIMIZE = 1 << 1,
@@ -64,7 +65,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 +73,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 +86,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;
@@ -144,6 +143,13 @@ class CWindowOverridableVar {
unset(priority);
}
operator std::optional<T>() {
if (hasValue())
return value();
else
return std::nullopt;
}
private:
std::map<eOverridePriority, T> values;
T defaultValue; // used for toggling, so required for bool
@@ -179,6 +185,9 @@ struct SWindowData {
CWindowOverridableVar<int> rounding;
CWindowOverridableVar<int> borderSize;
CWindowOverridableVar<float> scrollMouse;
CWindowOverridableVar<float> scrollTouchpad;
CWindowOverridableVar<std::string> animationStyle;
CWindowOverridableVar<Vector2D> maxSize;
CWindowOverridableVar<Vector2D> minSize;
@@ -187,32 +196,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;
};
@@ -271,13 +260,13 @@ 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};
MONITORID m_iMonitorID = -1;
SFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
std::string m_szTitle = "";
std::string m_szClass = "";
std::string m_szInitialTitle = "";
std::string m_szInitialClass = "";
PHLWORKSPACE m_pWorkspace;
PHLMONITORREF m_pMonitor;
bool m_bIsMapped = false;
@@ -325,6 +314,9 @@ class CWindow {
// 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;
@@ -333,8 +325,8 @@ class CWindow {
// Window decorations
// TODO: make this a SP.
std::deque<std::unique_ptr<IHyprWindowDecoration>> m_dWindowDecorations;
std::vector<IHyprWindowDecoration*> m_vDecosToRemove;
std::vector<std::unique_ptr<IHyprWindowDecoration>> m_dWindowDecorations;
std::vector<IHyprWindowDecoration*> m_vDecosToRemove;
// Special render data, rules, etc
SWindowData m_sWindowData;
@@ -346,7 +338,7 @@ class CWindow {
CAnimatedVariable<float> m_fActiveInactiveAlpha;
// animated shadow color
CAnimatedVariable<CColor> m_cRealShadowColor;
CAnimatedVariable<CHyprColor> m_cRealShadowColor;
// animated tint
CAnimatedVariable<float> m_fDimPercent;
@@ -357,6 +349,7 @@ class CWindow {
// swallowing
PHLWINDOWREF m_pSwallowed;
bool m_bGroupSwallowed = false;
// focus stuff
bool m_bStayFocused = false;
@@ -383,13 +376,13 @@ 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;
}
@@ -398,7 +391,6 @@ class CWindow {
CBox getFullWindowBoundingBox();
SBoxExtents getFullWindowExtents();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowMainSurfaceBox();
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
void updateWindowDecos();
@@ -411,12 +403,12 @@ class CWindow {
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();
@@ -424,24 +416,24 @@ class CWindow {
float rounding();
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(CMonitor* pMonitor);
bool visibleOnMonitor(PHLMONITOR pMonitor);
WORKSPACEID workspaceID();
MONITORID monitorID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode);
int getRealBorderSize();
float getScrollMouse();
float getScrollTouchpad();
void updateWindowData();
void updateWindowData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(void* ptr);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
int popupsCount();
void applyGroupRules();
void createGroup();
void destroyGroup();
@@ -464,11 +456,17 @@ class CWindow {
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();
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);
@@ -555,7 +553,7 @@ struct std::formatter<PHLWINDOW, CharT> : std::formatter<CharT> {
if (formatWorkspace)
std::format_to(out, ", workspace: {}", w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID);
if (formatMonitor)
std::format_to(out, ", monitor: {}", w->m_iMonitorID);
std::format_to(out, ", monitor: {}", w->monitorID());
if (formatClass)
std::format_to(out, ", class: {}", w->m_szClass);
return std::format_to(out, "]");

100
src/desktop/WindowRule.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "WindowRule.hpp"
#include <unordered_set>
#include <algorithm>
#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", "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("bordersize"))
ruleType = RULE_BORDERSIZE;
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("rounding"))
ruleType = RULE_ROUNDING;
else if (rule.starts_with("scrollmouse"))
ruleType = RULE_SCROLLMOUSE;
else if (rule.starts_with("scrolltouchpad"))
ruleType = RULE_SCROLLTOUCHPAD;
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("xray"))
ruleType = RULE_XRAY;
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,65 @@
#pragma once
#include <string>
#include <cstdint>
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_BORDERSIZE,
RULE_CENTER,
RULE_FULLSCREENSTATE,
RULE_GROUP,
RULE_IDLEINHIBIT,
RULE_MAXSIZE,
RULE_MINSIZE,
RULE_MONITOR,
RULE_MOVE,
RULE_OPACITY,
RULE_PLUGIN,
RULE_PSEUDO,
RULE_ROUNDING,
RULE_SCROLLMOUSE,
RULE_SCROLLTOUCHPAD,
RULE_SIZE,
RULE_SUPPRESSEVENT,
RULE_TAG,
RULE_WORKSPACE,
RULE_XRAY,
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
};

View File

@@ -5,18 +5,15 @@
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
PHLWORKSPACE CWorkspace::create(WORKSPACEID id, MONITORID monitorID, std::string name, bool special, bool isEmpty) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmpty);
PHLWORKSPACE CWorkspace::create(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitor, name, special, isEmpty);
workspace->init(workspace);
return workspace;
}
CWorkspace::CWorkspace(WORKSPACEID id, MONITORID monitorID, std::string name, bool special, bool isEmpty) {
m_iMonitorID = monitorID;
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) {
@@ -103,10 +100,10 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
});
if (ANIMSTYLE.starts_with("slidefade")) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
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));
@@ -151,7 +148,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
}
} else if (ANIMSTYLE == "slidevert") {
// fallback is slide
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
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.
@@ -164,7 +161,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
}
} else {
// fallback is slide
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
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.
@@ -224,7 +221,7 @@ void CWorkspace::rememberPrevWorkspace(const PHLWORKSPACE& prev) {
m_sPrevWorkspace.id = prev->m_iID;
m_sPrevWorkspace.name = prev->m_szName;
if (prev->m_iMonitorID == m_iMonitorID) {
if (prev->m_pMonitor == m_pMonitor) {
m_sPrevWorkspacePerMonitor.id = prev->m_iID;
m_sPrevWorkspacePerMonitor.name = prev->m_szName;
}
@@ -278,9 +275,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 +293,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 +329,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;
}
@@ -347,7 +344,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
const auto PMONITOR = g_pCompositor->getMonitorFromString(prop);
if (!(PMONITOR ? PMONITOR->ID == m_iMonitorID : false))
if (!(PMONITOR ? PMONITOR == m_pMonitor : false))
return false;
continue;
}
@@ -367,7 +364,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 +419,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 +453,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;
@@ -512,12 +509,151 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
}
void CWorkspace::markInert() {
m_bInert = true;
m_iID = WORKSPACE_INVALID;
m_iMonitorID = MONITOR_INVALID;
m_bVisible = false;
m_bInert = true;
m_iID = WORKSPACE_INVALID;
m_bVisible = false;
m_pMonitor.reset();
}
bool CWorkspace::inert() {
return m_bInert;
}
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;
g_pXWaylandManager->setWindowSize(w, w->m_vRealSize.value(), 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

@@ -17,16 +17,16 @@ class CWindow;
class CWorkspace {
public:
static PHLWORKSPACE create(WORKSPACEID id, MONITORID monitorID, std::string name, bool special = false, bool isEmpty = true);
static PHLWORKSPACE create(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true);
// use create() don't use this
CWorkspace(WORKSPACEID id, MONITORID monitorID, std::string name, bool special = false, bool isEmpty = true);
CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true);
~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 = "";
MONITORID m_iMonitorID = MONITOR_INVALID;
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;
@@ -63,22 +63,29 @@ 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;
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);

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();
@@ -36,5 +36,5 @@ class IHID {
CSignal destroy;
} events;
std::string deviceName;
std::string deviceName, hlName;
};

View File

@@ -178,13 +178,18 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbState)
xkb_state_unref(xkbState);
if (xkbSymState)
xkb_state_unref(xkbSymState);
xkbState = nullptr;
xkbStaticState = nullptr;
xkbSymState = nullptr;
if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbStaticState = xkb_state_new(keymap);
xkbState = xkb_state_new(keymap);
xkbSymState = xkb_state_new(keymap);
return;
}
@@ -230,6 +235,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(KEYMAP);
xkbStaticState = xkb_state_new(KEYMAP);
xkbSymState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);
@@ -252,6 +258,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(NEWKEYMAP);
xkbStaticState = xkb_state_new(NEWKEYMAP);
xkbSymState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT);
@@ -332,6 +339,9 @@ void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t l
xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group);
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, group);
if (!updateModifiersState())
return;
@@ -382,6 +392,9 @@ void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) {
xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
if (updateModifiersState()) {
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, modifiersState.group);
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,

View File

@@ -82,8 +82,9 @@ class IKeyboard : public IHID {
bool keymapOverridden = false;
xkb_layout_index_t activeLayout = 0;
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr;
xkb_keymap* xkbKeymap = nullptr;
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr,
*xkbSymState = nullptr /* Same as static but gets layouts */;
xkb_keymap* xkbKeymap = nullptr;
struct {
uint32_t depressed = 0, latched = 0, locked = 0, group = 0;
@@ -93,7 +94,6 @@ class IKeyboard : public IHID {
std::array<xkb_mod_index_t, 8> modIndexes = {XKB_MOD_INVALID};
uint32_t leds = 0;
std::string hlName = "";
std::string xkbFilePath = "";
std::string xkbKeymapString = "";
int xkbKeymapFD = -1;

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 {
@@ -104,7 +107,6 @@ class IPointer : public IHID {
CSignal holdEnd;
} pointerEvents;
std::string hlName;
bool connected = false; // means connected to the cursor
std::string boundOutput = "";

View File

@@ -44,7 +44,6 @@ class ITouch : public IHID {
CSignal frame;
} touchEvents;
std::string hlName = "";
std::string boundOutput = "";
WP<ITouch> self;

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

@@ -197,7 +197,7 @@ CTabletPad::CTabletPad(SP<Aquamarine::ITabletPad> pad_) : pad(pad_) {
});
});
listeners.attach = pad->events.attach.registerListener([this](std::any d) {
listeners.attach = pad->events.attach.registerListener([](std::any d) {
; // TODO: this doesn't do anything in aq atm
});

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,10 +91,10 @@ class CTablet : public IHID {
WP<CTablet> self;
bool relativeInput = false;
std::string hlName = "";
bool absolutePos = false;
std::string boundOutput = "";
CBox activeArea;
CBox boundBox; // output-local
CBox boundBox;
private:
CTablet(SP<Aquamarine::ITablet> tablet);
@@ -154,8 +153,6 @@ class CTabletPad : public IHID {
WP<CTabletPad> self;
WP<CTabletTool> parent;
std::string hlName;
private:
CTabletPad(SP<Aquamarine::ITabletPad> pad);
@@ -175,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,
@@ -186,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),
@@ -210,8 +207,6 @@ class CTabletTool : public IHID {
std::vector<uint32_t> buttonsDown;
Vector2D absolutePos; // last known absolute position.
std::string hlName;
private:
CTabletTool(SP<Aquamarine::ITabletTool> tool);

View File

@@ -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);
@@ -24,12 +19,4 @@ namespace Events {
DYNLISTENFUNC(requestMaximize);
DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(ackConfigure);
// Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorStateRequest);
DYNLISTENFUNC(monitorDamage);
DYNLISTENFUNC(monitorNeedsFrame);
DYNLISTENFUNC(monitorCommit);
DYNLISTENFUNC(monitorBind);
};

View File

@@ -1,105 +0,0 @@
#include "../Compositor.hpp"
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
#include "Events.hpp"
#include "../debug/HyprCtl.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/Screencopy.hpp"
#include "../protocols/ToplevelExport.hpp"
#include <aquamarine/output/Output.hpp>
// --------------------------------------------------------- //
// __ __ ____ _ _ _____ _______ ____ _____ _____ //
// | \/ |/ __ \| \ | |_ _|__ __/ __ \| __ \ / ____| //
// | \ / | | | | \| | | | | | | | | | |__) | (___ //
// | |\/| | | | | . ` | | | | | | | | | _ / \___ \ //
// | | | | |__| | |\ |_| |_ | | | |__| | | \ \ ____) | //
// |_| |_|\____/|_| \_|_____| |_| \____/|_| \_\_____/ //
// //
// --------------------------------------------------------- //
void Events::listener_monitorFrame(void* owner, void* data) {
CMonitor* const PMONITOR = (CMonitor*)owner;
if ((g_pCompositor->m_pAqBackend->hasSession() && !g_pCompositor->m_pAqBackend->session->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) {
return m->output != g_pCompositor->m_pUnsafeOutput->output;
})) {
// restore from unsafe state
g_pCompositor->leaveUnsafeState();
}
return; // cannot draw on session inactive (different tty)
}
if (!PMONITOR->m_bEnabled)
return;
g_pHyprRenderer->recheckSolitaryForMonitor(PMONITOR);
PMONITOR->tearingState.busy = false;
if (PMONITOR->tearingState.activelyTearing && PMONITOR->solitaryClient.lock() /* can be invalidated by a recheck */) {
if (!PMONITOR->tearingState.frameScheduledWhileBusy)
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
PMONITOR->tearingState.nextRenderTorn = true;
PMONITOR->tearingState.frameScheduledWhileBusy = false;
}
static auto PENABLERAT = CConfigValue<Hyprlang::INT>("misc:render_ahead_of_time");
static auto PRATSAFE = CConfigValue<Hyprlang::INT>("misc:render_ahead_safezone");
PMONITOR->lastPresentationTimer.reset();
if (*PENABLERAT && !PMONITOR->tearingState.nextRenderTorn) {
if (!PMONITOR->RATScheduled) {
// render
g_pHyprRenderer->renderMonitor(PMONITOR);
}
PMONITOR->RATScheduled = false;
const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(PMONITOR);
if (max + *PRATSAFE > 1000.0 / PMONITOR->refreshRate)
return;
const auto MSLEFT = 1000.0 / PMONITOR->refreshRate - PMONITOR->lastPresentationTimer.getMillis();
PMONITOR->RATScheduled = true;
const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE);
const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME);
if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1)
g_pHyprRenderer->renderMonitor(PMONITOR);
else
wl_event_source_timer_update(PMONITOR->renderTimer, TIMETOSLEEP);
} else {
g_pHyprRenderer->renderMonitor(PMONITOR);
}
}
void Events::listener_monitorNeedsFrame(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
}
void Events::listener_monitorCommit(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
PROTO::screencopy->onOutputCommit(PMONITOR);
PROTO::toplevelExport->onOutputCommit(PMONITOR);
}
}
void Events::listener_monitorBind(void* owner, void* data) {
;
}

View File

@@ -12,6 +12,7 @@
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../xwayland/XSurface.hpp"
#include "managers/PointerManager.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@@ -43,18 +44,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
static auto PSWALLOW = CConfigValue<Hyprlang::INT>("misc:enable_swallow");
static auto PSWALLOWREGEX = CConfigValue<std::string>("misc:swallow_regex");
static auto PNEWTAKESOVERFS = CConfigValue<Hyprlang::INT>("misc:new_window_takes_over_fullscreen");
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
auto PMONITOR = g_pCompositor->m_pLastMonitor.get();
auto PMONITOR = g_pCompositor->m_pLastMonitor.lock();
if (!g_pCompositor->m_pLastMonitor) {
g_pCompositor->setActiveMonitor(g_pCompositor->getMonitorFromVector({}));
PMONITOR = g_pCompositor->m_pLastMonitor.get();
PMONITOR = g_pCompositor->m_pLastMonitor.lock();
}
auto PWORKSPACE = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
PWINDOW->m_iMonitorID = PMONITOR->ID;
PWINDOW->m_pMonitor = PMONITOR;
PWINDOW->m_pWorkspace = PWORKSPACE;
PWINDOW->m_bIsMapped = true;
PWINDOW->m_bReadyToDelete = false;
@@ -135,149 +134,178 @@ void Events::listener_mapWindow(void* owner, void* data) {
// 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;
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_iMonitorID = PMONITOR->ID;
} else {
if (isNumber(MONITORSTR)) {
const MONITORID MONITOR = std::stoi(MONITORSTR);
if (!g_pCompositor->getMonitorFromID(MONITOR))
PWINDOW->m_iMonitorID = 0;
else
PWINDOW->m_iMonitorID = MONITOR;
if (MONITORSTR == "unset") {
PWINDOW->m_pMonitor = PMONITOR;
} else {
const auto PMONITOR = g_pCompositor->getMonitorFromName(MONITORSTR);
if (PMONITOR)
PWINDOW->m_iMonitorID = PMONITOR->ID;
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();
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;
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()); }
break;
}
case CWindowRule::RULE_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;
}
const auto PMONITORFROMID = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ;
if (PWINDOW->m_iMonitorID != PMONITOR->ID) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->m_iMonitorID));
PMONITOR = PMONITORFROMID;
if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName)
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->szValue);
break;
}
case CWindowRule::RULE_FLOAT: {
PWINDOW->m_bIsFloating = true;
break;
}
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
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
}
PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
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;
break;
}
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;
else
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
case CWindowRule::RULE_PIN: {
PWINDOW->m_bPinned = true;
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_FULLSCREEN: {
requestedInternalFSMode = FSMODE_FULLSCREEN;
break;
}
CVarList vars(r.szRule, 0, 's');
std::string vPrev = "";
for (auto const& v : vars) {
if (v == "group")
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);
}
@@ -297,23 +325,23 @@ void Events::listener_mapWindow(void* owner, void* data) {
auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID);
if (!pWorkspace)
pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->m_iMonitorID, requestedWorkspaceName);
pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->monitorID(), requestedWorkspaceName, false);
PWORKSPACE = pWorkspace;
PWINDOW->m_pWorkspace = pWorkspace;
PWINDOW->m_iMonitorID = pWorkspace->m_iMonitorID;
PWINDOW->m_pMonitor = pWorkspace->m_pMonitor;
if (g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeSpecialWorkspace && !pWorkspace->m_bIsSpecialWorkspace)
if (PWINDOW->m_pMonitor.lock()->activeSpecialWorkspace && !pWorkspace->m_bIsSpecialWorkspace)
workspaceSilent = true;
if (!workspaceSilent) {
if (pWorkspace->m_bIsSpecialWorkspace)
g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID)->setSpecialWorkspace(pWorkspace);
pWorkspace->m_pMonitor->setSpecialWorkspace(pWorkspace);
else if (PMONITOR->activeWorkspaceID() != REQUESTEDWORKSPACEID)
g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspaceName);
PMONITOR = g_pCompositor->m_pLastMonitor.get();
PMONITOR = g_pCompositor->m_pLastMonitor.lock();
}
} else
workspaceSilent = false;
@@ -321,124 +349,143 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->updateWindowData();
// Verify window swallowing. Get the swallower before calling onWindowCreated(PWINDOW) because getSwallower() wouldn't get it after if PWINDOW gets auto grouped.
const auto SWALLOWER = PWINDOW->getSwallower();
PWINDOW->m_pSwallowed = SWALLOWER;
if (PWINDOW->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
PWINDOW->m_bCreatedOverFullscreen = true;
// size and move rules
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);
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));
const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(PWINDOW);
return stringToPercentage(VALUE, REL);
};
const auto SIZEX = SIZEXSTR == "max" ?
std::clamp(MAXSIZE.x, 20.0, PMONITOR->vecSize.x) :
(!SIZEXSTR.contains('%') ? std::stoi(SIZEXSTR) : std::stof(SIZEXSTR.substr(0, SIZEXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
const auto SIZEY = SIZEYSTR == "max" ?
std::clamp(MAXSIZE.y, 20.0, PMONITOR->vecSize.y) :
(!SIZEYSTR.contains('%') ? std::stoi(SIZEYSTR) : std::stof(SIZEYSTR.substr(0, SIZEYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
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);
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
PWINDOW->m_vRealSize = Vector2D(SIZEX, SIZEY);
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal();
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal());
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);
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 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 bool ONSCREEN = value.starts_with("onscreen");
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY});
const bool CURSOR = value.starts_with("cursor");
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);
if (CURSOR)
value = value.substr(value.find_first_of(' ') + 1);
const bool ONSCREEN = value.starts_with("onscreen");
const auto POSXSTR = value.substr(0, value.find(' '));
const auto POSYSTR = value.substr(value.find(' ') + 1);
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 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;
}
}
@@ -453,29 +500,27 @@ 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 auto SIZEX = SIZEXSTR == "max" ?
std::clamp(MAXSIZE.x, 20.0, PMONITOR->vecSize.x) :
(!SIZEXSTR.contains('%') ? std::stoi(SIZEXSTR) : std::stof(SIZEXSTR.substr(0, SIZEXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
const auto SIZEY = SIZEYSTR == "max" ?
std::clamp(MAXSIZE.y, 20.0, PMONITOR->vecSize.y) :
(!SIZEYSTR.contains('%') ? std::stoi(SIZEYSTR) : std::stof(SIZEYSTR.substr(0, SIZEYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) : stringToPercentage(SIZEXSTR, PMONITOR->vecSize.x);
setPseudo = true;
PWINDOW->m_vPseudoSize = Vector2D(SIZEX, SIZEY);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) : stringToPercentage(SIZEYSTR, PMONITOR->vecSize.y);
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
}
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
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)
@@ -502,7 +547,7 @@ 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 &&
@@ -524,7 +569,7 @@ 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();
@@ -532,7 +577,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
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())
@@ -552,20 +597,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->focusWindow(nullptr);
}
// verify swallowing
if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) {
const auto SWALLOWER = PWINDOW->getSwallower();
if (SWALLOWER) {
// swallow
PWINDOW->m_pSwallowed = SWALLOWER;
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER);
SWALLOWER->setHidden(true);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
}
// swallow
if (SWALLOWER) {
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER);
g_pHyprRenderer->damageWindow(SWALLOWER);
SWALLOWER->setHidden(true);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
}
PWINDOW->m_bFirstMap = false;
@@ -604,7 +641,8 @@ 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;
@@ -626,7 +664,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
return;
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
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();
@@ -647,7 +685,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
// swallowing
if (valid(PWINDOW->m_pSwallowed)) {
PWINDOW->m_pSwallowed->setHidden(false);
if (PWINDOW->m_sGroupData.pNextWindow.lock())
PWINDOW->m_pSwallowed->m_bGroupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false.
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed.lock());
PWINDOW->m_pSwallowed->m_bGroupSwallowed = false;
PWINDOW->m_pSwallowed.reset();
}
@@ -672,6 +716,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
g_pHyprRenderer->damageWindow(PWINDOW);
// do this after onWindowRemoved because otherwise it'll think the window is invalid
PWINDOW->m_bIsMapped = false;
@@ -692,7 +738,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();
@@ -711,8 +757,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pCompositor->addToFadingOutSafe(PWINDOW);
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID));
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
@@ -724,7 +768,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
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();
@@ -748,36 +793,33 @@ 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();
if (MAXSIZE > Vector2D{1, 1}) {
const auto REALSIZE = PWINDOW->m_vRealSize.goal();
Vector2D newSize = REALSIZE;
if (MAXSIZE.x < newSize.x)
newSize.x = MAXSIZE.x;
if (MAXSIZE.y < newSize.y)
newSize.y = MAXSIZE.y;
if (MINSIZE.x > newSize.x)
newSize.x = MINSIZE.x;
if (MINSIZE.y > newSize.y)
newSize.y = MINSIZE.y;
const Vector2D DELTA = REALSIZE - newSize;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goal() + DELTA / 2.0;
PWINDOW->m_vRealSize = newSize;
g_pXWaylandManager->setWindowSize(PWINDOW, newSize);
g_pHyprRenderer->damageWindow(PWINDOW);
}
PWINDOW->clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional<Vector2D>{MAXSIZE} : std::nullopt);
g_pHyprRenderer->damageWindow(PWINDOW);
}
if (!PWINDOW->m_pWorkspace->m_bVisible)
return;
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);
const auto PMONITOR = PWINDOW->m_pMonitor.lock();
if (PMONITOR)
PMONITOR->debugLastPresentation(g_pSeatManager->isPointerFrameCommit ? "listener_commitWindow skip" : "listener_commitWindow");
if (g_pSeatManager->isPointerFrameCommit) {
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,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
if (g_pSeatManager->isPointerFrameSkipped) {
g_pPointerManager->sendStoredMovement();
g_pSeatManager->sendPointerFrame();
g_pSeatManager->isPointerFrameCommit = true;
}
if (!PWINDOW->m_bIsX11) {
PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces();
@@ -785,7 +827,6 @@ void Events::listener_commitWindow(void* owner, void* data) {
}
// tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
@@ -900,7 +941,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_pXWaylandSurface->geometry.size());
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) {
if (const auto PMONITOR = PWINDOW->m_pMonitor.lock(); PMONITOR) {
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goal() / PMONITOR->scale);
}
}

View File

@@ -2,11 +2,11 @@
#include "../managers/AnimationManager.hpp"
#include "../config/ConfigManager.hpp"
CBaseAnimatedVariable::CBaseAnimatedVariable(ANIMATEDVARTYPE type) : m_Type(type) {
CBaseAnimatedVariable::CBaseAnimatedVariable(eAnimatedVarType type) : m_Type(type) {
; // dummy var
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWindow = pWindow;
@@ -14,7 +14,7 @@ void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWIN
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, eAVarDamagePolicy policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pLayer = pLayer;
@@ -22,7 +22,7 @@ void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLLS
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWorkspace = pWorkspace;
@@ -30,7 +30,7 @@ void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWOR
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, eAVarDamagePolicy policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;

View File

@@ -10,38 +10,39 @@
#include "../debug/Log.hpp"
#include "../desktop/DesktopTypes.hpp"
enum ANIMATEDVARTYPE {
enum eAnimatedVarType : int8_t {
AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT,
AVARTYPE_VECTOR,
AVARTYPE_COLOR
};
// Utility to bind a type with its corresponding ANIMATEDVARTYPE
// Utility to bind a type with its corresponding eAnimatedVarType
template <class T>
struct typeToANIMATEDVARTYPE_t {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_INVALID;
// NOLINTNEXTLINE(readability-identifier-naming)
struct STypeToAnimatedVarType_t {
static constexpr eAnimatedVarType value = AVARTYPE_INVALID;
};
template <>
struct typeToANIMATEDVARTYPE_t<float> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_FLOAT;
struct STypeToAnimatedVarType_t<float> {
static constexpr eAnimatedVarType value = AVARTYPE_FLOAT;
};
template <>
struct typeToANIMATEDVARTYPE_t<Vector2D> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_VECTOR;
struct STypeToAnimatedVarType_t<Vector2D> {
static constexpr eAnimatedVarType value = AVARTYPE_VECTOR;
};
template <>
struct typeToANIMATEDVARTYPE_t<CColor> {
static constexpr ANIMATEDVARTYPE value = AVARTYPE_COLOR;
struct STypeToAnimatedVarType_t<CHyprColor> {
static constexpr eAnimatedVarType value = AVARTYPE_COLOR;
};
template <class T>
inline constexpr ANIMATEDVARTYPE typeToANIMATEDVARTYPE = typeToANIMATEDVARTYPE_t<T>::value;
inline constexpr eAnimatedVarType typeToeAnimatedVarType = STypeToAnimatedVarType_t<T>::value;
enum AVARDAMAGEPOLICY {
enum eAVarDamagePolicy : int8_t {
AVARDAMAGE_NONE = -1,
AVARDAMAGE_ENTIRE = 0,
AVARDAMAGE_BORDER,
@@ -63,15 +64,15 @@ 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);
CBaseAnimatedVariable(eAnimatedVarType type);
void create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy);
void create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, eAVarDamagePolicy policy);
void create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy);
void create(SAnimationPropertyConfig* pAnimConfig, eAVarDamagePolicy policy);
CBaseAnimatedVariable(const CBaseAnimatedVariable&) = delete;
CBaseAnimatedVariable(CBaseAnimatedVariable&&) = delete;
@@ -103,7 +104,7 @@ class CBaseAnimatedVariable {
float getCurveValue();
// checks if an animation is in progress
inline bool isBeingAnimated() {
bool isBeingAnimated() const {
return m_bIsBeingAnimated;
}
@@ -111,7 +112,7 @@ class CBaseAnimatedVariable {
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_fEndCallback = std::move(func);
m_bRemoveEndAfterRan = remove;
if (!isBeingAnimated())
@@ -121,14 +122,14 @@ class CBaseAnimatedVariable {
/* 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_fBeginCallback = std::move(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;
m_fUpdateCallback = std::move(func);
}
/* resets all callbacks. Does not call any. */
@@ -157,8 +158,8 @@ class CBaseAnimatedVariable {
std::chrono::steady_clock::time_point animationBegin;
AVARDAMAGEPOLICY m_eDamagePolicy = AVARDAMAGE_NONE;
ANIMATEDVARTYPE m_Type;
eAVarDamagePolicy m_eDamagePolicy = AVARDAMAGE_NONE;
eAnimatedVarType m_Type;
bool m_bRemoveEndAfterRan = true;
bool m_bRemoveBeginAfterRan = true;
@@ -206,24 +207,26 @@ class CBaseAnimatedVariable {
template <Animable VarType>
class CAnimatedVariable : public CBaseAnimatedVariable {
public:
CAnimatedVariable() : CBaseAnimatedVariable(typeToANIMATEDVARTYPE<VarType>) {} // dummy var
CAnimatedVariable() : CBaseAnimatedVariable(typeToeAnimatedVarType<VarType>) {
;
} // dummy var
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy) {
create(pAnimConfig, pWindow, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, eAVarDamagePolicy policy) {
create(pAnimConfig, pLayer, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy) {
create(pAnimConfig, pWorkspace, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, eAVarDamagePolicy policy) {
create(pAnimConfig, policy);
m_Value = value;
m_Goal = value;

View File

@@ -6,24 +6,27 @@
#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);
// Avoid reallocations by reserving enough memory upfront
m_vPoints.resize(pVec->size() + 2);
m_vPoints[0] = Vector2D(0, 0); // Start point
size_t index = 1; // Start after the first element
for (const auto& vec : *pVec) {
if (index < m_vPoints.size() - 1) { // Bounds check to ensure safety
m_vPoints[index] = vec;
++index;
}
}
m_vPoints.back() = Vector2D(1, 1); // End point
m_dPoints.emplace_back(Vector2D(1, 1));
RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: {})", m_dPoints.size());
RASSERT(m_vPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: {})", m_vPoints.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));
float const t = (i + 1) / (float)BAKEDPOINTS;
m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t));
}
const auto ELAPSEDUS = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGIN).count() / 1000.f;
@@ -40,18 +43,26 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
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 const& t) const {
float t2 = t * t;
float t3 = t2 * t;
return 3 * t * (1 - t) * (1 - t) * m_vPoints[1].x + 3 * t2 * (1 - t) * m_vPoints[2].x + t3 * m_vPoints[3].x;
}
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);
float CBezierCurve::getYForT(float const& t) const {
float t2 = t * t;
float t3 = t2 * t;
return 3 * t * (1 - t) * (1 - t) * m_vPoints[1].y + 3 * t2 * (1 - t) * m_vPoints[2].y + t3 * m_vPoints[3].y;
}
// Todo: this probably can be done better and faster
float CBezierCurve::getYForPoint(float x) {
float CBezierCurve::getYForPoint(float const& x) const {
if (x >= 1.f)
return 1.f;
if (x <= 0.f)
return 0.f;
int index = 0;
bool below = true;

View File

@@ -1,6 +1,6 @@
#pragma once
#include <deque>
#include <vector>
#include <array>
#include <vector>
#include "math/Math.hpp"
@@ -16,13 +16,13 @@ class CBezierCurve {
// 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);
float getYForT(float const& t) const;
float getXForT(float const& t) const;
float getYForPoint(float const& x) const;
private:
// this INCLUDES the 0,0 and 1,1 points.
std::deque<Vector2D> m_dPoints;
std::vector<Vector2D> m_vPoints;
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,41 @@
#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};
}

View File

@@ -1,35 +1,47 @@
#pragma once
#include <cstdint>
#include <hyprgraphics/color/Color.hpp>
#include "../debug/Log.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;
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
};

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

@@ -6,6 +6,7 @@
#include <optional>
#include <cstring>
#include <cmath>
#include <filesystem>
#include <set>
#include <sys/utsname.h>
#include <sys/mman.h>
@@ -17,7 +18,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>
@@ -261,7 +264,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;
}
@@ -302,7 +305,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// Collect all the workspaces we can't jump to.
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID)) {
if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor)) {
// Can't jump to this workspace
invalidWSes.insert(ws->m_iID);
}
@@ -320,7 +323,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// Prepare all named workspaces in case when we need them
std::vector<WORKSPACEID> namedWSes;
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0)
if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor) || ws->m_iID >= 0)
continue;
namedWSes.push_back(ws->m_iID);
@@ -464,7 +467,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
std::vector<WORKSPACEID> validWSes;
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID && !onAllMonitors))
if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor && !onAllMonitors))
continue;
validWSes.push_back(ws->m_iID);
@@ -583,18 +586,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,9 +607,26 @@ 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");
#endif
@@ -677,44 +691,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) {
@@ -863,3 +924,37 @@ bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
*ro_fd_ptr = ro_fd;
return true;
}
float stringToPercentage(const std::string& VALUE, const float REL) {
if (VALUE.ends_with('%'))
return (std::stof(VALUE.substr(0, VALUE.length() - 1)) * REL) / 100.f;
else
return std::stof(VALUE);
}
bool executableExistsInPath(const std::string& exe) {
if (!getenv("PATH"))
return false;
static CVarList paths(getenv("PATH"), 0, ':', true);
for (auto& p : paths) {
std::string path = p + std::string{"/"} + exe;
std::error_code ec;
if (!std::filesystem::exists(path, ec) || ec)
continue;
if (!std::filesystem::is_regular_file(path, ec) || ec)
continue;
auto stat = std::filesystem::status(path, ec);
if (ec)
continue;
auto perms = stat.permissions();
return std::filesystem::perms::none != (perms & std::filesystem::perms::others_exec);
}
return false;
}

View File

@@ -6,6 +6,7 @@
#include "math/Math.hpp"
#include <vector>
#include <format>
#include <expected>
#include "../SharedDefs.hpp"
#include "../macros.hpp"
@@ -19,27 +20,29 @@ 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);
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);
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);
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);
bool executableExistsInPath(const std::string& exe);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View File

@@ -11,18 +11,21 @@
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Output.hpp"
#include "../protocols/Screencopy.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../managers/PointerManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp>
#include "debug/Log.hpp"
#include <hyprutils/string/String.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data);
g_pHyprRenderer->renderMonitor(((CMonitor*)data)->self.lock());
return 1;
}
@@ -36,6 +39,7 @@ CMonitor::~CMonitor() {
}
void CMonitor::onConnect(bool noRule) {
EMIT_HOOK_EVENT("preMonitorAdded", self.lock());
CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }};
if (output->supportsExplicit) {
@@ -43,10 +47,15 @@ void CMonitor::onConnect(bool noRule) {
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
}
listeners.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); });
listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); });
listeners.frame = output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); });
listeners.commit = output->events.commit.registerListener([this](std::any d) {
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
PROTO::screencopy->onOutputCommit(self.lock());
PROTO::toplevelExport->onOutputCommit(self.lock());
}
});
listeners.needsFrame =
output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });
output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });
listeners.presented = output->events.present.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IOutput::SPresentEvent>(d);
@@ -63,7 +72,7 @@ void CMonitor::onConnect(bool noRule) {
Debug::log(LOG, "Removing monitor {} from realMonitors", szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](SP<CMonitor>& el) { return el.get() == this; });
std::erase_if(g_pCompositor->m_vRealMonitors, [&](PHLMONITOR& el) { return el.get() == this; });
});
listeners.state = output->events.state.registerListener([this](std::any d) {
@@ -76,7 +85,7 @@ void CMonitor::onConnect(bool noRule) {
return;
Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName);
g_pHyprRenderer->applyMonitorRule(this, &activeMonitorRule, true);
g_pHyprRenderer->applyMonitorRule(self.lock(), &activeMonitorRule, true);
return;
}
@@ -90,7 +99,7 @@ void CMonitor::onConnect(bool noRule) {
SMonitorRule rule = activeMonitorRule;
rule.resolution = SIZE;
g_pHyprRenderer->applyMonitorRule(this, &rule);
g_pHyprRenderer->applyMonitorRule(self.lock(), &rule);
});
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
@@ -141,7 +150,7 @@ void CMonitor::onConnect(bool noRule) {
return;
}
SP<CMonitor>* thisWrapper = nullptr;
PHLMONITOR* thisWrapper = nullptr;
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
@@ -163,7 +172,7 @@ void CMonitor::onConnect(bool noRule) {
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
g_pHyprRenderer->applyMonitorRule(self.lock(), &monitorRule, true);
if (!state.commit())
Debug::log(WARN, "state.commit() failed in CMonitor::onCommit");
@@ -179,7 +188,7 @@ void CMonitor::onConnect(bool noRule) {
continue;
if (ws->m_szLastMonitor == szName || g_pCompositor->m_vMonitors.size() == 1 /* avoid lost workspaces on recover */) {
g_pCompositor->moveWorkspaceToMonitor(ws, this);
g_pCompositor->moveWorkspaceToMonitor(ws, self.lock());
ws->startAnim(true, true, true);
ws->m_szLastMonitor = "";
}
@@ -196,13 +205,13 @@ void CMonitor::onConnect(bool noRule) {
setMirror(activeMonitorRule.mirrorOf);
if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet
g_pCompositor->setActiveMonitor(this);
g_pCompositor->setActiveMonitor(self.lock());
g_pHyprRenderer->arrangeLayersForMonitor(ID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
// ensure VRR (will enable if necessary)
g_pConfigManager->ensureVRR(this);
g_pConfigManager->ensureVRR(self.lock());
// verify last mon valid
bool found = false;
@@ -214,27 +223,28 @@ void CMonitor::onConnect(bool noRule) {
}
if (!found)
g_pCompositor->setActiveMonitor(this);
g_pCompositor->setActiveMonitor(self.lock());
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR);
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR);
PROTO::gamma->applyGammaToState(this);
PROTO::gamma->applyGammaToState(self.lock());
events.connect.emit();
g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName});
g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)});
EMIT_HOOK_EVENT("monitorAdded", this);
EMIT_HOOK_EVENT("monitorAdded", self.lock());
}
void CMonitor::onDisconnect(bool destroy) {
EMIT_HOOK_EVENT("preMonitorRemoved", self.lock());
CScopeGuard x = {[this]() {
if (g_pCompositor->m_bIsShuttingDown)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
EMIT_HOOK_EVENT("monitorRemoved", self.lock());
g_pCompositor->arrangeMonitors();
}};
@@ -251,21 +261,21 @@ void CMonitor::onDisconnect(bool destroy) {
events.disconnect.emit();
// Cleanup everything. Move windows back, snap cursor, shit.
CMonitor* BACKUPMON = nullptr;
PHLMONITOR BACKUPMON = nullptr;
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
BACKUPMON = m;
break;
}
}
// remove mirror
if (pMirrorOf) {
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; }));
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == self; }));
// unlock software for mirrored monitor
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf);
pMirrorOf = nullptr;
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf.lock());
pMirrorOf.reset();
}
if (!mirrors.empty()) {
@@ -304,11 +314,10 @@ 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_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) {
if (w->m_pMonitor == self || !w->m_pMonitor)
wspToMove.push_back(w);
}
}
for (auto const& w : wspToMove) {
@@ -327,37 +336,38 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace.reset();
output->state->resetExplicitFences();
output->state->setAdaptiveSync(false);
output->state->setEnabled(false);
if (!state.commit())
Debug::log(WARN, "state.commit() failed in CMonitor::onDisconnect");
if (g_pCompositor->m_pLastMonitor.get() == this)
g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput);
if (g_pCompositor->m_pLastMonitor == self)
g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput.lock());
if (g_pHyprRenderer->m_pMostHzMonitor == this) {
int mostHz = 0;
CMonitor* pMonitorMostHz = nullptr;
if (g_pHyprRenderer->m_pMostHzMonitor == self) {
int mostHz = 0;
PHLMONITOR pMonitorMostHz = nullptr;
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m->refreshRate > mostHz && m.get() != this) {
pMonitorMostHz = m.get();
if (m->refreshRate > mostHz && m != self) {
pMonitorMostHz = m;
mostHz = m->refreshRate;
}
}
g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz;
}
std::erase_if(g_pCompositor->m_vMonitors, [&](SP<CMonitor>& el) { return el.get() == this; });
std::erase_if(g_pCompositor->m_vMonitors, [&](PHLMONITOR& el) { return el.get() == this; });
}
void CMonitor::addDamage(const pixman_region32_t* rg) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} else if (damage.damage(rg))
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
void CMonitor::addDamage(const CRegion* rg) {
@@ -366,13 +376,13 @@ void CMonitor::addDamage(const CRegion* rg) {
void CMonitor::addDamage(const CBox* box) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
if (damage.damage(*box))
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
@@ -384,7 +394,7 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) {
// damage whole screen because some previous cursor box damages were skipped
damage.damageEntire();
return false;
@@ -448,7 +458,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
if (PNEWWORKSPACE) {
// workspace exists, move it to the newly connected monitor
g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, this);
g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, self.lock());
activeWorkspace = PNEWWORKSPACE;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
PNEWWORKSPACE->startAnim(true, true, true);
@@ -456,7 +466,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
if (newDefaultWorkspaceName == "")
newDefaultWorkspaceName = std::to_string(wsID);
PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(CWorkspace::create(wsID, ID, newDefaultWorkspaceName));
PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(CWorkspace::create(wsID, self.lock(), newDefaultWorkspaceName));
}
activeWorkspace = PNEWWORKSPACE;
@@ -477,7 +487,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
return;
}
if (PMIRRORMON == this) {
if (PMIRRORMON == self) {
Debug::log(ERR, "Cannot mirror self!");
return;
}
@@ -486,13 +496,13 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
// disable mirroring
if (pMirrorOf) {
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; }));
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == self; }));
// unlock software for mirrored monitor
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf);
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf.lock());
}
pMirrorOf = nullptr;
pMirrorOf.reset();
// set rule
const auto RULE = g_pConfigManager->getMonitorRuleFor(self.lock());
@@ -501,7 +511,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
// push to mvmonitors
SP<CMonitor>* thisWrapper = nullptr;
PHLMONITOR* thisWrapper = nullptr;
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
@@ -520,22 +530,21 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
setupDefaultWS(RULE);
g_pHyprRenderer->applyMonitorRule(this, (SMonitorRule*)&RULE, true); // will apply the offset and stuff
g_pHyprRenderer->applyMonitorRule(self.lock(), (SMonitorRule*)&RULE, true); // will apply the offset and stuff
} else {
CMonitor* BACKUPMON = nullptr;
PHLMONITOR BACKUPMON = nullptr;
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
BACKUPMON = m;
break;
}
}
// move all the WS
std::deque<PHLWORKSPACE> wspToMove;
std::vector<PHLWORKSPACE> wspToMove;
for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
if (w->m_pMonitor == self || !w->m_pMonitor)
wspToMove.push_back(w);
}
}
for (auto const& w : wspToMove) {
@@ -549,14 +558,14 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
pMirrorOf = PMIRRORMON;
pMirrorOf->mirrors.push_back(this);
pMirrorOf->mirrors.push_back(self);
// remove from mvmonitors
std::erase_if(g_pCompositor->m_vMonitors, [&](const auto& other) { return other.get() == this; });
std::erase_if(g_pCompositor->m_vMonitors, [&](const auto& other) { return other == self; });
g_pCompositor->arrangeMonitors();
g_pCompositor->setActiveMonitor(g_pCompositor->m_vMonitors.front().get());
g_pCompositor->setActiveMonitor(g_pCompositor->m_vMonitors.front());
g_pCompositor->sanityCheckWorkspaces();
@@ -618,19 +627,19 @@ 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_iMonitorID == ID)) {
!(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);
@@ -646,11 +655,11 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
EMIT_HOOK_EVENT("workspace", pWorkspace);
}
g_pHyprRenderer->damageMonitor(this);
g_pHyprRenderer->damageMonitor(self.lock());
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pConfigManager->ensureVRR(self.lock());
g_pCompositor->updateSuspendedStates();
@@ -666,7 +675,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (activeSpecialWorkspace == pWorkspace)
return;
g_pHyprRenderer->damageMonitor(this);
g_pHyprRenderer->damageMonitor(self.lock());
if (!pWorkspace) {
// remove special if exists
@@ -679,7 +688,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) {
if (const auto PLAST = activeWorkspace->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
@@ -688,7 +697,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pCompositor->updateFullscreenFadeOnWorkspace(activeWorkspace);
g_pConfigManager->ensureVRR(this);
g_pConfigManager->ensureVRR(self.lock());
g_pCompositor->updateSuspendedStates();
@@ -702,7 +711,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
bool animate = true;
//close if open elsewhere
const auto PMONITORWORKSPACEOWNER = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
const auto PMONITORWORKSPACEOWNER = pWorkspace->m_pMonitor.lock();
if (PMONITORWORKSPACEOWNER->activeSpecialWorkspace == pWorkspace) {
PMONITORWORKSPACEOWNER->activeSpecialWorkspace.reset();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITORWORKSPACEOWNER->ID);
@@ -715,7 +724,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
}
// open special
pWorkspace->m_iMonitorID = ID;
pWorkspace->m_pMonitor = self;
activeSpecialWorkspace = pWorkspace;
activeSpecialWorkspace->m_bVisible = true;
if (animate)
@@ -723,7 +732,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == pWorkspace) {
w->m_iMonitorID = ID;
w->m_pMonitor = self;
w->updateSurfaceScaleTransformDetails();
w->setAnimationsToMove();
@@ -747,7 +756,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) {
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
@@ -756,11 +765,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(this);
g_pHyprRenderer->damageMonitor(self.lock());
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pConfigManager->ensureVRR(self.lock());
g_pCompositor->updateSuspendedStates();
}
@@ -813,6 +822,12 @@ void CMonitor::scheduleDone() {
});
}
void CMonitor::setCTM(const Mat3x3& ctm_) {
ctm = ctm_;
ctmUpdated = true;
g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME);
}
bool CMonitor::attemptDirectScanout() {
if (!mirrors.empty() || isMirror() || g_pHyprRenderer->m_bDirectScanoutBlocked)
return false; // do not DS if this monitor is being mirrored. Will break the functionality.
@@ -831,7 +846,7 @@ bool CMonitor::attemptDirectScanout() {
return false;
// we can't scanout shm buffers.
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
if (!PSURFACE->current.buffer || !PSURFACE->current.buffer->buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
return false;
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt", (uintptr_t)PSURFACE.get());
@@ -927,8 +942,76 @@ bool CMonitor::attemptDirectScanout() {
return true;
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
void CMonitor::debugLastPresentation(const std::string& message) {
Debug::log(TRACE, "{} (last presentation {} - {} fps)", message, lastPresentationTimer.getMillis(),
lastPresentationTimer.getMillis() > 0 ? 1000.0f / lastPresentationTimer.getMillis() : 0.0f);
}
void CMonitor::onMonitorFrame() {
if ((g_pCompositor->m_pAqBackend->hasSession() && !g_pCompositor->m_pAqBackend->session->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) {
return m->output != g_pCompositor->m_pUnsafeOutput->output;
})) {
// restore from unsafe state
g_pCompositor->leaveUnsafeState();
}
return; // cannot draw on session inactive (different tty)
}
if (!m_bEnabled)
return;
g_pHyprRenderer->recheckSolitaryForMonitor(self.lock());
tearingState.busy = false;
if (tearingState.activelyTearing && solitaryClient.lock() /* can be invalidated by a recheck */) {
if (!tearingState.frameScheduledWhileBusy)
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
tearingState.nextRenderTorn = true;
tearingState.frameScheduledWhileBusy = false;
}
static auto PENABLERAT = CConfigValue<Hyprlang::INT>("misc:render_ahead_of_time");
static auto PRATSAFE = CConfigValue<Hyprlang::INT>("misc:render_ahead_safezone");
lastPresentationTimer.reset();
if (*PENABLERAT && !tearingState.nextRenderTorn) {
if (!RATScheduled) {
// render
g_pHyprRenderer->renderMonitor(self.lock());
}
RATScheduled = false;
const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(self.lock());
if (max + *PRATSAFE > 1000.0 / refreshRate)
return;
const auto MSLEFT = 1000.0 / refreshRate - lastPresentationTimer.getMillis();
RATScheduled = true;
const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE);
const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME);
if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1)
g_pHyprRenderer->renderMonitor(self.lock());
else
wl_event_source_timer_update(renderTimer, TIMETOSLEEP);
} else
g_pHyprRenderer->renderMonitor(self.lock());
}
CMonitorState::CMonitorState(CMonitor* owner) : m_pOwner(owner) {
;
}
CMonitorState::~CMonitorState() {
@@ -958,7 +1041,7 @@ bool CMonitorState::commit() {
if (!updateSwapchain())
return false;
EMIT_HOOK_EVENT("preMonitorCommit", m_pOwner);
EMIT_HOOK_EVENT("preMonitorCommit", m_pOwner->self.lock());
ensureBufferPresent();

View File

@@ -1,7 +1,7 @@
#pragma once
#include "../defines.hpp"
#include <deque>
#include <vector>
#include "WLClasses.hpp"
#include <vector>
#include <array>
@@ -16,7 +16,7 @@
#include <aquamarine/allocator/Swapchain.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,
@@ -54,7 +54,7 @@ class CMonitorState {
private:
void ensureBufferPresent();
CMonitor* m_pOwner;
CMonitor* m_pOwner = nullptr;
};
class CMonitor {
@@ -125,11 +125,15 @@ class CMonitor {
SP<CSyncTimeline> outTimeline;
uint64_t commitSeq = 0;
WP<CMonitor> self;
PHLMONITORREF self;
// mirroring
CMonitor* pMirrorOf = nullptr;
std::vector<CMonitor*> mirrors;
PHLMONITORREF pMirrorOf;
std::vector<PHLMONITORREF> mirrors;
// ctm
Mat3x3 ctm = Mat3x3::identity();
bool ctmUpdated = false;
// for tearing
PHLWINDOWREF solitaryClient;
@@ -179,6 +183,10 @@ class CMonitor {
CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
void setCTM(const Mat3x3& ctm);
void debugLastPresentation(const std::string& message);
void onMonitorFrame();
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;

View File

@@ -4,59 +4,56 @@
#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);
}

View File

@@ -3,80 +3,135 @@
#include <vector>
#include <string>
inline const std::vector<std::string> SPLASHES = {
// clang-format off
"Woo, animations!",
"It's like Hypr, but better.",
"Release 1.0 when?",
"It's not awesome, it's Hyprland!",
"\"I commit too often, people can't catch up lmao\" - Vaxry",
"This text is random.",
"\"There are reasons to not use rust.\" - Boga",
"Read the wiki.",
"\"Hello everyone this is YOUR daily dose of read the wiki\" - Vaxry",
"h",
"\"why no work, bro I haven't hacked your pc to get live feeds yet\" - Vaxry",
"Compile, wait for 20 minutes, notice a new commit, compile again.",
"To rice, or not to rice, that is the question.",
"Now available on Fedora!",
"\"Hyprland is so good it starts with a capital letter\" - Hazel",
"\"please make this message a splash\" - eriedaberrie",
"\"the only wayland compositor powered by fried chicken\" - raf",
"\"This will never get into Hyprland\" - Flafy",
"\"Hyprland only gives you up on -git\" - fazzi",
"Segmentation fault (core dumped)",
"\"disabling hyprland logo is a war crime\" - vaxry",
"some basic startup code",
"\"I think I am addicted to hyprland\" - mathisbuilder",
"\"hyprland is the most important package in the arch repos\" - jacekpoz",
"Thanks Brodie!",
"Thanks fufexan!",
"Thanks raf!",
"You can't use --splash to change this message :)",
"Hyprland will overtake Gnome in popularity by [insert year]",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",
"Wir sind schon sehr lang zusammen...",
"I see a red door and I want it painted black.",
"Take on me, take me on...",
"You spin me right round baby right round",
"Stayin' alive, stayin' alive",
"Say no way, say no way ya, no way!",
"Ground control to Major Tom...",
"Alors on danse",
"And all that I can see, is just a yellow lemon tree.",
"Got a one-way ticket to the blues",
"Is this the real life, is this just fantasy",
"What's in your head, in your head?",
"We're all living in America, America, America.",
"I'm still standing, better than I ever did",
"Here comes the sun, bringing you love and shining on everyone",
"Two trailer park girls go round the outside",
"With the lights out, it's less dangerous",
"Here we go back, this is the moment, tonight is the night",
"Now you're just somebody that I used to know...",
"Black bird, black moon, black sky",
"Some legends are told, some turn to dust or to gold",
"Your brain gets smart, but your head gets dumb.",
"Save your mercy for someone who needs it more",
"You're gonna hear my voice when I shout it out loud",
"Ding ding pch n daa, bam-ba-ba-re-bam baram bom bom baba-bam-bam-bommm",
"Súbeme la radio que esta es mi canción",
"I'm beggin', beggin' you",
"Never gonna let you down (I am trying!)",
"\"I use Arch, btw\" - John Cena",
"\"Hyper\".replace(\"e\", \"\")",
"\"my win11 install runs hyprland that is true\" - raf",
"\"stop playing league loser\" - hyprBot",
"\"If it ain't broke, don't fix it\" - Lucascito_03",
"\"@vaxry how do i learn c++\" - flicko",
//
"Join the discord server!",
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo",
//
"2 years!"
// clang-format on
namespace NSplashes {
inline const std::vector<std::string> SPLASHES = {
// clang-format off
"Woo, animations!",
"It's like Hypr, but better.",
"Release 1.0 when?",
"It's not awesome, it's Hyprland!",
"\"I commit too often, people can't catch up lmao\" - Vaxry",
"This text is random.",
"\"There are reasons to not use rust.\" - Boga",
"Read the wiki.",
"\"Hello everyone this is YOUR daily dose of read the wiki\" - Vaxry",
"h",
"\"why no work, bro I haven't hacked your pc to get live feeds yet\" - Vaxry",
"Compile, wait for 20 minutes, notice a new commit, compile again.",
"To rice, or not to rice, that is the question.",
"Now available on Fedora!",
"\"Hyprland is so good it starts with a capital letter\" - Hazel",
"\"please make this message a splash\" - eriedaberrie",
"\"the only wayland compositor powered by fried chicken\" - raf",
"\"This will never get into Hyprland\" - Flafy",
"\"Hyprland only gives you up on -git\" - fazzi",
"Segmentation fault (core dumped)",
"\"disabling hyprland logo is a war crime\" - vaxry",
"some basic startup code",
"\"I think I am addicted to hyprland\" - mathisbuilder",
"\"hyprland is the most important package in the arch repos\" - jacekpoz",
"Thanks Brodie!",
"Thanks fufexan!",
"Thanks raf!",
"You can't use --splash to change this message :)",
"Hyprland will overtake Gnome in popularity by [insert year]",
"Designed in California - Assembled in China",
"\"something <time here> and still no new splash\" - snowman",
"My name is Land. Hypr Land. One red bull, shaken not stirred.",
"\"Glory To The Emperor\" - raf",
"Help I forgot to install kitty",
"Go to settings to activate Hyprland",
"Why is there code??? Make a damn .exe file and give it to me.",
"Hyprland is not a window manager!",
"Can we get a version without anime girls?",
"Check out quickshell!",
"A day without Hyprland is a day wasted",
"By dt, do you mean damage tracking or distrotube?",
"Made in Poland",
"\"I use Arch, btw\" - John Cena",
"\"Hyper\".replace(\"e\", \"\")",
"\"my win11 install runs hyprland that is true\" - raf",
"\"stop playing league loser\" - hyprBot",
"\"If it ain't broke, don't fix it\" - Lucascito_03",
"\"@vaxry how do i learn c++\" - flicko",
"Join the discord server!",
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo",
"2 years!",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",
"Wir sind schon sehr lang zusammen...",
"I see a red door and I want it painted black.",
"Take on me, take me on...",
"You spin me right round baby right round",
"Stayin' alive, stayin' alive",
"Say no way, say no way ya, no way!",
"Ground control to Major Tom...",
"Alors on danse",
"And all that I can see, is just a yellow lemon tree.",
"Got a one-way ticket to the blues",
"Is this the real life, is this just fantasy",
"What's in your head, in your head?",
"We're all living in America, America, America.",
"I'm still standing, better than I ever did",
"Here comes the sun, bringing you love and shining on everyone",
"Two trailer park girls go round the outside",
"With the lights out, it's less dangerous",
"Here we go back, this is the moment, tonight is the night",
"Now you're just somebody that I used to know...",
"Black bird, black moon, black sky",
"Some legends are told, some turn to dust or to gold",
"Your brain gets smart, but your head gets dumb.",
"Save your mercy for someone who needs it more",
"You're gonna hear my voice when I shout it out loud",
"Ding ding pch n daa, bam-ba-ba-re-bam baram bom bom baba-bam-bam-bommm",
"Súbeme la radio que esta es mi canción",
"I'm beggin', beggin' you",
"Never gonna let you down (I am trying!)",
"Hier kommt die Sonne",
"Kickstart my heart, give it a start",
"Fear of the dark, I have a constant fear that something's always near",
"Komm mit, reih dich ein.",
"I wish I had an angel for one moment of love",
"We're the children of the dark",
"You float like a feather, in a beautiful world",
"Demons come at night and they bring the end",
"All I wanna say is that they don't really care about us",
"Has he lost his mind? Can he see or is he blind?",
// clang-format on
};
inline const std::vector<std::string> SPLASHES_CHRISTMAS = {
// clang-format off
"Merry Christmas!",
"Merry Xmas!",
"Ho ho ho",
"Santa was here",
"Make sure to spend some jolly time with those near and dear to you!",
"Have you checked for christmas presents yet?",
// clang-format on
};
// ONLY valid near new years.
inline static int newYear = []() -> int {
auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
auto local = *localtime(&tt);
if (local.tm_mon < 8 /* decided with a fair die I promise. */)
return local.tm_year + 1900;
return local.tm_year + 1901;
}();
inline const std::vector<std::string> SPLASHES_NEWYEAR = {
// clang-format off
"Happy new Year!",
"[New year] will be the year of the Linux desktop!",
"[New year] will be the year of the Hyprland desktop!",
std::format("{} will be the year of the Linux desktop!", newYear),
std::format("{} will be the year of the Hyprland desktop!", newYear),
std::format("Let's make {} even better than {}!", newYear, newYear - 1),
// clang-format on
};
};

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