Compare commits

..

273 Commits

Author SHA1 Message Date
Vaxry
cba1ade848 props: bump version to 0.40.0 2024-05-04 16:42:32 +01:00
Vaxry
c77b60c910 keyboard: prevent UAF in destroy events 2024-05-04 16:37:26 +01:00
Vaxry
c951c4f8a1 keyboard: check for wlr() validity before accessing
fixes #5873
2024-05-04 16:10:32 +01:00
Vaxry
25964e5a2b hid: don't access expired resources in virtual devices
fixes #5868
2024-05-04 12:36:02 +01:00
JManch
2d40046f24 windows: set fullscreen border size to 0 (#5865) 2024-05-04 12:07:36 +01:00
thejch
40201a760a workspacerule: Fix monitor settings being deleted when merging ws rules (#5864)
* fix deleting monitor settings when merging rules

* use empty and workspace invalid
2024-05-04 02:18:04 +01:00
Vaxry
a3b4923c42 input: ignore destroyed devices in static events
fixes #5863
2024-05-04 00:48:25 +01:00
Vaxry
1237732b97 input: Introduce basic hyprland HID classes
Implements an intermediary HID class for mice, keyboards and touch devices, removing the old structs from WLClasses.hpp

Yes, virtual ones are duplicated a bit, but will likely be de-duped once wlr_input_device is not used anymore.
2024-05-03 22:40:27 +01:00
William Gray
1d2acbe193 config: add absolute monitor workspace selectors (#5848)
* add absolute monitor workspace selectors

* implement absolute for `r`

* format code
2024-05-03 18:38:00 +01:00
Tom Englund
1c73beaf9b inputmgr: dont double free on hotplug (#5855)
* inputmgr: dont double free on hotplug

since we are also unrefing the state on hotplugging the keyboard set the
state to nullptr so the destructor if case actually catches its been
already freed.

* keybindgmgr: dont double free on layout switching

d5bf153 added keymap unref at the end of updateXKBTranslationState to
not leak it when exiting, only it causes updateXKBTranslationState to
double free when changing layouts. since its already freed. remove the
unneeded extra xkb_keymap_unref.
2024-05-03 18:10:41 +01:00
Vaxry
8a2269272b output-management: move to new impl 2024-05-03 18:08:04 +01:00
Tom Englund
d5bf15387a internal: fix a few asan reported leaks on exit of hyprland (#5852)
* notifications: free cairo images on destruction

asan reports a leak on exit if we dont free the image we created in the
draw function. add a destructor and free images on exit.

* compositor: destroy wlroots types on exit

there are a few types not being destroyed on exit and causing a leak on
exit in wlroots reported by asan, add those.

* cursormgr: ensure we destroy cursor mgr on exit

add a destructor and call wlr_xcursor_manager_destroy on the manager on
destruction, leak reported by asan.

* keybindmgr: free state and keymap

add missing keymap_unref on creation, and add a destructor and free the
state on exit. leak reported by asan.

* skeyboard: add destructor and free state

free the state on destruction of keyboard, reported as leak by asan
2024-05-03 14:42:08 +01:00
Ikalco
387127b12a config: added option to choose the default monitor for the cursor (#5847)
* added option to choose the default monitor that the cursor will appear in upon startup

* fix: don't set cursor to default monitor after startup

* refactor to checkDefaultCursorWarp also fix focus
2024-05-03 02:39:19 +01:00
Vaxry
41cf94faaa format: fix clang-format 2024-05-03 02:04:08 +01:00
Gabriel Ford
0623cfabc9 windowrules: Fix Floating Grouped Windows Crash and Bug. (#5826)
* Don't unhide grouped items.

* Remove head check as that wasn't always correct.

* Replace lock with expired()

* Remove set hidden from max size.
2024-05-03 02:00:58 +01:00
Vaxry
2755297670 virtual-pointer: move to new impl 2024-05-03 01:52:05 +01:00
Vaxry
7d49819b5e virtual-keyboard: destroy on vdestroy event 2024-05-03 00:54:32 +01:00
Vaxry
6aa2d123ae virtual-keyboard: move to new impl 2024-05-03 00:31:48 +01:00
Vaxry
a3ca016d42 cursor-shape: use - instead of _ in shapes
ref #5824
2024-05-02 16:21:33 +01:00
Vaxry
eeb78ef965 fractional: set scale before configure
fixes #5842
2024-05-02 15:13:47 +01:00
Vaxry
8427824719 cursormgr: attempt using - instead of _ for failed cursors before fallback
web uses -, and thus some themes might use - too. Attempt replacing _ with - before assuming a shape is missing.
2024-05-02 14:47:10 +01:00
thejch
d0229d6e1e layout: limit updates when mouse animate drag is enabled (#5838) 2024-05-02 14:28:51 +01:00
Vaxry
56de72f357 internal: store matched windowrules and emit event
new event for plugins, windowUpdateRules
2024-05-02 02:18:01 +01:00
Mihai Fufezan
02bfb2857e flake.lock: update 2024-05-01 22:00:26 +03:00
Vaxry
0237e39f74 protocols: utilize hyprwayland-scanner 0.3.3 functions
stuff like ::version(), ::client(), ::error() etc
2024-05-01 19:40:35 +01:00
Vaxry
47b087950d hyprctl: fix instances path
fixes #5831
2024-05-01 16:47:38 +01:00
Vaxry
8bcccf9f0f ime-v2: move to new impl 2024-05-01 16:41:17 +01:00
Vaxry
4ed6b69b68 socket2: fix empty activewindowv2 events
fixes #5827
2024-05-01 13:57:32 +01:00
zakk4223
d2899a6c27 layout: Trigger layout recalcuation on deco position/size change (#5821)
* Trigger layout recalcuation on deco position/size change

* Remove now unneeded code

* Formatting
2024-05-01 12:59:40 +01:00
Vaxry
ed58cc4c31 sessionLock: remove m_pLastFocus on destroy of surface
fixes #5822
2024-05-01 02:33:36 +01:00
Vaxry
3d09c6d526 makefile: fix old headers only being copied 2024-05-01 00:32:42 +01:00
Vaxry
70ebc3add3 sessionLock: call wlr_surface_map/_unmap on the surface manually
fixes unmapped subsurfaces etc

ref #5816
2024-04-30 21:59:07 +01:00
Vaxry
a09103cd38 sessionLock: set locked after emitting event
ref #5816
2024-04-30 21:20:06 +01:00
Vaxry
dbb8b294d6 sessionLock: fix incorrect protocol error raised
fixes #5816
2024-04-30 20:02:30 +01:00
Vaxry
1f6657f037 keybinds: check for special workspace equality before switching
fixes #5814
2024-04-30 16:42:31 +01:00
Vaxry
90e1411315 session-lock: move to new impl 2024-04-30 16:34:09 +01:00
Vaxry
d7a48cf478 hyprctl: use XDG_RUNTIME_DIR if available
fixes #5813
2024-04-30 14:17:35 +01:00
Jan Beich
07e070012b CrashReporter: unbreak build on FreeBSD (#5786)
* CrashReporter: skip Linux field on BSDs after 90a53aed59

In file included from src/debug/CrashReporter.cpp:10:
src/debug/signal-safe.hpp:113:17: error: no member named 'sa_restorer' in 'sigaction'
            act.sa_restorer = NULL;
            ~~~ ^

* CrashReporter: ensure *argv[] is NULL-terminated after 90a53aed59

execv() may fail with EFAULT otherwise.

* hyprpm: add missing header after 335015fe2d

hyprpm/src/core/PluginManager.cpp:165:43: error: use of undeclared identifier 'getuid'
  165 |     const std::string USERNAME = getpwuid(getuid())->pw_name;
      |                                           ^
hyprpm/src/core/PluginManager.cpp:431:45: error: use of undeclared identifier 'getuid'
  431 |     const std::string USERNAME   = getpwuid(getuid())->pw_name;
      |                                             ^
hyprpm/src/core/PluginManager.cpp:558:43: error: use of undeclared identifier 'getuid'
  558 |     const std::string USERNAME = getpwuid(getuid())->pw_name;
      |                                           ^
2024-04-30 14:14:31 +01:00
Sungyoon Cho
801437cd54 hyprctl: add missing json string escapes (#5811) 2024-04-30 14:13:36 +01:00
Vaxry
62ae2b3f40 pluginAPI/hooks: Remove dependency on cc from the hooksystem (#5801)
* Remove dependency on cc from the hooksystem

* Nix: remove cc from wrapper

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-04-30 02:54:43 +01:00
Vaxry
5edc32930d layerSurface: refactor/move to a memory-safe impl
Makes all the pointers smart to avoid memory issues
Refactors layerSurface code to live inside desktop/layersurface
2024-04-30 02:41:27 +01:00
Vaxry
5e6f7b1cdb cursor-shape: allow duplicate devices per pointer resource
fixes #5798
2024-04-30 00:05:29 +01:00
Vaxry
f2b03e9679 ext-idle-notify: move to new impl 2024-04-29 17:50:07 +01:00
Vaxry
86133983a9 xdg-activation: move to new impl 2024-04-29 16:43:24 +01:00
Vaxry
39595aaca3 hyprctl: make commits wrapped in quotes in json 2024-04-29 16:10:26 +01:00
thejch
a783cd8f40 log: Add some colors to stdout log 🔴🟡🟢🔵🟣 (#5778)
* add colored log

* add config option

* make it dynamic
2024-04-29 16:07:35 +01:00
Vaxry
33e0bb1478 wlr-output-power: move to new impl 2024-04-29 01:38:24 +01:00
Vaxry
a5a6480917 core: Move /tmp/hypr to $XDG_RUNTIME_DIR/hypr (#5788)
Moves the directory containing sockets and logs.
Also restructures lockfiles a bit.

For consumers, check if `$XDG_RUNTIME_DIR/hypr` exists. If so, use it. If not, use the old `/tmp/hypr`.
2024-04-28 22:25:24 +01:00
Vaxry
d20ee31210 hyprpm: fix updating headers
oopsie daisy
2024-04-28 21:32:22 +01:00
Vaxry
95a5e75c26 hooksystem: check for existing random outdir 2024-04-28 21:20:10 +01:00
Vaxry
335015fe2d hyprpm: fix for multi-user + improve directory structure 2024-04-28 20:27:44 +01:00
Vaxry
f7815dab42 compositor: more fs safety around tmp directories
HIS now includes a random bit, and hyprland will bail if /tmp/hypr is not a directory or if /tmp/hypr/his exists
2024-04-28 20:06:40 +01:00
Vaxry
b164e67d8b core: prefer mkdir over create_directory and permissions 2024-04-28 18:58:31 +01:00
Vaxry
28c8561924 hooksystem: use a random 700 directory for assembler 2024-04-28 18:28:19 +01:00
Vaxry
82a6fba6ec tokenmgr: separate getRandomUUID from registerNewToken 2024-04-28 18:17:48 +01:00
Vaxry
2e763764bf pointer-constraints: don't warp on entering a confined pointer
ref #5774
2024-04-27 21:15:33 +01:00
virchau13
90a53aed59 CrashReporter: fix deadlocks by making it mostly async-signal-safe (#5771)
`CrashReporter::createAndSaveCrash()` is not async-signal-safe,
resulting in random deadlocks/double-crashes during Hyprland crashes.
This changes the function to be (mostly) async-signal-safe.
2024-04-27 17:38:48 +01:00
niki-on-github
55490637aa windowrules: add subtract window option to move rule (#5770)
Co-authored-by: nix <nix@local>
2024-04-27 17:34:19 +01:00
Vaxry
e400a288d0 pointer-constraints: remove emoji to fix clang
fixes #5765
2024-04-27 13:27:24 +01:00
Vaxry
bca7804bb6 internal: Window storage rework - part 1 (#5762)
* Window storage rework - part 1

* format

* remove useless include

* fix pch

* format

* fix crash in dwindle

* fix vram leak

* prefer .expired() for bool checks
2024-04-27 12:43:12 +01:00
Vaxry
25aec3ac8c pointer-constraints: move to new impl 2024-04-27 03:17:04 +01:00
Vaxry
f94264928a swipe: fix crashes with invalid distance
fixes #5758
2024-04-26 19:11:28 +01:00
Vaxry
d9ec2785cb wlr-foreign-toplevel: send done after property changes
fixes #5753
2024-04-26 13:14:50 +01:00
Vaxry
1d40af64d3 text-input-v3: move to new impl 2024-04-25 23:27:44 +01:00
eriedaberrie
e87227e00a config: Default unconfigured monitors to open to the right (#5741)
* config: default unconfigured monitors to open to the right

* monitor: improve logging for auto positioning
2024-04-25 22:07:50 +01:00
eriedaberrie
faa9017043 renderer: fix rounding of the size of fractionally scaled monitors (#5748) 2024-04-25 18:52:49 +01:00
Vaxry
0652a20bd3 events: set window initial* before searching for rules 2024-04-25 16:57:11 +01:00
Vaxry
9fc3cb5629 foreign-toplevel: fix bad_any_cast in moveWindow
fixes #5740
2024-04-25 16:35:09 +01:00
Vaxry
01df3b73d8 shortcuts-inhibitor: move to new impl 2024-04-25 14:32:35 +01:00
Vaxry
ecf282d331 wlr-foreign-toplevel: move to new impl 2024-04-25 01:10:43 +01:00
zakk4223
72e31d3335 idle-inhibit: Always recheck idle inhibitors on creation and deletion (#5738)
Formatting
2024-04-25 00:05:19 +01:00
Vaxry
3878f806ff pointer-gestures: move to new impl 2024-04-24 21:36:56 +01:00
Vaxry
d86eec332f idle-inhibit: don't destroy inhibitor on surface destroy
fixes #5731
2024-04-24 19:15:01 +01:00
Vaxry
4540d8ccd5 style: fix clang-format 2024-04-24 16:45:54 +01:00
Vaxry
d27b5985c1 scripts: fix asan patch 2024-04-24 16:44:15 +01:00
Vaxry
932a0cd777 cmake: require hyprwayland-scanner 0.3.0 2024-04-24 16:39:18 +01:00
Virt
9fe409800b renderer: Fix mirrored displays when transformed and preserve aspect ratio (#5697)
* renderer: transform mirror buffer and preserve mirror aspect ratio

* renderer: render mirrors directly from offloadFB

* renderer: fix formatting

* renderer: use monitorMirrorFB again, but properly damage mirrors

* renderer: clean mirrors after reload and support cursor zoom mirroring
2024-04-24 16:29:41 +01:00
Vaxry
8aecd4f253 cursormgr: fix misscaled cursors on fractional 2024-04-24 16:18:38 +01:00
Vaxry
608eff600d tokens: add more modes to initial_workspace_tracking
1 is single-shot, 2 is persistent

fixes #5732
2024-04-24 16:16:52 +01:00
Agent00Ming
81bb4eb2f6 workspace: Fix duplication of "special:" in special workspace name (#5729)
* Fix duplication of "special:" in special workspace name
	modified:   src/desktop/Workspace.cpp

* Track default special workspace name as special:special
This is to fix the edge cases with the previous commit without breaking
user configs.

	modified:   src/helpers/MiscFunctions.cpp

---------

Co-authored-by: Agent_00Ming <agent00ming9366@gmail.com>
2024-04-24 16:07:22 +01:00
Vaxry
e5fa0007a5 foreign: fix no-pch builds 2024-04-24 16:03:56 +01:00
Vaxry
29b0529542 nix: bump inputs 2024-04-24 15:50:43 +01:00
Vaxry
0d1bb65c75 ext-foreign-toplevel: add implementation 2024-04-24 15:48:06 +01:00
Vaxry
34413d1f36 tokens: fix initial workspace token ignoring special
ref #5726
2024-04-24 02:06:13 +01:00
drendog
31d055f6d4 input: fix active keyboard for seat after destroying one (#5725)
* fix: manage active keyboard for seat after destroying one

* chore: clang-format
2024-04-23 23:30:35 +01:00
Vaxry
d119513749 renderer: fix safety around sendFrameEventsToWorkspace
ref #5718
2024-04-23 21:15:37 +01:00
Vaxry
bb4646bbdf compositor: properly update workspace in moveWindowToWorkspaceSafe
fixes #5714
2024-04-23 16:38:12 +01:00
Vaxry
a2366f78f0 renderer: send frame events to apps on empty damage renders
fixes #5711
2024-04-23 16:08:54 +01:00
Vaxry
cf3596a96d renderer: avoid rendering frame if finalDamage is empty 2024-04-23 12:29:01 +01:00
Vaxry
4f1214c7e9 windows: don't force workspace change on same tracked workspace 2024-04-23 11:37:20 +01:00
thejch
aab1df50ab CI: add no PCH build (#5708) 2024-04-23 11:02:51 +03:00
Mihai Fufezan
5fdd0bceac Meson & Nix: add libuuid dep 2024-04-23 07:01:20 +03:00
Vaxry
5262292abc cmake: add uuid to deps 2024-04-23 02:22:30 +01:00
Gabriel Ford
c3ec16f494 config: Add More Monitor 'Auto' Positions. (#5670)
* Reverse Window Positioning.

* Cleanup old comments and logs.

* Finish Splitting Left and Right offset.

* Forgot to add Auto Left to ConfigManager

* Fix problems with auto_left.

* Nearly finish up and down.

* Finish draft of all four dirs. Testing now.

* Change Y value in moveTo for up and down.

* Format, comment, and cleanup.

* Address Vaxry's feedback.

* Add check to see if auto position is first rule.

* Run clang-format.
2024-04-23 01:49:25 +01:00
Vaxry
29308b94ca windows: add misc:initial_workspace_tracking
By default enabled, will track the initial opened workspace of a window spawned for 2 minutes or until it's moved to a different workspace.

For example: you run a launcher and open an app on workspace 1, but quickly switch to workspace 2. The app will now open on workspace 1 regardless of your switch.
2024-04-23 01:49:23 +01:00
Vaxry
7778f01194 managers: Add a TokenManager 2024-04-23 01:28:27 +01:00
fred21O4
da839f20f1 CI/Nix: use hyprland cachix (#5701) 2024-04-22 23:58:27 +03:00
PostCyberPunk
855a516596 core: add libinput backend support for headless session (#5699) 2024-04-22 18:48:18 +01:00
Vaxry
012a2802e0 Protocols: implement protoLog 2024-04-22 18:44:25 +01:00
Vaxry
741c75d907 gamma-control: move to new impl 2024-04-22 18:21:03 +01:00
Vaxry
dafc9ed4eb pluginsystem: fix unhooking on exit 2024-04-22 15:57:03 +01:00
Vaxry
e91513a5e8 pluginapi: unregister callbacks on lost ptrs 2024-04-22 15:50:23 +01:00
Vaxry
450343b7b8 pluginsystem: unload entire plugin before calling dlclose()
fixes #5689
2024-04-22 15:46:43 +01:00
Matteo Quadrino
e1644e91ea config: polish default config file (#5672)
* polish default config file

polish default config, making it prettier by separating it in sections, and adding links to the wiki for each section.
No configuration was altered, I only made it prettier and more beginner friendly.
I propose this should also be used as the autogenerated config file at installation.

* update default config according to suggestions

* Update defaultConfig.hpp to match example

* remove some whitespace

* match default config

* restored string terminator
2024-04-22 10:31:29 +01:00
Mihai Fufezan
cbed4fa5ec flake.lock: update 2024-04-22 12:20:46 +03:00
Mihai Fufezan
a4f38a07d7 Nix: override wayland-protocols until merged 2024-04-22 00:38:28 +03:00
vaxerski
019d4900cb [gha] Nix: update wlroots 2024-04-21 20:34:55 +00:00
Vaxry
448e3208ca deps: update wlroots 2024-04-21 21:34:04 +01:00
Vaxry
8afdb8403b style: fix clang-format 2024-04-21 21:29:37 +01:00
Tom Englund
f041d763ae relative-pointer: fix missing header for g_pCompositor (#5681) 2024-04-21 21:24:07 +01:00
Vaxry
f587c3e0ba alpha-modifier: add support for protocol 2024-04-21 21:21:22 +01:00
Vaxry
87173bd09d protocols: fix for hyprwayland-scanner update 2024-04-21 21:20:48 +01:00
Vaxry
ed69502ff6 xdg-decoration: move to new impl 2024-04-21 20:04:58 +01:00
Vaxry
4954dcbbb3 relative-pointer: move to new impl 2024-04-21 19:30:23 +01:00
Vaxry
55f1f3fedf protocols: minor style improvements to new impls 2024-04-21 19:29:45 +01:00
Vaxry
75c87bde3c ci: Fix CI (#5679) 2024-04-21 18:33:03 +01:00
Vaxry
d9fe1d0f58 idle-inhibit: move to new impl 2024-04-21 16:54:52 +01:00
Vaxry
e823b5d693 Window: add destroy signal 2024-04-21 16:54:50 +01:00
SoSeDiK
e69bc5b870 config: Expand on window matching (#5518)
* Expand on window matching

* Requested changes
2024-04-21 15:19:59 +01:00
thejch
f47c89d495 git: ignore wlroots dirty (#5674) 2024-04-21 15:18:43 +01:00
Vaxry
93e5d7ca5a input: check for focused_client being null before reading its client
ref #5673
2024-04-21 15:17:12 +01:00
André Silva
1ce21fdb3e nix: fix missing git in wlroots build 2024-04-21 15:54:23 +03:00
Vaxry
4dc07c4378 keybinds: clear repeat source on mouse inputs
fixes #5671
2024-04-21 12:50:37 +01:00
vaxerski
8ca28dd510 [gha] Nix: update wlroots 2024-04-21 11:40:48 +00:00
Vaxry
9ce9bd9b0f deps: bump wlroots 2024-04-21 12:39:50 +01:00
thejch
7c3bd4c19f workspace: Add fullscreen workspace selector (#5640)
* add fullscreen selector

* use stoi
2024-04-21 01:50:08 +01:00
Vaxry
30e4b404f2 cursor-shape: move to new impl 2024-04-21 01:49:23 +01:00
Vaxry
a141bbbea5 helpers: Add new C++ Signal and Listener classes
A memory-safe alternative to wl_signal
2024-04-21 01:47:45 +01:00
fufexan
a10a6fff55 [gha] Nix: update inputs 2024-04-21 00:03:53 +00:00
Vaxry
4ad739ec63 HookSystem: improve callback safety 2024-04-20 20:16:42 +01:00
Vaxry
1055e6bee6 wayland-protocol: remove unused CWaylandResource 2024-04-20 19:40:01 +01:00
Vaxry
84ee839ca6 XDG-Output: move to hyprwayland-scanner 2024-04-20 19:39:59 +01:00
Yaroslav
a945346064 core: remove libsystemd dependency (#5660)
* remove libsystemd dependency

as per Lennart Poettering's advice:
https://github.com/systemd/systemd/issues/32028#issuecomment-2031366922

* fix naming for systemd helper functions

* rename vars according to code style

* Nix: update meson patch

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-04-20 18:50:07 +01:00
Vaxry
ea47e8c92a Fractional-scale: move to new impl 2024-04-20 14:19:16 +01:00
Vaxry
ea95449402 core: Move tearing to hyprwayland-scanner (#5657)
Adds a new dependency: hyprwayland-scanner https://github.com/hyprwm/hyprwayland-scanner

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2024-04-20 13:25:29 +01:00
Gabriel Ford
5c97b96278 config: Allow more sensible input options for enabling animations. (#5659)
* Add check for on/off and true/false.

* Cleanup feature and comment it out.

* Use already created helper function for this.

* Fix comparing int to char* ptr
2024-04-20 12:26:48 +01:00
thejch
10caa03ce5 config: merge all rules set for the same workspace selection (#5656) 2024-04-20 02:20:16 +01:00
Vaxry
1ebc32f5f4 tearing-control: search through all valid windows
ref #5655
2024-04-20 00:08:49 +01:00
Vaxry
b52a49b4c4 tearing-control: hyprland impl (#5655)
* tearing: hl impl

* format
2024-04-19 22:16:35 +01:00
Yixun Lan
1016faea53 misc: fix autocompletions for meson (hyprctl/hyprpm)
Signed-off-by: Julien Roy <julien@jroy.ca>
2024-04-19 21:09:10 +03:00
Sungyoon Cho
dd39cd7e42 windows: recalculate monitor after updating windows (#5647)
Recalculate monitor after updating windows, so layout render doesn't get
overridden.
2024-04-19 18:46:16 +01:00
zakk4223
4d0a635237 workspace: Add 'v' flag for workspace selector that counts only visible windows (#5628)
* Add 'v' flag for workspace selector: counts only visible windows

* extra commit because I'm dumb

* guard
2024-04-19 02:44:51 +01:00
Epikastema
82222342f1 shaders: Use sin-less hash for noise (#5607) 2024-04-17 19:01:50 +01:00
Thomas Lindae
78b04c3a76 hyprctl: fix activewindow request not showing workspace name (#5623) 2024-04-17 17:44:46 +01:00
Maarten de Vries
e57a2d7ec8 keybindmgr: add optional silent suffix to movewindow. (#5597)
With the `silent` suffix, the focus remains on the current position in
the layout or the current monitor, instead of following the moved
window. When combined with `movewindow mon:X`, this this allows you to
get the same behavior as xmonad's `windowToScreen` command.
2024-04-17 12:04:16 +01:00
Vaxry
e8e02e81e8 README: minor cleanup 2024-04-16 20:36:21 +01:00
Vaxry
fe7b748eb6 props: bump version to 0.39.1 2024-04-16 17:01:03 +01:00
Vaxry
eeca50e3dc hyprpm: err out on missing runtime deps 2024-04-16 16:59:06 +01:00
vaxerski
9a66514e26 hyprpm: shallow since a week before commit date
timezones, etc.

ref #5612
2024-04-16 15:41:11 +01:00
vaxerski
32555e98dd window: remove input ref on unmap
ref #5605
2024-04-16 15:17:54 +01:00
Mihai Fufezan
79a139c949 flake.lock: update 2024-04-15 23:57:27 +03:00
FUFSoB
c99803af15 notifications: fix notifications on manually rotated monitor (#5599) 2024-04-15 21:47:39 +01:00
Vaxry
02cbf049d2 hyprpm: checkout branch and rev separately
sometimes the branch is garbled by incorrect packaging
2024-04-15 19:16:25 +01:00
Vaxry
ccbdce7c85 input: send an empty relative event after constraint motion events
ref #4015
2024-04-15 17:22:25 +01:00
Vaxry
3dbf8e936e cursor: add hyprcursor loggers 2024-04-15 16:45:08 +01:00
Vaxry
d1c2d524a0 misc: fix autocompletions for meson (hyprctl/hyprpm)
Signed-off-by: Florian sp1rit <sp1rit@disroot.org>
2024-04-15 16:02:22 +01:00
Jan Beich
2ea367839b build: Unbreak build on FreeBSD by adjusting dependencies (#5595)
* deps: add epoll-shim for some BSDs after 863c7b6072

ld: error: undefined symbol: timerfd_create
>>> referenced by EventLoopManager.cpp
>>>               src/Hyprland.p/managers_eventLoop_EventLoopManager.cpp.o:(CEventLoopManager::CEventLoopManager())

ld: error: undefined symbol: timerfd_settime
>>> referenced by EventLoopManager.cpp
>>>               src/Hyprland.p/managers_eventLoop_EventLoopManager.cpp.o:(CEventLoopManager::nudgeTimers())

See also
https://github.com/freebsd/freebsd-src/commit/af93fea71038
https://github.com/netbsd/src/commit/75f1bc6655cf

* deps: drop unused xcb-image after 45945a3e7d

$ pkg install <hyprland dependencies>
$ pkg install meson jq `pkg rquery %dn wlroots` hwdata
$ gmake all
[...]
-- Checking for modules 'xcb;xwayland;xcb-util;xcb-render;xcb-image;xcb-xfixes;xcb-icccm;xcb-composite;xcb-res;xcb-ewmh'
--   Package 'xcb-image' not found
CMake Error at /usr/local/share/cmake/Modules/FindPkgConfig.cmake:619 (message):
  The following required packages were not found:

   - xcb-image

Call Stack (most recent call first):
  /usr/local/share/cmake/Modules/FindPkgConfig.cmake:841 (_pkg_check_modules_internal)
  CMakeLists.txt:177 (pkg_check_modules)

See also
https://github.com/swaywm/wlroots/commit/ae7c3f3d1c56
2024-04-15 14:42:17 +01:00
Vaxry
1719905e7f CI: unshallow on checkout before sourcing the tarball 2024-04-15 02:05:45 +01:00
Vaxry
ce4c36392d hyprpm: minor fixes to hyprpm for shallow and versioned clones 2024-04-15 01:57:10 +01:00
vaxerski
67f47fbdcc [gha] Nix: update wlroots 2024-04-14 20:33:15 +00:00
thejch
043a40cd7a deps: update wlroots (#5592) 2024-04-14 21:32:19 +01:00
LivingCodeX
fd7ea4f27c constraint: Fix xwl cursor locking for scaled monitors (#5587)
* Fix xwl cursor locking for scaled monitors

* Add null check for window

* Replace m_fLastScale with m_fX11SurfaceScaledBy

* Improve code style

* Improve code style via clang-format
2024-04-14 21:31:50 +01:00
Vaxry
e93fbd7c4f props: bump ver to 0.39.0 2024-04-14 19:48:28 +01:00
go0d1uck
83ab0f2d66 keybindmgr: fix workspace_back_and_forth (#5585) 2024-04-14 14:54:00 +01:00
MightyPlaza
0634aaeac6 renderer: remove border on fullscreen (#5577)
modified:   src/render/Renderer.cpp
2024-04-14 00:16:26 +01:00
André Silva
61fe47189b build: update asan patch (#5562) 2024-04-13 22:13:08 +01:00
Yaroslav
9e4b2efe7e cmake: Some small cmake cleanups (#5572)
* remove unnecessary include

* cmake: use pkg_get_variable

We can find wayland-scanner executable and wayland-protocols dir by
taking advantage of this function, so no need to use find_program or
manually call pkgconf executable.

* cmake: remove explicit rdynamic option

CMAKE_ENABLE_EXPORTS=ON already implies rdynamic so it's redundant to
set the latter explicitly.

Also, CMAKE_ENABLE_EXPORTS is superseded by
CMAKE_EXECUTABLE_ENABLE_EXPORTS in cmake 3.27.

* cmake: make xcb-errors dep optional

xcb-errors is being used in wlroots, where it's optional. So make it
optional in hyprland as well
2024-04-13 14:40:28 +01:00
thejch
d96501442f core: Fix double special workspace (#5574)
* fix double special name

* fix special on another monitor

* remove extra stuff
2024-04-13 14:39:20 +01:00
thejch
582d6233c8 workspace: fix workspace name selector returning true early (#5571) 2024-04-13 01:55:17 +01:00
thejch
34396f55a2 master: change the mfact dispatcher to use splitratio (#4766)
* master layout: change the mfact dispatcher to use splitratio

* add space for concat
2024-04-13 01:54:18 +01:00
Vaxry
0c513ba91b CI: fix packaging 2024-04-12 20:46:21 +01:00
Vaxry
dd6fdf49d9 window: always unref workspace on unmap
fixes #5563
2024-04-12 19:52:01 +01:00
Vaxry
ddcdb56f2c CI: fix arch 2024-04-12 19:50:36 +01:00
bvr-yr
32147f5e91 hyprpm: fix wlroots path (#5567) 2024-04-12 19:49:33 +01:00
Mihai Fufezan
d8d0d3b20b Nix & Meson: switch to wlroots-hyprland 2024-04-12 20:39:00 +03:00
Vaxry
382b6d3f6b makefile: move wlr headers dir 2024-04-12 18:07:04 +01:00
Vaxry
0a70ccd099 Makefile: remove refs to libwlroots 2024-04-12 17:58:18 +01:00
vaxerski
e1e11f5a87 [gha] Nix: update wlroots 2024-04-12 15:32:45 +00:00
Vaxry
45945a3e7d deps: move from wlroots to wlroots-hyprland 2024-04-12 16:31:50 +01:00
Vaxry
b1a9430289 inhibitor: always destroy on window unmap
ref #5555
2024-04-12 00:18:58 +01:00
Jan-Peter Dhallé
e0a7cf5c30 master: fix full height when all windows master (#5549) 2024-04-12 00:05:30 +01:00
Vaxry
185a3b4881 swipe: nuke numbered
fixes #5424

use_r instead
2024-04-11 12:46:19 +01:00
TheOnlyMrCat
47e5b41fea renderer: Add dimaround layer rule (#4643) 2024-04-11 12:41:18 +01:00
Vaxry
ac0f3411c1 macros: fix no pch warning 2024-04-11 02:13:05 +01:00
Vaxry
abc131ec7b configmgr: fix header priority 2024-04-11 02:12:29 +01:00
Ben Landon
558d1be7e3 hyprpm: Improve Hyprpm Update Performance (#5530)
* hyprpm: only clone the required history

* hyprpm: don't include tracy when building headers in release mode

* chore: remove old, commented-out code

See https://github.com/hyprwm/Hyprland/pull/4585#discussion_r1474780294

* chore: format code properly
2024-04-10 17:33:50 +01:00
SoSeDiK
0b2f7a1b2f cursor: Fallback to xcursor if failed to render hyprcursor (#5534) 2024-04-10 17:29:17 +01:00
Sungyoon Cho
c35fa9bacc workspace: update windows when selector match could change (#5533)
* workspace: update windows when group updates

* workspace: update windows when floating toggle

* workspace: update windows when stop dragging window by mouse
2024-04-10 17:26:11 +01:00
Vaxry
b573c20125 monitor: add workspace null check to visible flag
ref #5524
2024-04-10 17:21:45 +01:00
JManch
303b9956b2 hyprctl: print monitor disabled status (#5525) 2024-04-10 09:50:00 +01:00
SoSeDiK
1343aa865d config: Don't override fullscreen opacity if only two opacities are provided (#5512) 2024-04-09 16:22:44 +01:00
SoSeDiK
f2addfb404 props: Parse border color props as gradient (#5513) 2024-04-09 16:14:53 +01:00
Sungyoon Cho
fcac25bcc2 workspace: Add count group flag in windowCount workspace selector prop (#5499)
* Add groupCount workspace selector prop

* Intergrate groupCount with windowCount
2024-04-09 12:08:38 +01:00
ErrorNoInternet
f6786f04d2 hyprpm: install shell completions 2024-04-09 12:38:06 +03:00
ErrorNoInternet
c7b87e0aed hyprctl: fix fish completions 2024-04-09 12:38:06 +03:00
postsolar
d0d1ba5918 hyprctl: fix zsh completions
The file missed a line required by all ZSH completions in order to be automatically loaded
2024-04-09 07:08:54 +03:00
dranull
a06272ae55 input: Option for handling off-window axis events (#4177) 2024-04-08 23:35:21 +01:00
Mihai Fufezan
277f2bb76a Nix: add pkgconf 2024-04-08 20:54:06 +03:00
Vaxry
0457c2e348 pkg-config -> pkgconf for hyprpm and cmake 2024-04-08 20:54:06 +03:00
Tom Englund
125a8f7e07 workspace: fix crash on destruction of compositor (#5495)
when the compositor destructs because of exiting hyprland the
hookmanager and eventmanager is already destroyed, add an if check in
the destructor of workspace so it doesnt segfault on exit.
2024-04-08 18:28:11 +01:00
Vaxry
63e3668529 style: run clang-format 2024-04-08 15:33:02 +01:00
Tom Englund
db91d949f7 compositor: move wl_display_destroy_clients (#5498)
if enough clients are open when destructing the compositor destroying
clients will emit a wl_surface_unmap that a WLListener catches and doing
so it calls listener_unmapLayerSurface that tries to iterate over input
manager that is already destroyed, move the destruction of clients above
g_pInputManager.reset() and removeAllSignals() to ensure we dont
segfault at exit.
2024-04-08 15:32:31 +01:00
Vaxry
785d9d9521 config: verify string length in wrv2 before calling back
ref #5431
2024-04-08 15:27:13 +01:00
SoSeDiK
43b96f03b5 props: Allow setting per-window fullscreen opacity (#5470) 2024-04-07 23:19:02 +01:00
MightyPlaza
df1a3a978d input: don't remove pinned focus on workspace change (#5486)
modified:   src/helpers/Monitor.cpp
2024-04-07 23:13:56 +01:00
Vaxry
7d989f2cf0 damageSurface: don't correct smallVec twice 2024-04-07 22:25:34 +01:00
Vaxry
863c7b6072 eventloop: move timers to an event loop fd
fixes #5481
2024-04-07 21:55:29 +01:00
Mihai Fufezan
c0d283016b flake.lock: update
Contains hyprcursor 1.6
2024-04-07 22:52:10 +03:00
LOSEARDES77
20899d0df2 hyprpm: add shell completions (#5423)
* hyprpm: add completions

* hyprctl: correct spell mistakes

* Apply fixes

* makefile: correct shell completion paths

* makefile: remove complletions on uninstalling
2024-04-07 19:39:46 +03:00
Yaroslav Lelkin
b50182326c cmake: make sure that OpenGL::EGL is populated
bump cmake version cause 3.27 is the version where "... COMPONENTS
GLESx" option is introduced. See
https://cmake.org/cmake/help/latest/module/FindOpenGL.html
2024-04-07 19:08:25 +03:00
thejch
89f775aec2 master: fix crash (#5472) 2024-04-07 15:21:12 +01:00
Junxuan Liao
d657b59f70 IME: fix IME popup mouse inputs (again) (#5417)
`lastBoxLocal`'s size should be the actual popup's size instead of the cursor
rectangle's size. Also, the rectangle position is now relative to the popup.
(Actually fixes #5255 imho.)

One thing #3922 missed was handling focus held by buttons. Let's hope I get
it right this time.
2024-04-07 15:15:50 +01:00
Vaxry
f2a848cbcc core: Event loop rework (#5466)
* Event loop rework

* revert missed
2024-04-07 03:31:51 +01:00
thejch
9f1604e4b0 input: Dont set active monitor when simulating mouse movement (#5465)
* fix mouse simulation switching focusedmon

* fix some warnings with wrong enum
2024-04-07 01:07:21 +01:00
thejch
e80bccad51 master: fix workspace orientation not being restored after workspace rule no longer applies (#5463) 2024-04-06 23:49:38 +01:00
Vaxry
ff114cf6f9 input: fix focus on maximized bg surfaces 2024-04-06 18:59:23 +01:00
Vaxry
d846e82832 makefile: add patch option to make asan 2024-04-06 18:50:04 +01:00
Vaxry
fa79aacea3 constraint: fix possible uaf on double destruction
ref #5448
2024-04-06 18:43:17 +01:00
fred21O4
265c7924d8 flake.nix: add hyprcursor follows (#5435)
fixes a duplicate hyprlang instance sometimes being created due to hyprcursor not following hyprlands instance
2024-04-06 19:18:43 +03:00
Mihai Fufezan
3d64b0e9f0 flake.lock: update 2024-04-06 19:09:37 +03:00
Sungyoon Cho
04d067d78b IME: fix race condition on closing window (#5455) 2024-04-06 15:54:02 +01:00
staz
1596e2d1f7 workspacerules: add back on-created-empty functionality (#5452)
* workspacerules: add back on-created-empty functionality

* clang format

* workspacerules: spawn on-created-empty window while initializing CWorkspace

* clang format

* configManager: fix typo

---------

Co-authored-by: Your Name <you@example.com>
2024-04-06 15:53:32 +01:00
Vaxry
6cea710ac8 scripts: switch to branch --show-current for branch in generateVersion 2024-04-06 15:40:06 +01:00
Vaxry
f081a4300f input: fixup background layer checking on maximized 2024-04-06 15:18:58 +01:00
Vaxry
159444c45b compositor: fix ghost fadingOut windows remaining after cleanup 2024-04-06 14:59:30 +01:00
Vaxry
f8c22916ab compositor: remove windows from fadingOut properly 2024-04-06 14:51:35 +01:00
Vaxry
24734fbf1d subsurface: init existing subsurfaces on children creations
fixes #5333
2024-04-06 03:09:20 +01:00
Vaxry
dab149e4a6 core: fix compile without pch
fixes #5445
2024-04-05 21:23:28 +01:00
Vaxry
b5b1c0137d CColor: fix getAsHex 2024-04-05 21:23:06 +01:00
Vaxry
094bce8118 core: simplify sanityCheckWorkspaces 2024-04-05 19:43:51 +01:00
Vaxry
4909b0f350 monitor: unset visible flag from ws on disconnect
ref #5443
2024-04-05 19:25:40 +01:00
Vaxry
965a2e5b21 hooksystem: attempt allocating pages in linear order 2024-04-05 17:16:09 +01:00
Vaxry
f815a33f64 workspace: remove monitor and visible flags on inert 2024-04-05 16:57:49 +01:00
Vaxry
0051b078a1 monitor: check for invalid workspaces in onConnect
ref #5443
2024-04-05 16:57:49 +01:00
thejch
1e8f57c734 workspacerules: fix workspace rule loops (#5433) 2024-04-05 16:54:30 +01:00
Martin Sundhaug
942172d2dc hooksystem: Fix miscalculation in comment (#5442) 2024-04-05 12:56:53 +01:00
bvr-yr
baad44b4ca hyprpm: fix incorrect commits number parsing (#5437) 2024-04-05 04:40:44 +01:00
Vaxry
12d75c0c26 hyprpm: ignore version checks for shallow clones 2024-04-05 03:00:34 +01:00
Vaxry
1ae592fcd9 hyprpm: add support for minimum versions 2024-04-05 00:46:37 +01:00
Vaxry
51b3148f09 hyprpm: print more info on build failures 2024-04-05 00:23:05 +01:00
Vaxry
1454c6213e window: fix invalid last workspace id
ref #5432
2024-04-04 22:49:15 +01:00
Vaxry
ec2cc79c65 renderer: avoid double-rendering ls-es on fadingOut
fixes #5295
2024-04-04 22:43:57 +01:00
Vaxry
0569b9c300 hooksystem: manually map trampoline addresses
better patching of rip calls as we are close enough to just change them up
2024-04-04 18:50:37 +01:00
Vaxry
cba9c5ff95 core: fix visibility flags in moveWorkspaceToMonitor
fixes #5416
2024-04-04 18:30:50 +01:00
Sungyoon Cho
c4b660a339 IME: fix crash on restarting IME (#5428) 2024-04-04 17:34:04 +01:00
Vaxry
4f3e90ad2d popups: more safety in damage checking 2024-04-04 16:42:30 +01:00
end-4
9b8ef9206d layers: separate anim configs for open/close (#5421) 2024-04-04 16:41:09 +01:00
zakk4223
846162cce1 hyprpm: Use proper path to update repo when processing user provided revision (#5414) 2024-04-04 16:33:36 +01:00
Mihai Fufezan
81766647f2 hyprctl: fix grammar mistakes in completions 2024-04-04 10:49:25 +03:00
LOSEARDES77
1b43cd5231 hyprctl: Add shell completions (#5404) 2024-04-04 10:21:20 +03:00
Vaxry
b7d71bc0e1 keybinds: fix spammy warning 2024-04-04 01:16:47 +01:00
Vaxry
9cf563065a layouts: add missing include 2024-04-04 01:10:46 +01:00
Vaxry
36a8ae9bda input: allow focus to bottom layers on maximized in reserved 2024-04-03 21:57:19 +01:00
Micovec
949eb42613 hyprctl: improve help pages (#5385) 2024-04-03 23:41:10 +03:00
Vaxry
d605e47511 renderer: block screen shader on screencopy 2024-04-03 21:35:16 +01:00
Vaxry
10146f5ec5 core: fix some crash conditions around workspace ptrs in CWindow
ref #5402, supersedes #5409
2024-04-03 20:42:38 +01:00
Vaxry
d88d589880 swipe: add events 2024-04-03 19:20:47 +01:00
Vaxry
93915502d2 blur: block modif only on no new optimize 2024-04-03 17:08:11 +01:00
Vaxry
91061a2084 opengl: fix modif in blur 2024-04-03 15:08:29 +01:00
Vaxry
64964c4e3b renderer: render back layer for workspace-less passes 2024-04-03 14:28:15 +01:00
Vaxry
3981f85e94 opengl: log framebuffer errors 2024-04-03 14:24:15 +01:00
Vaxry
efdc1af044 renderer: some fixes for renderModif 2024-04-03 14:09:58 +01:00
Vaxry
347b839034 workspaces: add visible flag 2024-04-03 10:09:48 +01:00
thejch
fbdaf74a82 master: fix swapped workspaces (#5397) 2024-04-03 01:22:59 +01:00
thejch
3965faafac master: fix center resizing (#5394) 2024-04-03 01:22:27 +01:00
MightyPlaza
153c8f35ce workspace: fix special unnamed workspace rules (#5390)
modified:   src/desktop/Workspace.cpp
2024-04-02 22:58:45 +01:00
Vaxry
ef23ef60c5 Workspace/core: Refactor workspace storage (#5380)
* refactor workspaces to use ptrs

* clang-format
2024-04-02 20:32:39 +01:00
Vaxry
fc0a7af7ba IME: fix blurry ime on scaled
ref #5387
2024-04-02 16:10:55 +01:00
Vaxry
05eb2d4af2 master: guard window in moveWindowTo
fixes #5374
2024-04-02 12:46:15 +01:00
Sungyoon Cho
04a35891a1 IME: fix incorrect popup damage (#5383) 2024-04-02 12:22:41 +01:00
Vaxry
2e5b146e57 workspace: remove lastFocusedWindow on unmap 2024-04-02 12:10:03 +01:00
Vaxry
af3a61a4e4 core: assert attempted UAFs in windowExists
in prep of removing the thing altogether
2024-04-02 01:15:58 +01:00
Jan Beich
c377caee7a hyprerror: align 32-bit types after 4c796683c0 (#5375)
src/hyprerror/HyprError.cpp:64:33: error: no matching function for call to 'min'
    const auto   VISLINECOUNT = std::min(LINECOUNT, *LINELIMIT);
                                ^~~~~~~~
/usr/include/c++/v1/__algorithm/min.h:40:1: note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('int' vs. 'long long')
min(const _Tp& __a, const _Tp& __b)
^
/usr/include/c++/v1/__algorithm/min.h:51:1: note: candidate template ignored: could not match 'initializer_list<_Tp>' against 'int'
min(initializer_list<_Tp> __t, _Compare __comp)
^
/usr/include/c++/v1/__algorithm/min.h:60:1: note: candidate function template not viable: requires single argument '__t', but 2 arguments were provided
min(initializer_list<_Tp> __t)
^
/usr/include/c++/v1/__algorithm/min.h:31:1: note: candidate function template not viable: requires 3 arguments, but 2 were provided
min(const _Tp& __a, const _Tp& __b, _Compare __comp)
^
2024-04-01 21:18:18 +01:00
241 changed files with 16388 additions and 6489 deletions

View File

@@ -12,7 +12,6 @@ runs:
- name: Get required pacman pkgs
shell: bash
run: |
sed -i -e "1i [extra-testing]\nInclude = /etc/pacman.d/mirrorlist" "/etc/pacman.conf"
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy \
@@ -25,6 +24,7 @@ runs:
glslang \
go \
hyprlang \
hyprcursor \
jq \
libc++ \
libdisplay-info \
@@ -44,6 +44,7 @@ runs:
pango \
pixman \
pkgconf \
pugixml \
scdoc \
seatd \
systemd \
@@ -53,14 +54,16 @@ runs:
xcb-util-errors \
xcb-util-renderutil \
xcb-util-wm \
xcb-util \
xcb-util-image \
libzip \
librsvg
- name: Get hyprcursor-git
- name: Get hyprwayland-scanner-git
shell: bash
run: |
git clone https://github.com/hyprwm/hyprcursor --recursive
cd hyprcursor
git clone https://github.com/hyprwm/hyprwayland-scanner --recursive
cd hyprwayland-scanner
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build

View File

@@ -32,7 +32,6 @@ jobs:
cp build/Hyprland hyprland/
cp build/hyprctl/hyprctl hyprland/
cp build/hyprpm/hyprpm hyprland/
cp subprojects/wlroots/build/libwlroots.so.13032 hyprland/
cp build/Hyprland hyprland/
cp -r example/ hyprland/
cp -r assets/ hyprland/
@@ -64,6 +63,25 @@ jobs:
- name: Compile
run: ninja -C build
no-pch:
name: "Build Hyprland without precompiled headers (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
with:
INSTALL_XORG_PKGS: true
- name: Compile
run: make nopch
noxwayland:
name: "Build Hyprland in pure Wayland (Arch)"
runs-on: ubuntu-latest

View File

@@ -26,4 +26,4 @@ jobs:
name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix build .#${{ matrix.package }} -L
- run: nix build .#${{ matrix.package }} --extra-substituters "https://hyprland.cachix.org" -L

View File

@@ -18,6 +18,7 @@ jobs:
- name: Generate version
id: genversion
run: |
git fetch --unshallow || echo "failed unshallowing"
bash -c scripts/generateVersion.sh
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak

4
.gitignore vendored
View File

@@ -17,8 +17,8 @@ result*
.cache
*.o
*-protocol.c
*-protocol.h
protocols/*.c*
protocols/*.h*
.ccls-cache
*.so

7
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "wlroots"]
path = subprojects/wlroots
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
[submodule "subprojects/hyprland-protocols"]
path = subprojects/hyprland-protocols
url = https://github.com/hyprwm/hyprland-protocols
@@ -10,3 +7,7 @@
[submodule "subprojects/tracy"]
path = subprojects/tracy
url = https://github.com/wolfpld/tracy
[submodule "subprojects/wlroots-hyprland"]
path = subprojects/wlroots-hyprland
url = https://github.com/hyprwm/wlroots-hyprland
ignore = dirty

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.27)
include(CheckIncludeFile)
# Get version
@@ -54,25 +54,22 @@ else()
endif()
ExternalProject_Add(
wlroots
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots
PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/soversion = 13032/g" meson.build
CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
wlroots-hyprland
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland
CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 -Dbackends=drm,libinput $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
BUILD_COMMAND ninja -C build
BUILD_ALWAYS true
BUILD_IN_SOURCE true
BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032
INSTALL_COMMAND echo "wlroots: install not needed"
BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a
INSTALL_COMMAND echo "wlroots-hyprland: install not needed"
)
find_program(WaylandScanner NAMES wayland-scanner)
find_package(PkgConfig REQUIRED)
pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process(
COMMAND pkg-config --variable=pkgdatadir wayland-protocols
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
@@ -86,22 +83,34 @@ endif()
include_directories(
.
"src/"
"subprojects/wlroots/include/"
"subprojects/wlroots/build/include/"
"subprojects/wlroots-hyprland/include/"
"subprojects/wlroots-hyprland/build/include/"
"subprojects/udis86/"
"protocols/")
set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(WLR_USE_UNSTABLE)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
add_link_options(-rdynamic)
set(CMAKE_ENABLE_EXPORTS TRUE)
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2 hyprcursor) # we do not check for wlroots, as we provide it ourselves
if(LEGACY_RENDERER)
set(GLES_VERSION "GLES2")
else()
set(GLES_VERSION "GLES3")
endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(deps REQUIRED IMPORTED_TARGET
xkbcommon uuid
wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
hyprwayland-scanner>=0.3.4 hyprlang>=0.3.2 hyprcursor>=0.1.7
)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
@@ -112,7 +121,7 @@ if(USE_TRACY)
endif()
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
add_dependencies(Hyprland wlroots)
add_dependencies(Hyprland wlroots-hyprland)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
@@ -155,6 +164,12 @@ if(HAVE_LIBEXECINFO)
target_link_libraries(Hyprland execinfo)
endif()
check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
if(NOT HAS_TIMERFD AND epoll_FOUND)
target_link_libraries(Hyprland PkgConfig::epoll)
endif()
if(LEGACY_RENDERER)
message(STATUS "Using the legacy GLES2 renderer!")
add_compile_definitions(LEGACY_RENDERER)
@@ -165,27 +180,19 @@ if(NO_XWAYLAND)
add_compile_definitions(NO_XWAYLAND)
else()
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
pkg_check_modules(xcbdep REQUIRED IMPORTED_TARGET xcb)
target_link_libraries(Hyprland PkgConfig::xcbdep)
pkg_check_modules(xdeps REQUIRED IMPORTED_TARGET xcb xwayland xcb-util xcb-render xcb-xfixes xcb-icccm xcb-composite xcb-res xcb-ewmh)
pkg_check_modules(xcb_errors IMPORTED_TARGET xcb-errors)
target_link_libraries(Hyprland PkgConfig::xdeps)
if(xcb_errors_FOUND)
target_link_libraries(Hyprland PkgConfig::xcb_errors)
endif()
endif()
if(NO_SYSTEMD)
message(STATUS "SYSTEMD support is disabled...")
else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined) checking deps...")
check_include_file("systemd/sd-daemon.h" SYSTEMDH)
if(SYSTEMDH)
pkg_check_modules(LIBSYSTEMD libsystemd)
if (LIBSYSTEMD_FOUND)
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
add_compile_definitions(USES_SYSTEMD)
target_link_libraries(Hyprland "${LIBSYSTEMD_LIBRARIES}")
message(STATUS "Systemd found")
else()
message(WARNING "Systemd support requested but systemd libraries were not found")
endif()
else()
message(WARNING "Systemd support requested but systemd headers were not found")
endif()
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
@@ -219,31 +226,61 @@ function(protocol protoPath protoName external)
target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c)
endif()
endfunction()
function(protocolNew protoPath protoName external)
if (external)
execute_process(
COMMAND hyprwayland-scanner ${protoPath} ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp)
else()
execute_process(
COMMAND hyprwayland-scanner ${WAYLAND_PROTOCOLS_DIR}/${protoPath} ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp)
endif()
endfunction()
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032 # wlroots is provided by us
${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a
OpenGL::EGL
OpenGL::GL
Threads::Threads
libudis86
uuid
)
protocol("protocols/idle.xml" "idle" true)
protocol("protocols/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" true)
protocol("protocols/tablet-unstable-v2.xml" "tablet-unstable-v2" true)
protocol("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true)
protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocol("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true)
protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocol("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true)
protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true)
protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true)
protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true)
protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true)
protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
protocolNew("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-unstable-v1" false)
protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false)
protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false)
protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false)
protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false)
protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false)
protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false)
protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false)
protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" false)
protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false)
protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false)
protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false)
# tools
add_subdirectory(hyprctl)

View File

@@ -20,10 +20,14 @@ debug:
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
nopch:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
clear:
rm -rf build
rm -f ./protocols/*-protocol.h ./protocols/*-protocol.c
rm -rf ./subprojects/wlroots/build
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
rm -rf ./subprojects/wlroots-hyprland/build
all:
@if [[ "$EUID" = 0 ]]; then echo -en "Avoid running $(MAKE) all as sudo.\n"; fi
@@ -36,15 +40,24 @@ install:
mkdir -p ${PREFIX}/share/wayland-sessions
mkdir -p ${PREFIX}/bin
mkdir -p ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/bash-completion/completions
mkdir -p ${PREFIX}/share/fish/vendor_completions.d
mkdir -p ${PREFIX}/share/zsh/site-functions
cp -f ./build/Hyprland ${PREFIX}/bin
cp -f ./build/hyprctl/hyprctl ${PREFIX}/bin
cp -f ./build/hyprpm/hyprpm ${PREFIX}/bin
cp -f ./hyprctl/hyprctl.bash ${PREFIX}/share/bash-completion/completions/hyprctl
cp -f ./hyprctl/hyprctl.fish ${PREFIX}/share/fish/vendor_completions.d/hyprctl.fish
cp -f ./hyprctl/hyprctl.zsh ${PREFIX}/share/zsh/site-functions/_hyprctl
cp -f ./hyprpm/hyprpm.bash ${PREFIX}/share/bash-completion/completions/hyprpm
cp -f ./hyprpm/hyprpm.fish ${PREFIX}/share/fish/vendor_completions.d/hyprpm.fish
cp -f ./hyprpm/hyprpm.zsh ${PREFIX}/share/zsh/site-functions/_hyprpm
chmod 755 ${PREFIX}/bin/Hyprland
chmod 755 ${PREFIX}/bin/hyprctl
chmod 755 ${PREFIX}/bin/hyprpm
cd ${PREFIX}/bin && ln -sf Hyprland hyprland
if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland
cp ./assets/wall* ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/xdg-desktop-portal
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal
@@ -52,9 +65,6 @@ install:
mkdir -p ${PREFIX}/share/man/man1
install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
mkdir -p ${PREFIX}/lib/
cp ./subprojects/wlroots/build/libwlroots.so.13032 ${PREFIX}/lib/
$(MAKE) installheaders
uninstall:
@@ -63,10 +73,15 @@ uninstall:
rm -f ${PREFIX}/bin/hyprland
rm -f ${PREFIX}/bin/hyprctl
rm -f ${PREFIX}/bin/hyprpm
rm -f ${PREFIX}/lib/libwlroots.so.13032
rm -rf ${PREFIX}/share/hyprland
rm -f ${PREFIX}/share/man/man1/Hyprland.1
rm -f ${PREFIX}/share/man/man1/hyprctl.1
rm -f ${PREFIX}/share/bash-completion/completions/hyprctl
rm -f ${PREFIX}/share/fish/vendor_completions.d/hyprctl.fish
rm -f ${PREFIX}/share/zsh/site-functions/_hyprctl
rm -f ${PREFIX}/share/bash-completion/completions/hyprpm
rm -f ${PREFIX}/share/fish/vendor_completions.d/hyprpm.fish
rm -f ${PREFIX}/share/zsh/site-functions/_hyprpm
pluginenv:
@echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
@@ -78,13 +93,14 @@ installheaders:
rm -fr ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/include/hyprland/wlroots-hyprland
mkdir -p ${PREFIX}/share/pkgconfig
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cd subprojects/wlroots/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cd subprojects/wlroots-hyprland/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../..
cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../../..
cp ./protocols/*.h ${PREFIX}/include/hyprland/protocols
cp ./protocols/*.hpp ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
@@ -116,6 +132,10 @@ asan:
rm -rf ./wayland
git reset --hard
@echo -en "If you want to apply a patch, input its path (leave empty for none):\n"
@read patchvar
@if [-n "$patchvar"]; then patch -p1 < $patchvar || echo ""; else echo "No patch specified"; fi
git clone --recursive https://gitlab.freedesktop.org/wayland/wayland
cd wayland && patch -p1 < ../scripts/waylandStatic.diff && meson setup build --buildtype=debug -Db_sanitize=address -Ddocumentation=false && ninja -C build && cd ..
cp ./wayland/build/src/libwayland-server.a .

View File

@@ -10,7 +10,6 @@
[![Badge Pull Requests]][Pull Requests]
[![Badge Issues]][Issues]
![Badge Hi Mom]<br>
[![Badge Discord]][Discord]
<br>
@@ -45,11 +44,11 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
- Tearing support for better gaming performance
- Easily expandable and readable codebase
- Fast and active development
- Not scared to provide bleeding-edge features
- Not afraid to provide bleeding-edge features
- Config reloaded instantly upon saving
- Fully dynamic workspaces
- Two built-in layouts and more available as plugins
- Closely follows `wlroots-git`
- Uses forked wlroots with QoL patches
- Global keybinds passed to your apps of choice
- Tiling/pseudotiling/floating/fullscreen windows
- Special workspaces (scratchpads)
@@ -103,7 +102,6 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
<!----------------------------------------------------------------------------->
[Configure]: https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
[Discord]: https://discord.gg/hQ9XvMUjjr
[Stars]: https://starchart.cc/hyprwm/Hyprland
[Hypr]: https://github.com/hyprwm/Hypr
@@ -138,7 +136,6 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
[Badge Workflow]: https://github.com/hyprwm/Hyprland/actions/workflows/ci.yaml/badge.svg
[Badge Discord]: https://img.shields.io/badge/Join%20the-Discord%20server-6666ff
[Badge Issues]: https://img.shields.io/github/issues/hyprwm/Hyprland
[Badge Pull Requests]: https://img.shields.io/github/issues-pr/hyprwm/Hyprland
[Badge Language]: https://img.shields.io/github/languages/top/hyprwm/Hyprland

View File

@@ -34,16 +34,16 @@ If your bug crashes Hyprland, append additionally:
## Obtaining the Hyprland log
If you are in a TTY, and the hyprland session that crashed was the last one you launched, the log will be printed with
```
cat /tmp/hypr/$(ls -t /tmp/hypr/ | head -n 1)/hyprland.log
cat $XDG_RUNTIME_DIR/hypr/$(ls -t $XDG_RUNTIME_DIR/hypr | head -n 1)/hyprland.log
```
feel free to send it to a file, save, copy, etc.
if you are in a Hyprland session, and you want the log of the last session, use
```
cat /tmp/hypr/$(ls -t /tmp/hypr/ | head -n 2 | tail -n 1)/hyprland.log
cat $XDG_RUNTIME_DIR/hypr/$(ls -t $XDG_RUNTIME_DIR/hypr | head -n 2 | tail -n 1)/hyprland.log
```
basically, directories in /tmp/hypr are your sessions.
basically, directories in $XDG_RUNTIME_DIR/hypr are your sessions.
## Obtaining the Hyprland Crash Report (v0.22.0beta and up)

View File

@@ -1,70 +1,97 @@
# This is an example Hyprland config file.
#
# Refer to the wiki for more information.
# https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
#
# Please note not all available settings / options are set here.
# For a full list, see the wiki
#
# You can split this configuration into multiple files
# Create your files separately and then link them to this file like this:
# source = ~/.config/hypr/myColors.conf
################
### MONITORS ###
################
# See https://wiki.hyprland.org/Configuring/Monitors/
monitor=,preferred,auto,auto
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
###################
### MY PROGRAMS ###
###################
# Execute your favorite apps at launch
# exec-once = waybar & hyprpaper & firefox
# Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf
# See https://wiki.hyprland.org/Configuring/Keywords/
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars.
#################
### AUTOSTART ###
#################
# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:
# exec-once = $terminal
# exec-once = nm-applet &
# exec-once = waybar & hyprpaper & firefox
#############################
### ENVIRONMENT VARIABLES ###
#############################
# See https://wiki.hyprland.org/Configuring/Environment-variables/
env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that
env = HYPRCURSOR_SIZE,24
# For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
#####################
### LOOK AND FEEL ###
#####################
touchpad {
natural_scroll = false
}
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
}
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
gaps_in = 5
gaps_out = 20
border_size = 2
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
col.inactive_border = rgba(595959aa)
layout = dwindle
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
rounding = 10
# Change transparency of focused and unfocused windows
active_opacity = 1.0
inactive_opacity = 1.0
drop_shadow = true
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
enabled = true
size = 3
@@ -72,17 +99,13 @@ decoration {
vibrancy = 0.1696
}
drop_shadow = true
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = true
# Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = myBezier, 0.05, 0.9, 0.1, 1.05
@@ -94,25 +117,48 @@ animations {
animation = workspaces, 1, 6, default
}
dwindle {
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
pseudotile = true # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = true # you probably want this
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = true # You probably want this
}
master {
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_is_master = true
}
gestures {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
workspace_swipe = false
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
#############
### INPUT ###
#############
# https://wiki.hyprland.org/Configuring/Variables/#input
input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
touchpad {
natural_scroll = false
}
}
# https://wiki.hyprland.org/Configuring/Variables/#gestures
gestures {
workspace_swipe = false
}
# Example per-device config
@@ -122,16 +168,13 @@ device {
sensitivity = -0.5
}
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
####################
### KEYBINDINGSS ###
####################
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER
# See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, $terminal
@@ -184,3 +227,19 @@ bind = $mainMod, mouse_up, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
##############################
### WINDOWS AND WORKSPACES ###
##############################
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.

87
flake.lock generated
View File

@@ -2,7 +2,9 @@
"nodes": {
"hyprcursor": {
"inputs": {
"hyprlang": "hyprlang",
"hyprlang": [
"hyprlang"
],
"nixpkgs": [
"nixpkgs"
],
@@ -11,11 +13,11 @@
]
},
"locked": {
"lastModified": 1711466786,
"narHash": "sha256-sArxGyUBiCA1in+q6t0QqT+ZJiZ1PyBp7cNPKLmREM0=",
"lastModified": 1713612213,
"narHash": "sha256-zJboXgWNpNhKyNF8H/3UYzWkx7w00TOCGKi3cwi+tsw=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "d3876f34779cc03ee51e4aafc0d00a4f187c7544",
"rev": "cab4746180f210a3c1dd3d53e45c510e309e90e1",
"type": "github"
},
"original": {
@@ -50,17 +52,18 @@
"hyprlang": {
"inputs": {
"nixpkgs": [
"hyprcursor",
"nixpkgs"
],
"systems": "systems"
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1709914708,
"narHash": "sha256-bR4o3mynoTa1Wi4ZTjbnsZ6iqVcPGriXp56bZh5UFTk=",
"lastModified": 1713121246,
"narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "a685493fdbeec01ca8ccdf1f3655c044a8ce2fe2",
"rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706",
"type": "github"
},
"original": {
@@ -69,7 +72,7 @@
"type": "github"
}
},
"hyprlang_2": {
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@@ -79,26 +82,26 @@
]
},
"locked": {
"lastModified": 1711250455,
"narHash": "sha256-LSq1ZsTpeD7xsqvlsepDEelWRDtAhqwetp6PusHXJRo=",
"lastModified": 1714755542,
"narHash": "sha256-D0pg+ZRwrt4lavZ97Ca8clsgbPA3duLj8iEM7riaIFY=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "b3e430f81f3364c5dd1a3cc9995706a4799eb3fa",
"repo": "hyprwayland-scanner",
"rev": "1270ebaa539e56d61b708c24b072b09cbbd3a828",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"repo": "hyprwayland-scanner",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1711523803,
"narHash": "sha256-UKcYiHWHQynzj6CN/vTcix4yd1eCu1uFdsuarupdCQQ=",
"lastModified": 1714253743,
"narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2726f127c15a4cc9810843b96cad73c7eb39e443",
"rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
"type": "github"
},
"original": {
@@ -112,9 +115,10 @@
"inputs": {
"hyprcursor": "hyprcursor",
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang_2",
"hyprlang": "hyprlang",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"systems": "systems_2",
"systems": "systems",
"wlroots": "wlroots",
"xdph": "xdph"
}
@@ -134,38 +138,21 @@
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"wlroots": {
"flake": false,
"locked": {
"host": "gitlab.freedesktop.org",
"lastModified": 1709983277,
"narHash": "sha256-wXWIJLd4F2JZeMaihWVDW/yYXCLEC8OpeNJZg9a9ly8=",
"owner": "wlroots",
"repo": "wlroots",
"rev": "50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b",
"type": "gitlab"
"lastModified": 1713731601,
"narHash": "sha256-bdcKdtLkusvv85DNuJsajZLFeq7bXp+x5AGP1Sd4wD8=",
"owner": "hyprwm",
"repo": "wlroots-hyprland",
"rev": "5c1d51c5a2793480f5b6c4341ad0797052aec2ea",
"type": "github"
},
"original": {
"host": "gitlab.freedesktop.org",
"owner": "wlroots",
"repo": "wlroots",
"rev": "50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b",
"type": "gitlab"
"owner": "hyprwm",
"repo": "wlroots-hyprland",
"rev": "5c1d51c5a2793480f5b6c4341ad0797052aec2ea",
"type": "github"
}
},
"xdph": {
@@ -184,11 +171,11 @@
]
},
"locked": {
"lastModified": 1709299639,
"narHash": "sha256-jYqJM5khksLIbqSxCLUUcqEgI+O2LdlSlcMEBs39CAU=",
"lastModified": 1714060055,
"narHash": "sha256-j43TS9wv9luaAlpxcxw0sjxkbcc2mGANVR2RYgo3RCw=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "2d2fb547178ec025da643db57d40a971507b82fe",
"rev": "0fe840441e43da12cd7865ed9aa8cdc35a8da85a",
"type": "github"
},
"original": {

View File

@@ -8,11 +8,10 @@
systems.url = "github:nix-systems/default-linux";
wlroots = {
type = "gitlab";
host = "gitlab.freedesktop.org";
owner = "wlroots";
repo = "wlroots";
rev = "50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b";
type = "github";
owner = "hyprwm";
repo = "wlroots-hyprland";
rev = "5c1d51c5a2793480f5b6c4341ad0797052aec2ea";
flake = false;
};
@@ -20,6 +19,7 @@
url = "github:hyprwm/hyprcursor";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprlang.follows = "hyprlang";
};
hyprland-protocols = {
@@ -34,6 +34,12 @@
inputs.systems.follows = "systems";
};
hyprwayland-scanner = {
url = "github:hyprwm/hyprwayland-scanner";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
xdph = {
url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs";

158
hyprctl/Strings.hpp Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help]
commands:
activewindow Gets the active window name and its properties
activeworkspace Gets the active workspace and its properties
animations Gets the current config'd info about animations
and beziers
binds Lists all registered binds
clients Lists all windows with their properties
configerrors Lists all current config parsing errors
cursorpos Gets the current cursor position in global layout
coordinates
decorations <window_regex> Lists all decorations and their info
devices Lists all connected keyboards and mice
dismissnotify [amount] Dismisses all or up to AMOUNT notifications
dispatch <dispatcher> [args] Issue a dispatch to call a keybind
dispatcher with arguments
getoption <option> Gets the config option status (values)
globalshortcuts Lists all global shortcuts
hyprpaper ... Issue a hyprpaper request
instances Lists all running instances of Hyprland with
their info
keyword <name> <value> Issue a keyword to call a config keyword
dynamically
kill Issue a kill to get into a kill mode, where you can
kill an app by clicking on it. You can exit it
with ESCAPE
layers Lists all the surface layers
layouts Lists all layouts available (including plugin'd ones)
monitors Lists active outputs with their properties,
'monitors all' lists active and inactive outputs
notify ... Sends a notification using the built-in Hyprland
notification system
output ... Allows you to add and remove fake outputs to your
preferred backend
plugin ... Issue a plugin request
reload [config-only] Issue a reload to force reload the config. Pass
'config-only' to disable monitor reload
rollinglog Prints tail of the log
setcursor <theme> <size> Sets the cursor theme and reloads the cursor
manager
seterror <color> <message...> Sets the hyprctl error string. Color has
the same format as in colors in config. Will reset
when Hyprland's config is reloaded
setprop ... Sets a window property
splash Get the current splash
switchxkblayout ... Sets the xkb layout index for a keyboard
systeminfo Get system info
version Prints the hyprland version, meaning flags, commit
and branch of build.
workspacerules Lists all workspace rules
workspaces Lists all workspaces with their properties
flags:
-j Output in JSON
-r Refresh state after issuing command (e.g. for
updating variables)
--batch Execute a batch of commands, separated by ';'
--instance (-i) use a specific instance. Can be either signature or
index in hyprctl instances (0, 1, etc)
--help:
Can be used to print command's arguments that did not fit into this page
(three dots))#";
const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper <request>
requests:
listactive Lists all active images
listloaded Lists all loaded images
preload <path> Preloads image
unload <path> Unloads image. Pass 'all' as path to unload all images
wallpaper Issue a wallpaper to call a config wallpaper dynamically
flags:
See 'hyprctl --help')#";
const std::string_view NOTIFY_HELP = R"#(usage: hyprctl [flags] notify <icon> <time_ms> <color> <message...>
icon:
Integer of value:
0 Warning
1 Info
2 Hint
3 Error
4 Confused
5 Ok
6 or -1 No icon
time_ms:
Time to display notification in milliseconds
color:
Notification color. Format is the same as for colors in hyprland.conf. Use
0 for default color for icon
message:
Notification message
flags:
See 'hyprctl --help')#";
const std::string_view OUTPUT_HELP = R"#(usage: hyprctl [flags] output <create <backend> | remove <name>>
create <backend>:
Creates new virtual output. Possible values for backend: wayland, x11,
headless or auto.
remove <name>:
Removes virtual output. Pass the output's name, as found in
'hyprctl monitors'
flags:
See 'hyprctl --help')#";
const std::string_view PLUGIN_HELP = R"#(usage: hyprctl [flags] plugin <request>
requests:
load <path> Loads a plugin. Path must be absolute
unload <path> Unloads a plugin. Path must be absolute
list Lists all loaded plugins
flags:
See 'hyprctl --help')#";
const std::string_view SETPROP_HELP = R"#(usage: hyprctl [flags] setprop <regex> <property> <value> [lock]
regex:
Regular expression by which a window will be searched
property:
See https://wiki.hyprland.org/Configuring/Using-hyprctl/#setprop for list
of properties
value:
Property value
lock:
Optional argument. If lock is not added, will be unlocked. Locking means a
dynamic windowrule cannot override this setting.
flags:
See 'hyprctl --help')#";
const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout <device> <cmd>
device:
You can find the device using 'hyprctl devices' command
cmd:
'next' for next, 'prev' for previous, or ID for a specific one. IDs are
assigned based on their order in config file (keyboard_layout),
starting from 0
flags:
See 'hyprctl --help')#";

127
hyprctl/hyprctl.bash Normal file
View File

@@ -0,0 +1,127 @@
_hyprctl_cmd_2 () {
hyprctl monitors | grep Monitor | awk '{ print $2 }'
}
_hyprctl_cmd_1 () {
hyprpm list | grep "Plugin" | awk '{print $4}'
}
_hyprctl_cmd_0 () {
hyprctl clients | grep class | awk '{print $2}'
}
_hyprctl_cmd_3 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
}
_hyprctl () {
if [[ $(type -t _get_comp_words_by_ref) != function ]]; then
echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed
return 1
fi
local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow")
declare -A literal_transitions
literal_transitions[0]="([103]=1 [74]=2 [33]=3 [1]=2 [2]=2 [77]=2 [105]=4 [36]=2 [108]=5 [40]=2 [45]=2 [112]=2 [84]=6 [113]=8 [51]=2 [53]=2 [88]=9 [117]=2 [119]=2 [121]=2 [15]=2 [58]=10 [59]=2 [17]=11 [122]=12 [19]=2 [124]=2 [126]=2 [25]=13 [67]=2 [96]=5 [97]=2 [27]=2 [28]=14 [100]=2 [102]=5)"
literal_transitions[3]="([72]=18 [13]=2 [32]=18 [54]=18 [55]=18 [89]=18 [104]=2 [120]=2 [76]=1 [16]=2 [123]=18 [3]=1 [5]=2 [63]=18 [127]=2 [129]=18 [80]=18 [130]=18 [83]=18 [31]=18 [48]=2 [12]=2 [85]=18 [10]=18 [86]=18 [137]=18)"
literal_transitions[7]="([103]=1 [74]=2 [33]=3 [1]=2 [2]=2 [77]=2 [105]=4 [36]=2 [40]=2 [45]=2 [112]=2 [84]=6 [113]=8 [51]=2 [53]=2 [88]=9 [117]=2 [119]=2 [121]=2 [15]=2 [58]=10 [59]=2 [17]=11 [122]=12 [19]=2 [124]=2 [126]=2 [25]=13 [67]=2 [97]=2 [27]=2 [28]=14 [100]=2)"
literal_transitions[8]="([128]=2 [131]=2 [0]=2 [73]=2 [35]=2 [106]=2 [37]=2 [107]=2 [4]=2 [78]=2 [39]=2 [79]=2 [110]=2 [6]=2 [41]=2 [42]=2 [81]=2 [82]=2 [46]=2 [47]=2 [9]=2 [109]=2 [50]=2 [52]=2 [11]=2 [115]=2 [87]=2 [49]=2 [56]=2 [90]=2 [57]=2 [91]=2 [92]=2 [60]=2 [61]=2 [125]=2 [93]=2 [62]=2 [20]=2 [95]=2 [22]=2 [23]=2 [64]=2 [65]=2 [24]=2 [132]=2 [26]=2 [68]=2 [98]=2 [69]=2 [29]=2 [136]=2 [70]=2 [99]=2)"
literal_transitions[9]="([114]=15 [111]=16)"
literal_transitions[11]="([101]=2)"
literal_transitions[13]="([21]=1 [116]=1 [30]=1 [135]=1 [118]=1 [43]=1 [71]=1)"
literal_transitions[14]="([38]=2)"
literal_transitions[15]="([8]=2 [66]=2 [14]=2 [133]=2)"
literal_transitions[17]="([75]=19)"
literal_transitions[18]="([18]=2 [7]=2)"
literal_transitions[19]="([34]=5 [44]=5)"
literal_transitions[20]="([134]=2 [94]=2)"
declare -A match_anything_transitions
match_anything_transitions=([1]=2 [0]=7 [6]=2 [20]=2 [10]=2 [2]=17 [7]=7 [12]=2 [14]=17 [16]=2 [4]=20 [11]=17)
declare -A subword_transitions
local state=0
local word_index=1
while [[ $word_index -lt $cword ]]; do
local word=${words[$word_index]}
if [[ -v "literal_transitions[$state]" ]]; then
declare -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
local word_matched=0
for literal_id in $(seq 0 $((${#literals[@]} - 1))); do
if [[ ${literals[$literal_id]} = "$word" ]]; then
if [[ -v "state_transitions[$literal_id]" ]]; then
state=${state_transitions[$literal_id]}
word_index=$((word_index + 1))
word_matched=1
break
fi
fi
done
if [[ $word_matched -ne 0 ]]; then
continue
fi
fi
if [[ -v "match_anything_transitions[$state]" ]]; then
state=${match_anything_transitions[$state]}
word_index=$((word_index + 1))
continue
fi
return 1
done
local prefix="${words[$cword]}"
local shortest_suffix="$word"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate="${word##*$char}"
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$word" ]]; then
local superfluous_prefix=${word%$shortest_suffix}
fi
if [[ -v "literal_transitions[$state]" ]]; then
local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions
eval "state_transitions=$state_transitions_initializer"
for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"}
COMPREPLY+=("$completion ")
fi
done
fi
declare -A commands
commands=([16]=2 [4]=3 [12]=1 [10]=0)
if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]}
local completions=()
mapfile -t completions < <(_hyprctl_cmd_${command_id} "$prefix" | cut -f1)
for item in "${completions[@]}"; do
if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item")
fi
done
fi
return 0
}
complete -o nospace -F _hyprctl hyprctl

216
hyprctl/hyprctl.fish Normal file
View File

@@ -0,0 +1,216 @@
function _hyprctl_3
set 1 $argv[1]
hyprctl monitors | grep Monitor | awk '{ print $2 }'
end
function _hyprctl_2
set 1 $argv[1]
hyprpm list | grep "Plugin" | awk '{print $4}'
end
function _hyprctl_1
set 1 $argv[1]
hyprctl clients | grep class | awk '{print $2}'
end
function _hyprctl_4
set 1 $argv[1]
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
end
function _hyprctl
set COMP_LINE (commandline --cut-at-cursor)
set COMP_WORDS
echo $COMP_LINE | read --tokenize --array COMP_WORDS
if string match --quiet --regex '.*\s$' $COMP_LINE
set COMP_CWORD (math (count $COMP_WORDS) + 1)
else
set COMP_CWORD (count $COMP_WORDS)
end
set --local literals "cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow"
set --local descriptions
set descriptions[1] "Focus the next window on a workspace"
set descriptions[3] "Get the current cursor pos in global layout coordinates"
set descriptions[5] "Rename a workspace"
set descriptions[7] "Focus the first window matching"
set descriptions[10] "Swap the focused window with the next window"
set descriptions[12] "Move the active window"
set descriptions[16] "List the layers"
set descriptions[18] "List active outputs with their properties"
set descriptions[20] "Get into a kill mode, where you can kill an app by clicking on it"
set descriptions[21] "Set the current window's floating state to false"
set descriptions[22] "ERROR"
set descriptions[23] "Focus a monitor"
set descriptions[24] "Swap the active window with another window"
set descriptions[25] "Move the active window out of a group"
set descriptions[26] "Send a notification using the built-in Hyprland notification system"
set descriptions[27] "Move the cursor to a specified position"
set descriptions[28] "Set the cursor theme and reloads the cursor manager"
set descriptions[29] "Set the hyprctl error string"
set descriptions[30] "Move the active workspace to a monitor"
set descriptions[31] "CONFUSED"
set descriptions[34] "Set a property of a window"
set descriptions[35] "Specify the Hyprland instance"
set descriptions[36] "Toggle the current window's floating state"
set descriptions[37] "Get the list of defined workspace rules"
set descriptions[38] "Move the focused window to a workspace"
set descriptions[40] "Temporarily enable or disable binds:ignore_group_lock"
set descriptions[41] "List all workspaces with their properties"
set descriptions[42] "Swap the active window with the next or previous in a group"
set descriptions[43] "Close a specified window"
set descriptions[44] "WARNING"
set descriptions[45] "Specify the Hyprland instance"
set descriptions[46] "List all registered binds"
set descriptions[47] "Move the active window in a direction or to a monitor"
set descriptions[48] "Change the split ratio"
set descriptions[50] "Prohibit the active window from becoming or being inserted into group"
set descriptions[51] "Change the workspace"
set descriptions[52] "List all current config parsing errors"
set descriptions[53] "Toggle the current active window into a group"
set descriptions[54] "Get the config option status (values)"
set descriptions[57] "Close the active window"
set descriptions[58] "Pass the key to a specified window"
set descriptions[59] "List all decorations and their info"
set descriptions[60] "List all connected keyboards and mice"
set descriptions[61] "Switch focus from current to previously focused window"
set descriptions[62] "Change the current mapping group"
set descriptions[63] "Execute a Global Shortcut using the GlobalShortcuts portal"
set descriptions[65] "Force the renderer to reload all resources and outputs"
set descriptions[66] "Move a selected window"
set descriptions[68] "Print the Hyprland version: flags, commit and branch of build"
set descriptions[69] "Set all monitors' DPMS status"
set descriptions[70] "Resize the active window"
set descriptions[71] "Move the active window into a group"
set descriptions[72] "OK"
set descriptions[74] "Set the current window's floating state to true"
set descriptions[75] "Print tail of the log"
set descriptions[78] "List all layouts available (including plugin ones)"
set descriptions[79] "Move a workspace to a monitor"
set descriptions[80] "Execute a shell command"
set descriptions[82] "Modify the window stack order of the active or specified window"
set descriptions[83] "Toggle the focused window's internal fullscreen state"
set descriptions[85] "Issue a keyword to call a config keyword dynamically"
set descriptions[88] "Pin a window"
set descriptions[89] "Allows adding/removing fake outputs to a specific backend"
set descriptions[91] "Toggle a special workspace on/off"
set descriptions[92] "Toggle the focused window's fullscreen state"
set descriptions[93] "Toggle the current window to always be opaque"
set descriptions[94] "Focus the requested workspace"
set descriptions[96] "Switch to the next window in a group"
set descriptions[97] "Output in JSON format"
set descriptions[98] "List all running Hyprland instances and their info"
set descriptions[99] "Execute a raw shell command"
set descriptions[100] "Exit the compositor with no questions asked"
set descriptions[101] "List all windows with their properties"
set descriptions[103] "Execute a batch of commands separated by ;"
set descriptions[104] "Dismiss all or up to amount of notifications"
set descriptions[106] "Set the xkb layout index for a keyboard"
set descriptions[107] "Move window doesnt switch to the workspace"
set descriptions[108] "Behave as moveintogroup"
set descriptions[109] "Refresh state after issuing the command"
set descriptions[110] "Move the focus in a direction"
set descriptions[111] "Focus the urgent window or the last window"
set descriptions[113] "Get the active workspace name and its properties"
set descriptions[114] "Issue a dispatch to call a keybind dispatcher with an arg"
set descriptions[116] "Center the active window"
set descriptions[117] "HINT"
set descriptions[118] "Interact with hyprpaper if present"
set descriptions[119] "No Icon"
set descriptions[120] "Force reload the config"
set descriptions[122] "Print system info"
set descriptions[123] "Interact with a plugin"
set descriptions[125] "Get the active window name and its properties"
set descriptions[126] "Swap the active workspaces between two monitors"
set descriptions[127] "Print the current random splash"
set descriptions[129] "Lock the focused group"
set descriptions[132] "Lock the groups"
set descriptions[133] "Move the cursor to the corner of the active window"
set descriptions[136] "INFO"
set descriptions[137] "Resize a selected window"
set --local literal_transitions
set literal_transitions[1] "set inputs 104 75 34 2 3 78 106 37 109 41 46 113 85 114 52 54 89 118 120 122 16 59 60 18 123 20 125 127 26 68 97 98 28 29 101 103; set tos 2 3 4 3 3 3 5 3 6 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 6 3 3 15 3 6"
set literal_transitions[4] "set inputs 73 14 33 55 56 90 105 121 77 17 124 4 6 64 128 130 81 131 84 32 49 13 86 11 87 138; set tos 19 3 19 19 19 19 3 3 2 3 19 2 3 19 3 19 19 19 19 19 3 3 19 19 19 19"
set literal_transitions[8] "set inputs 104 75 34 2 3 78 106 37 41 46 113 85 114 52 54 89 118 120 122 16 59 60 18 123 20 125 127 26 68 98 28 29 101; set tos 2 3 4 3 3 3 5 3 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 3 3 15 3"
set literal_transitions[9] "set inputs 129 132 1 74 36 107 38 108 5 79 40 80 111 7 42 43 82 83 47 48 10 110 51 53 12 116 88 50 57 91 58 92 93 61 62 126 94 63 21 96 23 24 65 66 25 133 27 69 99 70 30 137 71 100; set tos 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3"
set literal_transitions[10] "set inputs 115 112; set tos 16 17"
set literal_transitions[12] "set inputs 102; set tos 3"
set literal_transitions[14] "set inputs 22 117 31 136 119 44 72; set tos 2 2 2 2 2 2 2"
set literal_transitions[15] "set inputs 39; set tos 3"
set literal_transitions[16] "set inputs 9 67 15 134; set tos 3 3 3 3"
set literal_transitions[18] "set inputs 76; set tos 20"
set literal_transitions[19] "set inputs 19 8; set tos 3 3"
set literal_transitions[20] "set inputs 35 45; set tos 6 6"
set literal_transitions[21] "set inputs 135 95; set tos 3 3"
set --local match_anything_transitions_from 2 1 7 21 11 3 8 13 15 17 5 12
set --local match_anything_transitions_to 3 8 3 3 3 18 8 3 18 3 21 18
set --local state 1
set --local word_index 2
while test $word_index -lt $COMP_CWORD
set --local -- word $COMP_WORDS[$word_index]
if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs
set --local --erase tos
eval $literal_transitions[$state]
if contains -- $word $literals
set --local literal_matched 0
for literal_id in (seq 1 (count $literals))
if test $literals[$literal_id] = $word
set --local index (contains --index -- $literal_id $inputs)
set state $tos[$index]
set word_index (math $word_index + 1)
set literal_matched 1
break
end
end
if test $literal_matched -ne 0
continue
end
end
end
if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state]
set --local index (contains --index -- $state $match_anything_transitions_from)
set state $match_anything_transitions_to[$index]
set word_index (math $word_index + 1)
continue
end
return 1
end
if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs
set --local --erase tos
eval $literal_transitions[$state]
for literal_id in $inputs
if test -n $descriptions[$literal_id]
printf '%s\t%s\n' $literals[$literal_id] $descriptions[$literal_id]
else
printf '%s\n' $literals[$literal_id]
end
end
end
set command_states 17 5 13 11
set command_ids 3 4 2 1
if contains $state $command_states
set --local index (contains --index $state $command_states)
set --local function_id $command_ids[$index]
set --local function_name _hyprctl_$function_id
set --local --erase inputs
set --local --erase tos
$function_name "$COMP_WORDS[$COMP_CWORD]"
end
return 0
end
complete --command hyprctl --no-files --arguments "(_hyprctl)"

148
hyprctl/hyprctl.usage Normal file
View File

@@ -0,0 +1,148 @@
# This is a file feeded to complgen to generate bash/fish/zsh completions
# Repo: https://github.com/adaszko/complgen
# Generate completion scripts: "complgen aot --bash-script hyprctl.bash --fish-script hyprctl.fish --zsh-script hyprctl.zsh ./hyprctl.usage"
hyprctl [<OPTIONS>]... <ARGUMENTS>
<OPTIONS> ::= (-i | --instance) "Specify the Hyprland instance"
| (-j) "Output in JSON format"
| (-r) "Refresh state after issuing the command"
| (--batch) "Execute a batch of commands separated by ;"
;
<WINDOWS> ::= {{{ hyprctl clients | grep class | awk '{print $2}' }}};
<AVAILABLE_PLUGINS> ::= {{{ hyprpm list | grep "Plugin" | awk '{print $4}' }}};
<MONITORS> ::= {{{ hyprctl monitors | grep Monitor | awk '{ print $2 }' }}};
<KEYBOARDS> ::= {{{ hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' }}};
<NOTIFICATION_TYPES> ::= (0) "WARNING"
| (1) "INFO"
| (2) "HINT"
| (3) "ERROR"
| (4) "CONFUSED"
| (5) "OK"
| (-1) "No Icon"
;
<PROPS> ::= (animationstyle)
| (rounding <NUM>)
| (bordersize <NUM>)
| (forcenoblur (0 | 1))
| (forceopaque (0 | 1))
| (forceopaqueoverriden (0 | 1))
| (forceallowsinput (0 | 1))
| (forcenoanims (0 | 1))
| (forcenoborder (0 | 1))
| (forcenodim (0 | 1))
| (forcenoshadow (0 | 1))
| (nofocus (0 | 1))
| (windowdancecompat (0 | 1))
| (nomaxsize (0 | 1))
| (minsize)
| (maxsize)
| (dimaround (0 | 1))
| (keepaspectratio (0 | 1))
| (alphaoverride (0 | 1))
| (alpha)
| (alphainactiveoverride (0 | 1))
| (alphainactive)
| (alphafullscreenoverride (0 | 1))
| (alphafullscreen)
| (activebordercolor)
| (inactivebordercolor)
;
<ARGUMENTS> ::= (activewindow) "Get the active window name and its properties"
| (activeworkspace) "Get the active workspace name and its properties"
| (binds) "List all registered binds"
| (clients) "List all windows with their properties"
| (configerrors) "List all current config parsing errors"
| (cursorpos) "Get the current cursor pos in global layout coordinates"
| (decorations <WINDOWS>) "List all decorations and their info"
| (devices) "List all connected keyboards and mice"
| (dismissnotify <NUM>) "Dismiss all or up to amount of notifications"
| (dispatch <DISPATCHERS>) "Issue a dispatch to call a keybind dispatcher with an arg"
| (getoption) "Get the config option status (values)"
| (globalshortcuts) ""
| (hyprpaper) "Interact with hyprpaper if present"
| (instances) "List all running Hyprland instances and their info"
| (keyword <KEYWORDS>) "Issue a keyword to call a config keyword dynamically"
| (kill) "Get into a kill mode, where you can kill an app by clicking on it"
| (layers) "List the layers"
| (layouts) "List all layouts available (including plugin ones)"
| (monitors [all]) "List active outputs with their properties"
| (notify <NOTIFICATION_TYPES> <NUM>) "Send a notification using the built-in Hyprland notification system"
| (output (create (wayland | x11 | headless | auto) | remove <MONITORS>)) "Allows adding/removing fake outputs to a specific backend"
| (plugin <AVAILABLE_PLUGINS>) "Interact with a plugin"
| (reload) "Force reload the config"
| (rollinglog) "Print tail of the log"
| (setcursor) "Set the cursor theme and reloads the cursor manager"
| (seterror [disable]) "Set the hyprctl error string"
| (setprop <PROPS>) "Set a property of a window"
| (splash) "Print the current random splash"
| (switchxkblayout <KEYBOARDS> (next | prev | <NUM>)) "Set the xkb layout index for a keyboard"
| (systeminfo) "Print system info"
| (version) "Print the Hyprland version: flags, commit and branch of build"
| (workspacerules) "Get the list of defined workspace rules"
| (workspaces) "List all workspaces with their properties"
;
<DISPATCHERS> ::= (exec) "Execute a shell command"
| (execr) "Execute a raw shell command"
| (pass) "Pass the key to a specified window"
| (killactive) "Close the active window"
| (closewindow) "Close a specified window"
| (workspace) "Change the workspace"
| (movetoworkspace) "Move the focused window to a workspace"
| (movetoworkspacesilent) "Move window doesnt switch to the workspace"
| (togglefloating) "Toggle the current window's floating state"
| (setfloating) "Set the current window's floating state to true"
| (settiled) "Set the current window's floating state to false"
| (fullscreen) "Toggle the focused window's fullscreen state"
| (fakefullscreen) "Toggle the focused window's internal fullscreen state"
| (dpms) "Set all monitors' DPMS status"
| (pin) "Pin a window"
| (movefocus) "Move the focus in a direction"
| (movewindow) "Move the active window in a direction or to a monitor"
| (swapwindow) "Swap the active window with another window"
| (centerwindow) "Center the active window"
| (resizeactive) "Resize the active window"
| (moveactive) "Move the active window"
| (resizewindowpixel) "Resize a selected window"
| (movewindowpixel) "Move a selected window"
| (cyclenext) "Focus the next window on a workspace"
| (swapnext) "Swap the focused window with the next window"
| (focuswindow) "Focus the first window matching"
| (focusmonitor) "Focus a monitor"
| (splitratio) "Change the split ratio"
| (toggleopaque) "Toggle the current window to always be opaque"
| (movecursortocorner) "Move the cursor to the corner of the active window"
| (movecursor) "Move the cursor to a specified position"
| (renameworkspace) "Rename a workspace"
| (exit) "Exit the compositor with no questions asked"
| (forcerendererreload) "Force the renderer to reload all resources and outputs"
| (movecurrentworkspacetomonitor) "Move the active workspace to a monitor"
| (focusworkspaceoncurrentmonitor) "Focus the requested workspace"
| (moveworkspacetomonitor) "Move a workspace to a monitor"
| (swapactiveworkspaces) "Swap the active workspaces between two monitors"
| (alterzorder) "Modify the window stack order of the active or specified window"
| (togglespecialworkspace) "Toggle a special workspace on/off"
| (focusurgentorlast) "Focus the urgent window or the last window"
| (togglegroup) "Toggle the current active window into a group"
| (changegroupactive) "Switch to the next window in a group"
| (focuscurrentorlast) "Switch focus from current to previously focused window"
| (lockgroups) "Lock the groups"
| (lockactivegroup) "Lock the focused group"
| (moveintogroup) "Move the active window into a group"
| (moveoutofgroup) "Move the active window out of a group"
| (movewindoworgroup) "Behave as moveintogroup"
| (movegroupwindow) "Swap the active window with the next or previous in a group"
| (denywindowfromgroup) "Prohibit the active window from becoming or being inserted into group"
| (setignoregrouplock) "Temporarily enable or disable binds:ignore_group_lock"
| (global) "Execute a Global Shortcut using the GlobalShortcuts portal"
| (submap) "Change the current mapping group"
;

251
hyprctl/hyprctl.zsh Normal file
View File

@@ -0,0 +1,251 @@
#compdef hyprctl
_hyprctl_cmd_2 () {
hyprctl monitors | grep Monitor | awk '{ print $2 }'
}
_hyprctl_cmd_1 () {
hyprpm list | grep "Plugin" | awk '{print $4}'
}
_hyprctl_cmd_0 () {
hyprctl clients | grep class | awk '{print $2}'
}
_hyprctl_cmd_3 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
}
_hyprctl () {
local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow")
local -A descriptions
descriptions[1]="Focus the next window on a workspace"
descriptions[3]="Get the current cursor pos in global layout coordinates"
descriptions[5]="Rename a workspace"
descriptions[7]="Focus the first window matching"
descriptions[10]="Swap the focused window with the next window"
descriptions[12]="Move the active window"
descriptions[16]="List the layers"
descriptions[18]="List active outputs with their properties"
descriptions[20]="Get into a kill mode, where you can kill an app by clicking on it"
descriptions[21]="Set the current window's floating state to false"
descriptions[22]="ERROR"
descriptions[23]="Focus a monitor"
descriptions[24]="Swap the active window with another window"
descriptions[25]="Move the active window out of a group"
descriptions[26]="Send a notification using the built-in Hyprland notification system"
descriptions[27]="Move the cursor to a specified position"
descriptions[28]="Set the cursor theme and reloads the cursor manager"
descriptions[29]="Set the hyprctl error string"
descriptions[30]="Move the active workspace to a monitor"
descriptions[31]="CONFUSED"
descriptions[34]="Set a property of a window"
descriptions[35]="Specify the Hyprland instance"
descriptions[36]="Toggle the current window's floating state"
descriptions[37]="Get the list of defined workspace rules"
descriptions[38]="Move the focused window to a workspace"
descriptions[40]="Temporarily enable or disable binds:ignore_group_lock"
descriptions[41]="List all workspaces with their properties"
descriptions[42]="Swap the active window with the next or previous in a group"
descriptions[43]="Close a specified window"
descriptions[44]="WARNING"
descriptions[45]="Specify the Hyprland instance"
descriptions[46]="List all registered binds"
descriptions[47]="Move the active window in a direction or to a monitor"
descriptions[48]="Change the split ratio"
descriptions[50]="Prohibit the active window from becoming or being inserted into group"
descriptions[51]="Change the workspace"
descriptions[52]="List all current config parsing errors"
descriptions[53]="Toggle the current active window into a group"
descriptions[54]="Get the config option status (values)"
descriptions[57]="Close the active window"
descriptions[58]="Pass the key to a specified window"
descriptions[59]="List all decorations and their info"
descriptions[60]="List all connected keyboards and mice"
descriptions[61]="Switch focus from current to previously focused window"
descriptions[62]="Change the current mapping group"
descriptions[63]="Execute a Global Shortcut using the GlobalShortcuts portal"
descriptions[65]="Force the renderer to reload all resources and outputs"
descriptions[66]="Move a selected window"
descriptions[68]="Print the Hyprland version: flags, commit and branch of build"
descriptions[69]="Set all monitors' DPMS status"
descriptions[70]="Resize the active window"
descriptions[71]="Move the active window into a group"
descriptions[72]="OK"
descriptions[74]="Set the current window's floating state to true"
descriptions[75]="Print tail of the log"
descriptions[78]="List all layouts available (including plugin ones)"
descriptions[79]="Move a workspace to a monitor"
descriptions[80]="Execute a shell command"
descriptions[82]="Modify the window stack order of the active or specified window"
descriptions[83]="Toggle the focused window's internal fullscreen state"
descriptions[85]="Issue a keyword to call a config keyword dynamically"
descriptions[88]="Pin a window"
descriptions[89]="Allows adding/removing fake outputs to a specific backend"
descriptions[91]="Toggle a special workspace on/off"
descriptions[92]="Toggle the focused window's fullscreen state"
descriptions[93]="Toggle the current window to always be opaque"
descriptions[94]="Focus the requested workspace"
descriptions[96]="Switch to the next window in a group"
descriptions[97]="Output in JSON format"
descriptions[98]="List all running Hyprland instances and their info"
descriptions[99]="Execute a raw shell command"
descriptions[100]="Exit the compositor with no questions asked"
descriptions[101]="List all windows with their properties"
descriptions[103]="Execute a batch of commands separated by ;"
descriptions[104]="Dismiss all or up to amount of notifications"
descriptions[106]="Set the xkb layout index for a keyboard"
descriptions[107]="Move window doesnt switch to the workspace"
descriptions[108]="Behave as moveintogroup"
descriptions[109]="Refresh state after issuing the command"
descriptions[110]="Move the focus in a direction"
descriptions[111]="Focus the urgent window or the last window"
descriptions[113]="Get the active workspace name and its properties"
descriptions[114]="Issue a dispatch to call a keybind dispatcher with an arg"
descriptions[116]="Center the active window"
descriptions[117]="HINT"
descriptions[118]="Interact with hyprpaper if present"
descriptions[119]="No Icon"
descriptions[120]="Force reload the config"
descriptions[122]="Print system info"
descriptions[123]="Interact with a plugin"
descriptions[125]="Get the active window name and its properties"
descriptions[126]="Swap the active workspaces between two monitors"
descriptions[127]="Print the current random splash"
descriptions[129]="Lock the focused group"
descriptions[132]="Lock the groups"
descriptions[133]="Move the cursor to the corner of the active window"
descriptions[136]="INFO"
descriptions[137]="Resize a selected window"
local -A literal_transitions
literal_transitions[1]="([104]=2 [75]=3 [34]=4 [2]=3 [3]=3 [78]=3 [106]=5 [37]=3 [109]=6 [41]=3 [46]=3 [113]=3 [85]=7 [114]=9 [52]=3 [54]=3 [89]=10 [118]=3 [120]=3 [122]=3 [16]=3 [59]=11 [60]=3 [18]=12 [123]=13 [20]=3 [125]=3 [127]=3 [26]=14 [68]=3 [97]=6 [98]=3 [28]=3 [29]=15 [101]=3 [103]=6)"
literal_transitions[4]="([73]=19 [14]=3 [33]=19 [55]=19 [56]=19 [90]=19 [105]=3 [121]=3 [77]=2 [17]=3 [124]=19 [4]=2 [6]=3 [64]=19 [128]=3 [130]=19 [81]=19 [131]=19 [84]=19 [32]=19 [49]=3 [13]=3 [86]=19 [11]=19 [87]=19 [138]=19)"
literal_transitions[8]="([104]=2 [75]=3 [34]=4 [2]=3 [3]=3 [78]=3 [106]=5 [37]=3 [41]=3 [46]=3 [113]=3 [85]=7 [114]=9 [52]=3 [54]=3 [89]=10 [118]=3 [120]=3 [122]=3 [16]=3 [59]=11 [60]=3 [18]=12 [123]=13 [20]=3 [125]=3 [127]=3 [26]=14 [68]=3 [98]=3 [28]=3 [29]=15 [101]=3)"
literal_transitions[9]="([129]=3 [132]=3 [1]=3 [74]=3 [36]=3 [107]=3 [38]=3 [108]=3 [5]=3 [79]=3 [40]=3 [80]=3 [111]=3 [7]=3 [42]=3 [43]=3 [82]=3 [83]=3 [47]=3 [48]=3 [10]=3 [110]=3 [51]=3 [53]=3 [12]=3 [116]=3 [88]=3 [50]=3 [57]=3 [91]=3 [58]=3 [92]=3 [93]=3 [61]=3 [62]=3 [126]=3 [94]=3 [63]=3 [21]=3 [96]=3 [23]=3 [24]=3 [65]=3 [66]=3 [25]=3 [133]=3 [27]=3 [69]=3 [99]=3 [70]=3 [30]=3 [137]=3 [71]=3 [100]=3)"
literal_transitions[10]="([115]=16 [112]=17)"
literal_transitions[12]="([102]=3)"
literal_transitions[14]="([22]=2 [117]=2 [31]=2 [136]=2 [119]=2 [44]=2 [72]=2)"
literal_transitions[15]="([39]=3)"
literal_transitions[16]="([9]=3 [67]=3 [15]=3 [134]=3)"
literal_transitions[18]="([76]=20)"
literal_transitions[19]="([19]=3 [8]=3)"
literal_transitions[20]="([35]=6 [45]=6)"
literal_transitions[21]="([135]=3 [95]=3)"
local -A match_anything_transitions
match_anything_transitions=([2]=3 [1]=8 [7]=3 [21]=3 [11]=3 [3]=18 [8]=8 [13]=3 [15]=18 [17]=3 [5]=21 [12]=18)
declare -A subword_transitions
local state=1
local word_index=2
while [[ $word_index -lt $CURRENT ]]; do
if [[ -v "literal_transitions[$state]" ]]; then
local -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
local word=${words[$word_index]}
local word_matched=0
for ((literal_id = 1; literal_id <= $#literals; literal_id++)); do
if [[ ${literals[$literal_id]} = "$word" ]]; then
if [[ -v "state_transitions[$literal_id]" ]]; then
state=${state_transitions[$literal_id]}
word_index=$((word_index + 1))
word_matched=1
break
fi
fi
done
if [[ $word_matched -ne 0 ]]; then
continue
fi
fi
if [[ -v "match_anything_transitions[$state]" ]]; then
state=${match_anything_transitions[$state]}
word_index=$((word_index + 1))
continue
fi
return 1
done
completions_no_description_trailing_space=()
completions_no_description_no_trailing_space=()
completions_trailing_space=()
suffixes_trailing_space=()
descriptions_trailing_space=()
completions_no_trailing_space=()
suffixes_no_trailing_space=()
descriptions_no_trailing_space=()
if [[ -v "literal_transitions[$state]" ]]; then
local -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
for literal_id in ${(k)state_transitions}; do
if [[ -v "descriptions[$literal_id]" ]]; then
completions_trailing_space+=("${literals[$literal_id]}")
suffixes_trailing_space+=("${literals[$literal_id]}")
descriptions_trailing_space+=("${descriptions[$literal_id]}")
else
completions_no_description_trailing_space+=("${literals[$literal_id]}")
fi
done
fi
local -A commands=([17]=2 [5]=3 [13]=1 [11]=0)
if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]}
local output=$(_hyprctl_cmd_${command_id} "${words[$CURRENT]}")
local -a command_completions=("${(@f)output}")
for line in ${command_completions[@]}; do
local parts=(${(@s: :)line})
if [[ -v "parts[2]" ]]; then
completions_trailing_space+=("${parts[1]}")
suffixes_trailing_space+=("${parts[1]}")
descriptions_trailing_space+=("${parts[2]}")
else
completions_no_description_trailing_space+=("${parts[1]}")
fi
done
fi
local maxlen=0
for suffix in ${suffixes_trailing_space[@]}; do
if [[ ${#suffix} -gt $maxlen ]]; then
maxlen=${#suffix}
fi
done
for suffix in ${suffixes_no_trailing_space[@]}; do
if [[ ${#suffix} -gt $maxlen ]]; then
maxlen=${#suffix}
fi
done
for ((i = 1; i <= $#suffixes_trailing_space; i++)); do
if [[ -z ${descriptions_trailing_space[$i]} ]]; then
descriptions_trailing_space[$i]="${(r($maxlen)( ))${suffixes_trailing_space[$i]}}"
else
descriptions_trailing_space[$i]="${(r($maxlen)( ))${suffixes_trailing_space[$i]}} -- ${descriptions_trailing_space[$i]}"
fi
done
for ((i = 1; i <= $#suffixes_no_trailing_space; i++)); do
if [[ -z ${descriptions_no_trailing_space[$i]} ]]; then
descriptions_no_trailing_space[$i]="${(r($maxlen)( ))${suffixes_no_trailing_space[$i]}}"
else
descriptions_no_trailing_space[$i]="${(r($maxlen)( ))${suffixes_no_trailing_space[$i]}} -- ${descriptions_no_trailing_space[$i]}"
fi
done
compadd -Q -a completions_no_description_trailing_space
compadd -Q -S ' ' -a completions_no_description_no_trailing_space
compadd -l -Q -a -d descriptions_trailing_space completions_trailing_space
compadd -l -Q -S '' -a -d descriptions_no_trailing_space completions_no_trailing_space
return 0
}
compdef _hyprctl hyprctl

View File

@@ -8,6 +8,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <pwd.h>
#include <unistd.h>
#include <ranges>
#include <algorithm>
@@ -24,49 +25,7 @@
#include <stdarg.h>
#include <regex>
const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args]
commands:
activewindow
activeworkspace
binds
clients
configerrors
cursorpos
decorations
devices
dismissnotify
dispatch
getoption
globalshortcuts
hyprpaper
instances
keyword
kill
layers
layouts
monitors
notify
output
plugin
reload
rollinglog
setcursor
seterror
setprop
splash
switchxkblayout
systeminfo
version
workspacerules
workspaces
flags:
-j -> output in JSON
-r -> refresh state after issuing command (e.g. for updating variables)
--batch -> execute a batch of commands, separated by ';'
--instance (-i) -> use a specific instance. Can be either signature or index in hyprctl instances (0, 1, etc)
)#";
#include "Strings.hpp"
#define PAD
@@ -80,24 +39,34 @@ struct SInstanceData {
bool valid = true;
};
std::string getRuntimeDir() {
const auto XDG = getenv("XDG_RUNTIME_DIR");
if (!XDG) {
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
return "/run/user/" + USERID + "/hypr";
}
return std::string{XDG} + "/hypr";
}
std::vector<SInstanceData> instances() {
std::vector<SInstanceData> result;
for (const auto& el : std::filesystem::directory_iterator("/tmp/hypr")) {
if (el.is_directory() || !el.path().string().ends_with(".lock"))
for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
continue;
// read lock
SInstanceData* data = &result.emplace_back();
data->id = el.path().string();
data->id = data->id.substr(data->id.find_last_of('/') + 1, data->id.find(".lock") - data->id.find_last_of('/') - 1);
data->id = el.path().filename().string();
try {
data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1));
data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1, data->id.find_last_of('_') - (data->id.find_first_of('_') + 1)));
} catch (std::exception& e) { continue; }
// read file
std::ifstream ifs(el.path().string());
std::ifstream ifs(el.path().string() + "/hyprland.lock");
int i = 0;
for (std::string line; std::getline(ifs, line); ++i) {
@@ -141,10 +110,12 @@ void request(std::string arg, int minArgs = 0) {
return;
}
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
std::string socketPath = "/tmp/hypr/" + instanceSignature + "/.socket.sock";
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.socket.sock";
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
@@ -202,7 +173,9 @@ void requestHyprpaper(std::string arg) {
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
std::string socketPath = "/tmp/hypr/" + instanceSignature + "/.hyprpaper.sock";
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.hyprpaper.sock";
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
@@ -295,7 +268,7 @@ int main(int argc, char** argv) {
bool parseArgs = true;
if (argc < 2) {
printf("%s\n", USAGE.c_str());
std::cout << USAGE << std::endl;
return 1;
}
@@ -326,13 +299,33 @@ int main(int argc, char** argv) {
++i;
if (i >= ARGS.size()) {
printf("%s\n", USAGE.c_str());
std::cout << USAGE << std::endl;
return 1;
}
overrideInstance = ARGS[i];
} else if (ARGS[i] == "--help") {
const std::string& cmd = ARGS[0];
if (cmd == "hyprpaper") {
std::cout << HYPRPAPER_HELP << std::endl;
} else if (cmd == "notify") {
std::cout << NOTIFY_HELP << std::endl;
} else if (cmd == "output") {
std::cout << OUTPUT_HELP << std::endl;
} else if (cmd == "plugin") {
std::cout << PLUGIN_HELP << std::endl;
} else if (cmd == "setprop") {
std::cout << SETPROP_HELP << std::endl;
} else if (cmd == "switchxkblayout") {
std::cout << SWITCHXKBLAYOUT_HELP << std::endl;
} else {
printf("%s\n", USAGE.c_str());
std::cout << USAGE << std::endl;
}
return 1;
} else {
std::cout << USAGE << std::endl;
return 1;
}
@@ -343,7 +336,7 @@ int main(int argc, char** argv) {
}
if (fullRequest.empty()) {
printf("%s\n", USAGE.c_str());
std::cout << USAGE << std::endl;
return 1;
}
@@ -415,11 +408,11 @@ int main(int argc, char** argv) {
else if (fullRequest.contains("/decorations"))
request(fullRequest, 1);
else if (fullRequest.contains("/--help"))
printf("%s", USAGE.c_str());
std::cout << USAGE << std::endl;
else {
request(fullRequest);
}
printf("\n");
std::cout << std::endl;
return exitStatus;
}

View File

@@ -1,3 +1,7 @@
executable('hyprctl', 'main.cpp',
install: true
)
install_data('hyprctl.bash', install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), install_tag: 'runtime', rename: 'hyprctl')
install_data('hyprctl.fish', install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), install_tag: 'runtime')
install_data('hyprctl.zsh', install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), install_tag: 'runtime', rename: '_hyprctl')

View File

@@ -5,4 +5,4 @@ Name: Hyprland
URL: https://github.com/hyprwm/Hyprland
Description: Hyprland header files
Version: @HYPRLAND_VERSION@
Cflags: -I"${includedir}/hyprland/protocols" -I"${includedir}/hyprland/wlroots" -I"${includedir}"
Cflags: -I"${includedir}/hyprland/protocols" -I"${includedir}/hyprland/wlroots-hyprland" -I"${includedir}"

106
hyprpm/hyprpm.bash Normal file
View File

@@ -0,0 +1,106 @@
_hyprpm_cmd_0 () {
hyprpm list | grep Plugin | awk '{print $4}'
}
_hyprpm () {
if [[ $(type -t _get_comp_words_by_ref) != function ]]; then
echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed
return 1
fi
local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
local -a literals=("-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f")
declare -A literal_transitions
literal_transitions[0]="([9]=6 [2]=2 [7]=6 [8]=6 [4]=6 [10]=2 [11]=3 [5]=2 [13]=6 [3]=3 [14]=2 [15]=6 [6]=2)"
literal_transitions[1]="([10]=2 [11]=3 [3]=3 [2]=2 [14]=2 [5]=2 [6]=2)"
literal_transitions[4]="([1]=5)"
literal_transitions[5]="([0]=6 [12]=6)"
declare -A match_anything_transitions
match_anything_transitions=([3]=2 [2]=4 [0]=1 [1]=1)
declare -A subword_transitions
local state=0
local word_index=1
while [[ $word_index -lt $cword ]]; do
local word=${words[$word_index]}
if [[ -v "literal_transitions[$state]" ]]; then
declare -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
local word_matched=0
for literal_id in $(seq 0 $((${#literals[@]} - 1))); do
if [[ ${literals[$literal_id]} = "$word" ]]; then
if [[ -v "state_transitions[$literal_id]" ]]; then
state=${state_transitions[$literal_id]}
word_index=$((word_index + 1))
word_matched=1
break
fi
fi
done
if [[ $word_matched -ne 0 ]]; then
continue
fi
fi
if [[ -v "match_anything_transitions[$state]" ]]; then
state=${match_anything_transitions[$state]}
word_index=$((word_index + 1))
continue
fi
return 1
done
local prefix="${words[$cword]}"
local shortest_suffix="$word"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate="${word##*$char}"
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$word" ]]; then
local superfluous_prefix=${word%$shortest_suffix}
fi
if [[ -v "literal_transitions[$state]" ]]; then
local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions
eval "state_transitions=$state_transitions_initializer"
for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"}
COMPREPLY+=("$completion ")
fi
done
fi
declare -A commands
commands=([3]=0)
if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]}
local completions=()
mapfile -t completions < <(_hyprpm_cmd_${command_id} "$prefix" | cut -f1)
for item in "${completions[@]}"; do
if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item")
fi
done
fi
return 0
}
complete -o nospace -F _hyprpm hyprpm

109
hyprpm/hyprpm.fish Normal file
View File

@@ -0,0 +1,109 @@
function _hyprpm_1
set 1 $argv[1]
hyprpm list | grep Plugin | awk '{print $4}'
end
function _hyprpm
set COMP_LINE (commandline --cut-at-cursor)
set COMP_WORDS
echo $COMP_LINE | read --tokenize --array COMP_WORDS
if string match --quiet --regex '.*\s$' $COMP_LINE
set COMP_CWORD (math (count $COMP_WORDS) + 1)
else
set COMP_CWORD (count $COMP_WORDS)
end
set --local literals "-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f"
set --local descriptions
set descriptions[1] "Send a hyprland notification for important events (e.g. load fail)"
set descriptions[3] "List all installed plugins"
set descriptions[4] "Unload a plugin"
set descriptions[5] "Show help menu"
set descriptions[6] "Check and update all plugins if needed"
set descriptions[7] "Install a new plugin repository from git"
set descriptions[8] "Enable too much loggin"
set descriptions[9] "Enable too much loggin"
set descriptions[10] "Force an operation ignoring checks (e.g. update -f)"
set descriptions[11] "Remove a plugin repository"
set descriptions[12] "Load a plugin"
set descriptions[13] "Send a hyprland notification for important events (e.g. load fail)"
set descriptions[14] "Show help menu"
set descriptions[15] "Reload all plugins"
set descriptions[16] "Force an operation ignoring checks (e.g. update -f)"
set --local literal_transitions
set literal_transitions[1] "set inputs 10 3 8 9 5 11 12 6 14 4 15 16 7; set tos 7 3 7 7 7 3 4 3 7 4 3 7 3"
set literal_transitions[2] "set inputs 11 12 4 3 15 6 7; set tos 3 4 4 3 3 3 3"
set literal_transitions[5] "set inputs 2; set tos 6"
set literal_transitions[6] "set inputs 1 13; set tos 7 7"
set --local match_anything_transitions_from 4 3 1 2
set --local match_anything_transitions_to 3 5 2 2
set --local state 1
set --local word_index 2
while test $word_index -lt $COMP_CWORD
set --local -- word $COMP_WORDS[$word_index]
if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs
set --local --erase tos
eval $literal_transitions[$state]
if contains -- $word $literals
set --local literal_matched 0
for literal_id in (seq 1 (count $literals))
if test $literals[$literal_id] = $word
set --local index (contains --index -- $literal_id $inputs)
set state $tos[$index]
set word_index (math $word_index + 1)
set literal_matched 1
break
end
end
if test $literal_matched -ne 0
continue
end
end
end
if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state]
set --local index (contains --index -- $state $match_anything_transitions_from)
set state $match_anything_transitions_to[$index]
set word_index (math $word_index + 1)
continue
end
return 1
end
if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs
set --local --erase tos
eval $literal_transitions[$state]
for literal_id in $inputs
if test -n $descriptions[$literal_id]
printf '%s\t%s\n' $literals[$literal_id] $descriptions[$literal_id]
else
printf '%s\n' $literals[$literal_id]
end
end
end
set command_states 4
set command_ids 1
if contains $state $command_states
set --local index (contains --index $state $command_states)
set --local function_id $command_ids[$index]
set --local function_name _hyprpm_$function_id
set --local --erase inputs
set --local --erase tos
$function_name "$COMP_WORDS[$COMP_CWORD]"
end
return 0
end
complete --command hyprpm --no-files --arguments "(_hyprpm)"

19
hyprpm/hyprpm.usage Normal file
View File

@@ -0,0 +1,19 @@
hyprpm [<FLAGS>]... <ARGUMENT>
<FLAGS> ::= (--notify | -n) "Send a hyprland notification for important events (e.g. load fail)"
| (--help | -h) "Show help menu"
| (--verbose | -v) "Enable too much loggin"
| (--force | -f) "Force an operation ignoring checks (e.g. update -f)"
;
<ARGUMENT> ::= (add) "Install a new plugin repository from git"
| (remove) "Remove a plugin repository"
| (update) "Check and update all plugins if needed"
| (list) "List all installed plugins"
| (enable <PLUGINS>) "Load a plugin"
| (disable <PLUGINS>) "Unload a plugin"
| (reload) "Reload all plugins"
;
<PLUGINS> ::= {{{ hyprpm list | grep Plugin | awk '{print $4}' }}};

151
hyprpm/hyprpm.zsh Normal file
View File

@@ -0,0 +1,151 @@
#compdef hyprpm
_hyprpm_cmd_0 () {
hyprpm list | grep Plugin | awk '{print $4}'
}
_hyprpm () {
local -a literals=("-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f")
local -A descriptions
descriptions[1]="Send a hyprland notification for important events (e.g. load fail)"
descriptions[3]="List all installed plugins"
descriptions[4]="Unload a plugin"
descriptions[5]="Show help menu"
descriptions[6]="Check and update all plugins if needed"
descriptions[7]="Install a new plugin repository from git"
descriptions[8]="Enable too much loggin"
descriptions[9]="Enable too much loggin"
descriptions[10]="Force an operation ignoring checks (e.g. update -f)"
descriptions[11]="Remove a plugin repository"
descriptions[12]="Load a plugin"
descriptions[13]="Send a hyprland notification for important events (e.g. load fail)"
descriptions[14]="Show help menu"
descriptions[15]="Reload all plugins"
descriptions[16]="Force an operation ignoring checks (e.g. update -f)"
local -A literal_transitions
literal_transitions[1]="([10]=7 [3]=3 [8]=7 [9]=7 [5]=7 [11]=3 [12]=4 [6]=3 [14]=7 [4]=4 [15]=3 [16]=7 [7]=3)"
literal_transitions[2]="([11]=3 [12]=4 [4]=4 [3]=3 [15]=3 [6]=3 [7]=3)"
literal_transitions[5]="([2]=6)"
literal_transitions[6]="([1]=7 [13]=7)"
local -A match_anything_transitions
match_anything_transitions=([4]=3 [3]=5 [1]=2 [2]=2)
declare -A subword_transitions
local state=1
local word_index=2
while [[ $word_index -lt $CURRENT ]]; do
if [[ -v "literal_transitions[$state]" ]]; then
local -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
local word=${words[$word_index]}
local word_matched=0
for ((literal_id = 1; literal_id <= $#literals; literal_id++)); do
if [[ ${literals[$literal_id]} = "$word" ]]; then
if [[ -v "state_transitions[$literal_id]" ]]; then
state=${state_transitions[$literal_id]}
word_index=$((word_index + 1))
word_matched=1
break
fi
fi
done
if [[ $word_matched -ne 0 ]]; then
continue
fi
fi
if [[ -v "match_anything_transitions[$state]" ]]; then
state=${match_anything_transitions[$state]}
word_index=$((word_index + 1))
continue
fi
return 1
done
completions_no_description_trailing_space=()
completions_no_description_no_trailing_space=()
completions_trailing_space=()
suffixes_trailing_space=()
descriptions_trailing_space=()
completions_no_trailing_space=()
suffixes_no_trailing_space=()
descriptions_no_trailing_space=()
if [[ -v "literal_transitions[$state]" ]]; then
local -A state_transitions
eval "state_transitions=${literal_transitions[$state]}"
for literal_id in ${(k)state_transitions}; do
if [[ -v "descriptions[$literal_id]" ]]; then
completions_trailing_space+=("${literals[$literal_id]}")
suffixes_trailing_space+=("${literals[$literal_id]}")
descriptions_trailing_space+=("${descriptions[$literal_id]}")
else
completions_no_description_trailing_space+=("${literals[$literal_id]}")
fi
done
fi
local -A commands=([4]=0)
if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]}
local output=$(_hyprpm_cmd_${command_id} "${words[$CURRENT]}")
local -a command_completions=("${(@f)output}")
for line in ${command_completions[@]}; do
local parts=(${(@s: :)line})
if [[ -v "parts[2]" ]]; then
completions_trailing_space+=("${parts[1]}")
suffixes_trailing_space+=("${parts[1]}")
descriptions_trailing_space+=("${parts[2]}")
else
completions_no_description_trailing_space+=("${parts[1]}")
fi
done
fi
local maxlen=0
for suffix in ${suffixes_trailing_space[@]}; do
if [[ ${#suffix} -gt $maxlen ]]; then
maxlen=${#suffix}
fi
done
for suffix in ${suffixes_no_trailing_space[@]}; do
if [[ ${#suffix} -gt $maxlen ]]; then
maxlen=${#suffix}
fi
done
for ((i = 1; i <= $#suffixes_trailing_space; i++)); do
if [[ -z ${descriptions_trailing_space[$i]} ]]; then
descriptions_trailing_space[$i]="${(r($maxlen)( ))${suffixes_trailing_space[$i]}}"
else
descriptions_trailing_space[$i]="${(r($maxlen)( ))${suffixes_trailing_space[$i]}} -- ${descriptions_trailing_space[$i]}"
fi
done
for ((i = 1; i <= $#suffixes_no_trailing_space; i++)); do
if [[ -z ${descriptions_no_trailing_space[$i]} ]]; then
descriptions_no_trailing_space[$i]="${(r($maxlen)( ))${suffixes_no_trailing_space[$i]}}"
else
descriptions_no_trailing_space[$i]="${(r($maxlen)( ))${suffixes_no_trailing_space[$i]}} -- ${descriptions_no_trailing_space[$i]}"
fi
done
compadd -Q -a completions_no_description_trailing_space
compadd -Q -S ' ' -a completions_no_description_no_trailing_space
compadd -l -Q -a -d descriptions_trailing_space completions_trailing_space
compadd -l -Q -S '' -a -d descriptions_no_trailing_space completions_no_trailing_space
return 0
}
if [[ $ZSH_EVAL_CONTEXT =~ :file$ ]]; then
compdef _hyprpm hyprpm
else
_hyprpm
fi

View File

@@ -75,6 +75,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.output = manifest[plugin.name]["output"].value_or("?");
plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0);
auto authors = manifest[plugin.name]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {

View File

@@ -19,6 +19,7 @@ class CManifest {
std::vector<std::string> authors;
std::vector<std::string> buildSteps;
std::string output;
int since = 0;
bool failed = false;
};

View File

@@ -12,6 +12,11 @@
#include <algorithm>
#include <format>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <unistd.h>
#include <toml++/toml.hpp>
static std::string removeBeginEndSpacesTabs(std::string str) {
@@ -70,16 +75,52 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << "\n";
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
hldate = hldate.substr(0, hldate.find("\n"));
ver = SHyprlandVersion{hlbranch, hlcommit};
std::string hlcommits;
if (HLVERCALL.contains("commits:")) {
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
hlcommits = hlcommits.substr(0, hlcommits.find(" "));
}
int commits = 0;
try {
commits = std::stoi(hlcommits);
} catch (...) { ; }
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << " on " << hldate << ", commits " << commits << "\n";
ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
return ver;
}
bool CPluginManager::createSafeDirectory(const std::string& path) {
if (path.empty() || !path.starts_with("/tmp"))
return false;
if (std::filesystem::exists(path))
std::filesystem::remove_all(path);
if (std::filesystem::exists(path))
return false;
if (mkdir(path.c_str(), S_IRWXU) < 0)
return false;
return true;
}
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
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";
return false;
}
if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
return false;
@@ -117,24 +158,31 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
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";
return false;
}
if (std::filesystem::exists("/tmp/hyprpm/new")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old plugin repo build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/new");
const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
if (!createSafeDirectory(m_szWorkingPluginDirectory)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for repo\n";
return false;
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " new");
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " " + USERNAME);
if (!std::filesystem::exists("/tmp/hyprpm/new")) {
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";
return false;
}
if (!rev.empty()) {
std::string ret = execAndGet("git -C /tmp/hyprpm/new reset --hard --recurse-submodules " + rev);
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";
return false;
@@ -148,12 +196,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/new/hyprpm.toml")) {
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/new/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/new/hyprload.toml")) {
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");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/new/hyprload.toml");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
}
if (!pManifest) {
@@ -192,7 +240,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd /tmp/hyprpm/new/ && git reset --hard --recurse-submodules " + plugin);
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
}
}
@@ -214,21 +262,29 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
for (auto& p : pManifest->m_vPlugins) {
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");
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd /tmp/hyprpm/new && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
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";
if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " failed to build.\n");
if (!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");
p.failed = true;
continue;
}
@@ -242,7 +298,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
// add repo toml to DataState
SPluginRepository repo;
std::string repohash = execAndGet("cd /tmp/hyprpm/new/ && git rev-parse HEAD");
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
@@ -250,7 +306,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
repo.rev = rev;
repo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false, p.failed});
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
}
DataState::addNewPluginRepo(repo);
@@ -263,7 +319,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::cout << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/new");
std::filesystem::remove_all(m_szWorkingPluginDirectory);
return true;
}
@@ -297,7 +353,7 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_MISSING;
// find headers commit
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkg-config --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
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/"))
@@ -315,7 +371,7 @@ eHeadersErrors CPluginManager::headersValid() {
else
headers = "";
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots"))
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland"))
continue;
verHeader = removeBeginEndSpacesTabs(PATH.substr(2)) + "/hyprland/src/version.h";
@@ -355,6 +411,11 @@ bool CPluginManager::updateHeaders(bool force) {
const auto HLVER = getHyprlandVersion();
if (!hasDeps()) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio\n";
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);
@@ -371,16 +432,31 @@ bool CPluginManager::updateHeaders(bool force) {
progress.m_szCurrentMessage = "Cloning the hyprland repository";
progress.print();
if (std::filesystem::exists("/tmp/hyprpm/hyprland")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old hyprland source files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
const std::string USERNAME = getpwuid(getuid())->pw_name;
const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME;
if (!createSafeDirectory(WORKINGDIR)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for hl\n";
return false;
}
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment.");
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland");
// let us give a bit of leg-room for shallowing
// due to timezones, etc.
const std::string SHALLOW_DATE = removeBeginEndSpacesTabs(HLVER.date).empty() ? "" : execAndGet("date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
if (!std::filesystem::exists("/tmp/hyprpm/hyprland")) {
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + " --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);
}
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the hyprland repository. shell returned:\n" << ret << "\n";
return false;
}
@@ -390,11 +466,15 @@ bool CPluginManager::updateHeaders(bool force) {
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
ret =
execAndGet("cd /tmp/hyprpm/hyprland && git checkout " + HLVER.branch + " 2>&1 && git submodule update --init 2>&1 && git reset --hard --recurse-submodules " + HLVER.hash);
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.branch + " 2>&1");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned: " + ret);
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "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(std::string{Colors::GREEN} + "" + Colors::RESET + " checked out to running ver");
progress.m_iSteps = 3;
@@ -406,14 +486,13 @@ bool CPluginManager::updateHeaders(bool force) {
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath());
ret = execAndGet(
std::format("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja",
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);
// le hack. Wlroots has to generate its build/include
ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build");
ret = execAndGet("cd " + WORKINGDIR + "/subprojects/wlroots-hyprland && meson setup -Drenderers=gles2 -Dexamples=false build");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret);
@@ -422,12 +501,8 @@ bool CPluginManager::updateHeaders(bool force) {
progress.m_szCurrentMessage = "Installing sources";
progress.print();
// progress.printMessageAbove(
// std::string{Colors::YELLOW} + "!" + Colors::RESET +
// " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running.");
std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" /tmp/hyprpm/hyprland/Makefile && cd /tmp/hyprpm/hyprland && make installheaders",
DataState::getHeadersPath());
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);
@@ -437,7 +512,7 @@ bool CPluginManager::updateHeaders(bool force) {
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) {
@@ -484,6 +559,9 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Updating repositories";
progress.print();
const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
for (auto& repo : REPOS) {
bool update = forceUpdateAll;
@@ -493,16 +571,13 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(std::string{Colors::RESET} + " → checking for updates for " + repo.name);
if (std::filesystem::exists("/tmp/hyprpm/update")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old update build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/update");
}
createSafeDirectory(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + repo.url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " update");
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " " + USERNAME);
if (!std::filesystem::exists("/tmp/hyprpm/update")) {
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not clone repo: shell returned:\n" + ret;
return false;
}
@@ -510,7 +585,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!repo.rev.empty()) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Plugin has revision set, resetting: " + repo.rev);
std::string ret = execAndGet("git -C /tmp/hyprpm reset --hard --recurse-submodules " + repo.rev);
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
if (ret.compare(0, 6, "fatal:") == 0) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not check out revision " + repo.rev + ": shell returned:\n" + ret;
return false;
@@ -519,7 +594,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!update) {
// check if git has updates
std::string hash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
std::string hash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
if (!hash.empty())
hash.pop_back();
@@ -527,7 +602,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
}
if (!update) {
std::filesystem::remove_all("/tmp/hyprpm/update");
std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " is up-to-date.");
progress.m_iSteps++;
progress.print();
@@ -543,12 +618,12 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/update/hyprpm.toml")) {
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/update/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/update/hyprload.toml")) {
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");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/update/hyprload.toml");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
}
if (!pManifest) {
@@ -572,53 +647,59 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd /tmp/hyprpm/update/ && git reset --hard --recurse-submodules " + plugin);
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
}
}
bool failed = false;
for (auto& p : pManifest->m_vPlugins) {
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");
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd /tmp/hyprpm/update && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
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";
if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n";
failed = true;
break;
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";
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
}
if (failed)
continue;
// add repo toml to DataState
SPluginRepository newrepo = repo;
newrepo.plugins.clear();
execAndGet(
"cd /tmp/hyprpm/update/ && git pull --recurse-submodules && git reset --hard --recurse-submodules"); // repo hash in the state.toml has to match head and not any pin
std::string repohash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
execAndGet("cd " + m_szWorkingPluginDirectory +
" && git pull --recurse-submodules && git reset --hard --recurse-submodules"); // repo hash in the state.toml has to match head and not any pin
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
newrepo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
newrepo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/update/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
}
DataState::removePluginRepo(newrepo.name);
DataState::addNewPluginRepo(newrepo);
std::filesystem::remove_all("/tmp/hyprpm/update");
std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
}
@@ -792,3 +873,13 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
}
bool CPluginManager::hasDeps() {
std::vector<std::string> deps = {"meson", "cpio", "cmake"};
for (auto& d : deps) {
if (!execAndGet("which " + d + " 2>&1").contains("/"))
return false;
}
return true;
}

View File

@@ -32,6 +32,8 @@ enum ePluginLoadStateReturn {
struct SHyprlandVersion {
std::string branch;
std::string hash;
std::string date;
int commits = 0;
};
class CPluginManager {
@@ -54,10 +56,17 @@ class CPluginManager {
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
bool hasDeps();
bool m_bVerbose = false;
// will delete recursively if exists!!
bool createSafeDirectory(const std::string& path);
private:
std::string headerError(const eHeadersErrors err);
std::string m_szWorkingPluginDirectory = "";
};
inline std::unique_ptr<CPluginManager> g_pPluginManager;

View File

@@ -8,3 +8,7 @@ executable('hyprpm', src,
],
install : true
)
install_data('../hyprpm.bash', install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), install_tag: 'runtime', rename: 'hyprpm')
install_data('../hyprpm.fish', install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), install_tag: 'runtime')
install_data('../hyprpm.zsh', install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), install_tag: 'runtime', rename: '_hyprpm')

View File

@@ -33,7 +33,7 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
wlroots = subproject('wlroots-hyprland', default_options: ['examples=false', 'renderers=gles2'])
have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
@@ -51,14 +51,10 @@ if not have_xwayland
endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
systemd_dep = dependency('libsystemd', required: get_option('systemd'))
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
if get_option('systemd').enabled()
if systemd_dep.found()
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
else
error('Cannot enable systemd in Hyprland: libsystemd was not found')
endif
endif
if get_option('legacy_renderer').enabled()

View File

@@ -2,6 +2,7 @@
lib,
stdenv,
pkg-config,
pkgconf,
makeWrapper,
meson,
ninja,
@@ -11,11 +12,13 @@
hyprcursor,
hyprland-protocols,
hyprlang,
hyprwayland-scanner,
jq,
libGL,
libdrm,
libexecinfo,
libinput,
libuuid,
libxcb,
libxkbcommon,
mesa,
@@ -27,7 +30,7 @@
wayland,
wayland-protocols,
wayland-scanner,
wlroots,
wlroots-hyprland,
xcbutilwm,
xwayland,
debug ? false,
@@ -46,7 +49,7 @@
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert lib.assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland"; let
wlr = wlroots.override {inherit enableXWayland;};
wlr = wlroots-hyprland.override {inherit enableXWayland;};
in
stdenv.mkDerivation {
pname = "hyprland${lib.optionalString debug "-debug"}";
@@ -85,6 +88,7 @@ in
'';
nativeBuildInputs = [
hyprwayland-scanner
jq
makeWrapper
meson
@@ -100,7 +104,8 @@ in
];
buildInputs =
[
wlr.buildInputs
++ [
cairo
git
hyprcursor.dev
@@ -109,6 +114,7 @@ in
libdrm
libGL
libinput
libuuid
libxkbcommon
mesa
pango
@@ -142,9 +148,9 @@ in
${lib.optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [
stdenv.cc
binutils
pciutils
pkgconf
]}
''}
'';

View File

@@ -24,8 +24,10 @@ in {
inputs.hyprcursor.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprwayland-scanner.overlays.default
self.overlays.wlroots-hyprland
self.overlays.udis86
self.overlays.wayland-protocols
# Hyprland packages themselves
(final: prev: let
date = mkDate (self.lastModifiedDate or "19700101");
@@ -34,8 +36,8 @@ in {
stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
commit = self.rev or "";
wlroots = final.wlroots-hyprland; # explicit override until decided on breaking change of the name
udis86 = final.udis86-hyprland; # explicit override until decided on breaking change of the name
inherit (final) wlroots-hyprland; # explicit override until decided on breaking change of the name
inherit date;
};
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
@@ -75,4 +77,14 @@ in {
src = inputs.wlroots;
};
};
wayland-protocols = final: prev: {
wayland-protocols = prev.wayland-protocols.overrideAttrs (self: super: {
version = "1.35";
src = prev.fetchurl {
url = "https://gitlab.freedesktop.org/wayland/${super.pname}/-/releases/${self.version}/downloads/${super.pname}-${self.version}.tar.xz";
hash = "sha256-N6JxaigTPcgZNBxWiinSHoy3ITDlwSah/PyfQsI9las=";
};
});
};
}

View File

@@ -1,12 +1,12 @@
diff --git a/meson.build b/meson.build
index 1d2c7f9f..c5ef4e67 100644
index 40883073..d8f2e536 100644
--- a/meson.build
+++ b/meson.build
@@ -33,20 +33,7 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
-wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
-wlroots = subproject('wlroots-hyprland', default_options: ['examples=false', 'renderers=gles2'])
-have_xwlr = wlroots.get_variable('features').get('xwayland')
-xcb_dep = dependency('xcb', required: get_option('xwayland'))
-
@@ -24,7 +24,7 @@ index 1d2c7f9f..c5ef4e67 100644
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
@@ -69,8 +56,6 @@ if get_option('buildtype') == 'debug'
@@ -65,8 +52,6 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
@@ -34,7 +34,7 @@ index 1d2c7f9f..c5ef4e67 100644
headers = globber.stdout().strip().split('\n')
foreach file : headers
diff --git a/src/meson.build b/src/meson.build
index 45701f5f..3505cefe 100644
index 15c69552..327aa4fb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,7 +9,7 @@ executable('Hyprland', src,
@@ -46,14 +46,14 @@ index 45701f5f..3505cefe 100644
dependency('cairo'),
dependency('hyprcursor'),
dependency('hyprlang', version: '>= 0.3.2'),
@@ -16,12 +16,12 @@ executable('Hyprland', src,
@@ -17,10 +17,10 @@ executable('Hyprland', src,
dependency('egl'),
dependency('xkbcommon'),
dependency('libinput'),
- xcb_dep,
+ dependency('xcb', required: get_option('xwayland')),
backtrace_dep,
systemd_dep,
epoll_dep,
- udis86,
+ dependency('udis86'),

View File

@@ -1,6 +1,7 @@
{
version,
src,
git,
wlroots,
enableXWayland ? true,
}:
@@ -10,4 +11,6 @@ wlroots.overrideAttrs (old: {
pname = "${old.pname}-hyprland";
patches = [ ]; # don't inherit old.patches
nativeBuildInputs = old.nativeBuildInputs ++ [ git ];
})

View File

@@ -1,3 +1,3 @@
{
"version": "0.38.0"
"version": "0.40.0"
}

View File

@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle">
<copyright><![CDATA[
Copyright (C) 2015 Martin Gräßlin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]></copyright>
<interface name="org_kde_kwin_idle" version="1">
<description summary="User idle time manager">
This interface allows to monitor user idle time on a given seat. The interface
allows to register timers which trigger after no user activity was registered
on the seat for a given interval. It notifies when user activity resumes.
This is useful for applications wanting to perform actions when the user is not
interacting with the system, e.g. chat applications setting the user as away, power
management features to dim screen, etc..
</description>
<request name="get_idle_timeout">
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
</request>
</interface>
<interface name="org_kde_kwin_idle_timeout" version="1">
<request name="release" type="destructor">
<description summary="release the timeout object"/>
</request>
<request name="simulate_user_activity">
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
</request>
<event name="idle">
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
</event>
<event name="resumed">
<description summary="Triggered on the first user activity after an idle event"/>
</event>
</interface>
</protocol>

View File

@@ -0,0 +1,494 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="input_method_unstable_v2">
<copyright>
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2011 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2012, 2013 Intel Corporation
Copyright © 2015, 2016 Jan Arne Petersen
Copyright © 2017, 2018 Red Hat, Inc.
Copyright © 2018 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Protocol for creating input methods">
This protocol allows applications to act as input methods for compositors.
An input method context is used to manage the state of the input method.
Text strings are UTF-8 encoded, their indices and lengths are in bytes.
This document adheres to the RFC 2119 when using words like "must",
"should", "may", etc.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwp_input_method_v2" version="1">
<description summary="input method">
An input method object allows for clients to compose text.
The objects connects the client to a text input in an application, and
lets the client to serve as an input method for a seat.
The zwp_input_method_v2 object can occupy two distinct states: active and
inactive. In the active state, the object is associated to and
communicates with a text input. In the inactive state, there is no
associated text input, and the only communication is with the compositor.
Initially, the input method is in the inactive state.
Requests issued in the inactive state must be accepted by the compositor.
Because of the serial mechanism, and the state reset on activate event,
they will not have any effect on the state of the next text input.
There must be no more than one input method object per seat.
</description>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
</enum>
<event name="activate">
<description summary="input method has been requested">
Notification that a text input focused on this seat requested the input
method to be activated.
This event serves the purpose of providing the compositor with an
active input method.
This event resets all state associated with previous enable, disable,
surrounding_text, text_change_cause, and content_type events, as well
as the state associated with set_preedit_string, commit_string, and
delete_surrounding_text requests. In addition, it marks the
zwp_input_method_v2 object as active, and makes any existing
zwp_input_popup_surface_v2 objects visible.
The surrounding_text, and content_type events must follow before the
next done event if the text input supports the respective
functionality.
State set with this event is double-buffered. It will get applied on
the next zwp_input_method_v2.done event, and stay valid until changed.
</description>
</event>
<event name="deactivate">
<description summary="deactivate event">
Notification that no focused text input currently needs an active
input method on this seat.
This event marks the zwp_input_method_v2 object as inactive. The
compositor must make all existing zwp_input_popup_surface_v2 objects
invisible until the next activate event.
State set with this event is double-buffered. It will get applied on
the next zwp_input_method_v2.done event, and stay valid until changed.
</description>
</event>
<event name="surrounding_text">
<description summary="surrounding text event">
Updates the surrounding plain text around the cursor, excluding the
preedit text.
If any preedit text is present, it is replaced with the cursor for the
purpose of this event.
The argument text is a buffer containing the preedit string, and must
include the cursor position, and the complete selection. It should
contain additional characters before and after these. There is a
maximum length of wayland messages, so text can not be longer than 4000
bytes.
cursor is the byte offset of the cursor within the text buffer.
anchor is the byte offset of the selection anchor within the text
buffer. If there is no selected text, anchor must be the same as
cursor.
If this event does not arrive before the first done event, the input
method may assume that the text input does not support this
functionality and ignore following surrounding_text events.
Values set with this event are double-buffered. They will get applied
and set to initial values on the next zwp_input_method_v2.done
event.
The initial state for affected fields is empty, meaning that the text
input does not support sending surrounding text. If the empty values
get applied, subsequent attempts to change them may have no effect.
</description>
<arg name="text" type="string"/>
<arg name="cursor" type="uint"/>
<arg name="anchor" type="uint"/>
</event>
<event name="text_change_cause">
<description summary="indicates the cause of surrounding text change">
Tells the input method why the text surrounding the cursor changed.
Whenever the client detects an external change in text, cursor, or
anchor position, it must issue this request to the compositor. This
request is intended to give the input method a chance to update the
preedit text in an appropriate way, e.g. by removing it when the user
starts typing with a keyboard.
cause describes the source of the change.
The value set with this event is double-buffered. It will get applied
and set to its initial value on the next zwp_input_method_v2.done
event.
The initial value of cause is input_method.
</description>
<arg name="cause" type="uint" enum="zwp_text_input_v3.change_cause"/>
</event>
<event name="content_type">
<description summary="content purpose and hint">
Indicates the content type and hint for the current
zwp_input_method_v2 instance.
Values set with this event are double-buffered. They will get applied
on the next zwp_input_method_v2.done event.
The initial value for hint is none, and the initial value for purpose
is normal.
</description>
<arg name="hint" type="uint" enum="zwp_text_input_v3.content_hint"/>
<arg name="purpose" type="uint" enum="zwp_text_input_v3.content_purpose"/>
</event>
<event name="done">
<description summary="apply state">
Atomically applies state changes recently sent to the client.
The done event establishes and updates the state of the client, and
must be issued after any changes to apply them.
Text input state (content purpose, content hint, surrounding text, and
change cause) is conceptually double-buffered within an input method
context.
Events modify the pending state, as opposed to the current state in use
by the input method. A done event atomically applies all pending state,
replacing the current state. After done, the new pending state is as
documented for each related request.
Events must be applied in the order of arrival.
Neither current nor pending state are modified unless noted otherwise.
</description>
</event>
<request name="commit_string">
<description summary="commit string">
Send the commit string text for insertion to the application.
Inserts a string at current cursor position (see commit event
sequence). The string to commit could be either just a single character
after a key press or the result of some composing.
The argument text is a buffer containing the string to insert. There is
a maximum length of wayland messages, so text can not be longer than
4000 bytes.
Values set with this event are double-buffered. They must be applied
and reset to initial on the next zwp_text_input_v3.commit request.
The initial value of text is an empty string.
</description>
<arg name="text" type="string"/>
</request>
<request name="set_preedit_string">
<description summary="pre-edit string">
Send the pre-edit string text to the application text input.
Place a new composing text (pre-edit) at the current cursor position.
Any previously set composing text must be removed. Any previously
existing selected text must be removed. The cursor is moved to a new
position within the preedit string.
The argument text is a buffer containing the preedit string. There is
a maximum length of wayland messages, so text can not be longer than
4000 bytes.
The arguments cursor_begin and cursor_end are counted in bytes relative
to the beginning of the submitted string buffer. Cursor should be
hidden by the text input when both are equal to -1.
cursor_begin indicates the beginning of the cursor. cursor_end
indicates the end of the cursor. It may be equal or different than
cursor_begin.
Values set with this event are double-buffered. They must be applied on
the next zwp_input_method_v2.commit event.
The initial value of text is an empty string. The initial value of
cursor_begin, and cursor_end are both 0.
</description>
<arg name="text" type="string"/>
<arg name="cursor_begin" type="int"/>
<arg name="cursor_end" type="int"/>
</request>
<request name="delete_surrounding_text">
<description summary="delete text">
Remove the surrounding text.
before_length and after_length are the number of bytes before and after
the current cursor index (excluding the preedit text) to delete.
If any preedit text is present, it is replaced with the cursor for the
purpose of this event. In effect before_length is counted from the
beginning of preedit text, and after_length from its end (see commit
event sequence).
Values set with this event are double-buffered. They must be applied
and reset to initial on the next zwp_input_method_v2.commit request.
The initial values of both before_length and after_length are 0.
</description>
<arg name="before_length" type="uint"/>
<arg name="after_length" type="uint"/>
</request>
<request name="commit">
<description summary="apply state">
Apply state changes from commit_string, set_preedit_string and
delete_surrounding_text requests.
The state relating to these events is double-buffered, and each one
modifies the pending state. This request replaces the current state
with the pending state.
The connected text input is expected to proceed by evaluating the
changes in the following order:
1. Replace existing preedit string with the cursor.
2. Delete requested surrounding text.
3. Insert commit string with the cursor at its end.
4. Calculate surrounding text to send.
5. Insert new preedit text in cursor position.
6. Place cursor inside preedit text.
The serial number reflects the last state of the zwp_input_method_v2
object known to the client. The value of the serial argument must be
equal to the number of done events already issued by that object. When
the compositor receives a commit request with a serial different than
the number of past done events, it must proceed as normal, except it
should not change the current state of the zwp_input_method_v2 object.
</description>
<arg name="serial" type="uint"/>
</request>
<request name="get_input_popup_surface">
<description summary="create popup surface">
Creates a new zwp_input_popup_surface_v2 object wrapping a given
surface.
The surface gets assigned the "input_popup" role. If the surface
already has an assigned role, the compositor must issue a protocol
error.
</description>
<arg name="id" type="new_id" interface="zwp_input_popup_surface_v2"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="grab_keyboard">
<description summary="grab hardware keyboard">
Allow an input method to receive hardware keyboard input and process
key events to generate text events (with pre-edit) over the wire. This
allows input methods which compose multiple key events for inputting
text like it is done for CJK languages.
The compositor should send all keyboard events on the seat to the grab
holder via the returned wl_keyboard object. Nevertheless, the
compositor may decide not to forward any particular event. The
compositor must not further process any event after it has been
forwarded to the grab holder.
Releasing the resulting wl_keyboard object releases the grab.
</description>
<arg name="keyboard" type="new_id"
interface="zwp_input_method_keyboard_grab_v2"/>
</request>
<event name="unavailable">
<description summary="input method unavailable">
The input method ceased to be available.
The compositor must issue this event as the only event on the object if
there was another input_method object associated with the same seat at
the time of its creation.
The compositor must issue this request when the object is no longer
usable, e.g. due to seat removal.
The input method context becomes inert and should be destroyed after
deactivation is handled. Any further requests and events except for the
destroy request must be ignored.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the text input">
Destroys the zwp_text_input_v2 object and any associated child
objects, i.e. zwp_input_popup_surface_v2 and
zwp_input_method_keyboard_grab_v2.
</description>
</request>
</interface>
<interface name="zwp_input_popup_surface_v2" version="1">
<description summary="popup surface">
This interface marks a surface as a popup for interacting with an input
method.
The compositor should place it near the active text input area. It must
be visible if and only if the input method is in the active state.
The client must not destroy the underlying wl_surface while the
zwp_input_popup_surface_v2 object exists.
</description>
<event name="text_input_rectangle">
<description summary="set text input area position">
Notify about the position of the area of the text input expressed as a
rectangle in surface local coordinates.
This is a hint to the input method telling it the relative position of
the text being entered.
</description>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</event>
<request name="destroy" type="destructor"/>
</interface>
<interface name="zwp_input_method_keyboard_grab_v2" version="1">
<!-- Closely follows wl_keyboard version 6 -->
<description summary="keyboard grab">
The zwp_input_method_keyboard_grab_v2 interface represents an exclusive
grab of the wl_keyboard interface associated with the seat.
</description>
<event name="keymap">
<description summary="keyboard mapping">
This event provides a file descriptor to the client which can be
memory-mapped to provide a keyboard mapping description.
</description>
<arg name="format" type="uint" enum="wl_keyboard.keymap_format"
summary="keymap format"/>
<arg name="fd" type="fd" summary="keymap file descriptor"/>
<arg name="size" type="uint" summary="keymap size, in bytes"/>
</event>
<event name="key">
<description summary="key event">
A key was pressed or released.
The time argument is a timestamp with millisecond granularity, with an
undefined base.
</description>
<arg name="serial" type="uint" summary="serial number of the key event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint" summary="key that produced the event"/>
<arg name="state" type="uint" enum="wl_keyboard.key_state"
summary="physical state of the key"/>
</event>
<event name="modifiers">
<description summary="modifier and group state">
Notifies clients that the modifier and/or group state has changed, and
it should update its local state.
</description>
<arg name="serial" type="uint" summary="serial number of the modifiers event"/>
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
<arg name="group" type="uint" summary="keyboard layout"/>
</event>
<request name="release" type="destructor">
<description summary="release the grab object"/>
</request>
<event name="repeat_info">
<description summary="repeat rate and delay">
Informs the client about the keyboard's repeat rate and delay.
This event is sent as soon as the zwp_input_method_keyboard_grab_v2
object has been created, and is guaranteed to be received by the
client before any key press event.
Negative values for either rate or delay are illegal. A rate of zero
will disable any repeating (regardless of the value of delay).
This event can be sent later on as well with a new value if necessary,
so clients should continue listening for the event past the creation
of zwp_input_method_keyboard_grab_v2.
</description>
<arg name="rate" type="int"
summary="the rate of repeating keys in characters per second"/>
<arg name="delay" type="int"
summary="delay in milliseconds since key down until repeating starts"/>
</event>
</interface>
<interface name="zwp_input_method_manager_v2" version="1">
<description summary="input method manager">
The input method manager allows the client to become the input method on
a chosen seat.
No more than one input method must be associated with any seat at any
given time.
</description>
<request name="get_input_method">
<description summary="request an input method object">
Request a new input zwp_input_method_v2 object associated with a given
seat.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="input_method" type="new_id" interface="zwp_input_method_v2"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the input method manager">
Destroys the zwp_input_method_manager_v2 object.
The zwp_input_method_v2 objects originating from it remain valid.
</description>
</request>
</interface>
</protocol>

View File

@@ -17,27 +17,52 @@ wayland_scanner = find_program(
wayland_scanner_dep.get_variable('wayland_scanner'),
native: true,
)
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', native: true)
hyprwayland_scanner = find_program(
hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'),
native: true,
)
protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
['pointer-constraints-unstable-v1.xml'],
['tablet-unstable-v2.xml'],
['idle.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml']
]
new_protocols = [
['wlr-gamma-control-unstable-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
['input-method-unstable-v2.xml'],
['virtual-keyboard-unstable-v1.xml'],
['wlr-virtual-pointer-unstable-v1.xml'],
['wlr-output-management-unstable-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'staging/alpha-modifier/alpha-modifier-v1.xml'],
[wl_protocol_dir, 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml'],
[wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'],
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
]
wl_protos_src = []
wl_protos_headers = []
foreach p : protocols
xml = join_paths(p)
wl_protos_src += custom_target(
@@ -56,15 +81,28 @@ foreach p : protocols
)
endforeach
new_wl_protos = []
foreach p : new_protocols
xml = join_paths(p)
new_wl_protos += custom_target(
xml.underscorify(),
input: xml,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '@INPUT@', '@OUTDIR@'],
)
endforeach
wayland_server = dependency('wayland-server', version: '>=1.20.0')
lib_server_protos = static_library(
'server_protos',
wl_protos_src + wl_protos_headers,
wl_protos_src + wl_protos_headers + new_wl_protos,
dependencies: wayland_server.partial_dependency(compile_args: true),
)
server_protos = declare_dependency(
link_with: lib_server_protos,
sources: wl_protos_headers,
sources: wl_protos_headers + new_wl_protos,
)

View File

@@ -1,339 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="pointer_constraints_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="protocol for constraining pointer motions">
This protocol specifies a set of interfaces used for adding constraints to
the motion of a pointer. Possible constraints include confining pointer
motions to a given region, or locking it to its current position.
In order to constrain the pointer, a client must first bind the global
interface "wp_pointer_constraints" which, if a compositor supports pointer
constraints, is exposed by the registry. Using the bound global object, the
client uses the request that corresponds to the type of constraint it wants
to make. See wp_pointer_constraints for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_pointer_constraints_v1" version="1">
<description summary="constrain the movement of a pointer">
The global interface exposing pointer constraining functionality. It
exposes two requests: lock_pointer for locking the pointer to its
position, and confine_pointer for locking the pointer to a region.
The lock_pointer and confine_pointer requests create the objects
wp_locked_pointer and wp_confined_pointer respectively, and the client can
use these objects to interact with the lock.
For any surface, only one lock or confinement may be active across all
wl_pointer objects of the same seat. If a lock or confinement is requested
when another lock or confinement is active or requested on the same surface
and with any of the wl_pointer objects of the same seat, an
'already_constrained' error will be raised.
</description>
<enum name="error">
<description summary="wp_pointer_constraints error values">
These errors can be emitted in response to wp_pointer_constraints
requests.
</description>
<entry name="already_constrained" value="1"
summary="pointer constraint already requested on that surface"/>
</enum>
<enum name="lifetime">
<description summary="constraint lifetime">
These values represent different lifetime semantics. They are passed
as arguments to the factory requests to specify how the constraint
lifetimes should be managed.
</description>
<entry name="oneshot" value="1">
<description summary="the pointer constraint is defunct once deactivated">
A oneshot pointer constraint will never reactivate once it has been
deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
<entry name="persistent" value="2">
<description summary="the pointer constraint may reactivate">
A persistent pointer constraint may again reactivate once it has
been deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the pointer constraints manager object">
Used by the client to notify the server that it will no longer use this
pointer constraints object.
</description>
</request>
<request name="lock_pointer">
<description summary="lock pointer to a position">
The lock_pointer request lets the client request to disable movements of
the virtual pointer (i.e. the cursor), effectively locking the pointer
to a position. This request may not take effect immediately; in the
future, when the compositor deems implementation-specific constraints
are satisfied, the pointer lock will be activated and the compositor
sends a locked event.
The protocol provides no guarantee that the constraints are ever
satisfied, and does not require the compositor to send an error if the
constraints cannot ever be satisfied. It is thus possible to request a
lock that will never activate.
There may not be another pointer constraint of any kind requested or
active on the surface for any of the wl_pointer objects of the seat of
the passed pointer when requesting a lock. If there is, an error will be
raised. See general pointer lock documentation for more details.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the lock to activate. It is up to the compositor whether to
warp the pointer or require some kind of user interaction for the lock
to activate. If the region is null the surface input region is used.
A surface may receive pointer focus without the lock being activated.
The request creates a new object wp_locked_pointer which is used to
interact with the lock as well as receive updates about its state. See
the the description of wp_locked_pointer for further information.
Note that while a pointer is locked, the wl_pointer objects of the
corresponding seat will not emit any wl_pointer.motion events, but
relative motion events will still be emitted via wp_relative_pointer
objects of the same seat. wl_pointer.axis and wl_pointer.button events
are unaffected.
</description>
<arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be locked"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="lock lifetime"/>
</request>
<request name="confine_pointer">
<description summary="confine pointer to a region">
The confine_pointer request lets the client request to confine the
pointer cursor to a given region. This request may not take effect
immediately; in the future, when the compositor deems implementation-
specific constraints are satisfied, the pointer confinement will be
activated and the compositor sends a confined event.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the confinement to activate. It is up to the compositor
whether to warp the pointer or require some kind of user interaction for
the confinement to activate. If the region is null the surface input
region is used.
The request will create a new object wp_confined_pointer which is used
to interact with the confinement as well as receive updates about its
state. See the the description of wp_confined_pointer for further
information.
</description>
<arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be confined"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" enum="lifetime" summary="confinement lifetime"/>
</request>
</interface>
<interface name="zwp_locked_pointer_v1" version="1">
<description summary="receive relative pointer motion events">
The wp_locked_pointer interface represents a locked pointer state.
While the lock of this object is active, the wl_pointer objects of the
associated seat will not emit any wl_pointer.motion events.
This object will send the event 'locked' when the lock is activated.
Whenever the lock is activated, it is guaranteed that the locked surface
will already have received pointer focus and that the pointer will be
within the region passed to the request creating this object.
To unlock the pointer, send the destroy request. This will also destroy
the wp_locked_pointer object.
If the compositor decides to unlock the pointer the unlocked event is
sent. See wp_locked_pointer.unlock for details.
When unlocking, the compositor may warp the cursor position to the set
cursor position hint. If it does, it will not result in any relative
motion events emitted via wp_relative_pointer.
If the surface the lock was requested on is destroyed and the lock is not
yet activated, the wp_locked_pointer object is now defunct and must be
destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the locked pointer object">
Destroy the locked pointer object. If applicable, the compositor will
unlock the pointer.
</description>
</request>
<request name="set_cursor_position_hint">
<description summary="set the pointer cursor position hint">
Set the cursor position hint relative to the top left corner of the
surface.
If the client is drawing its own cursor, it should update the position
hint to the position of its own cursor. A compositor may use this
information to warp the pointer upon unlock in order to avoid pointer
jumps.
The cursor position hint is double buffered. The new hint will only take
effect when the associated surface gets it pending state applied. See
wl_surface.commit for details.
</description>
<arg name="surface_x" type="fixed"
summary="surface-local x coordinate"/>
<arg name="surface_y" type="fixed"
summary="surface-local y coordinate"/>
</request>
<request name="set_region">
<description summary="set a new lock region">
Set a new region used to lock the pointer.
The new lock region is double-buffered. The new lock region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
For details about the lock region, see wp_locked_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="locked">
<description summary="lock activation event">
Notification that the pointer lock of the seat's pointer is activated.
</description>
</event>
<event name="unlocked">
<description summary="lock deactivation event">
Notification that the pointer lock of the seat's pointer is no longer
active. If this is a oneshot pointer lock (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer lock (see
wp_pointer_constraints.lifetime) this pointer lock may again
reactivate in the future.
</description>
</event>
</interface>
<interface name="zwp_confined_pointer_v1" version="1">
<description summary="confined pointer object">
The wp_confined_pointer interface represents a confined pointer state.
This object will send the event 'confined' when the confinement is
activated. Whenever the confinement is activated, it is guaranteed that
the surface the pointer is confined to will already have received pointer
focus and that the pointer will be within the region passed to the request
creating this object. It is up to the compositor to decide whether this
requires some user interaction and if the pointer will warp to within the
passed region if outside.
To unconfine the pointer, send the destroy request. This will also destroy
the wp_confined_pointer object.
If the compositor decides to unconfine the pointer the unconfined event is
sent. The wp_confined_pointer object is at this point defunct and should
be destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the confined pointer object">
Destroy the confined pointer object. If applicable, the compositor will
unconfine the pointer.
</description>
</request>
<request name="set_region">
<description summary="set a new confine region">
Set a new region used to confine the pointer.
The new confine region is double-buffered. The new confine region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
If the confinement is active when the new confinement region is applied
and the pointer ends up outside of newly applied region, the pointer may
warped to a position within the new confinement region. If warped, a
wl_pointer.motion event will be emitted, but no
wp_relative_pointer.relative_motion event.
The compositor may also, instead of using the new region, unconfine the
pointer.
For details about the confine region, see wp_confined_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="confined">
<description summary="pointer confined">
Notification that the pointer confinement of the seat's pointer is
activated.
</description>
</event>
<event name="unconfined">
<description summary="pointer unconfined">
Notification that the pointer confinement of the seat's pointer is no
longer active. If this is a oneshot pointer confinement (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer confinement (see
wp_pointer_constraints.lifetime) this pointer confinement may again
reactivate in the future.
</description>
</event>
</interface>
</protocol>

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="virtual_keyboard_unstable_v1">
<copyright>
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2013 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2018 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="zwp_virtual_keyboard_v1" version="1">
<description summary="virtual keyboard">
The virtual keyboard provides an application with requests which emulate
the behaviour of a physical keyboard.
This interface can be used by clients on its own to provide raw input
events, or it can accompany the input method protocol.
</description>
<request name="keymap">
<description summary="keyboard mapping">
Provide a file descriptor to the compositor which can be
memory-mapped to provide a keyboard mapping description.
Format carries a value from the keymap_format enumeration.
</description>
<arg name="format" type="uint" summary="keymap format"/>
<arg name="fd" type="fd" summary="keymap file descriptor"/>
<arg name="size" type="uint" summary="keymap size, in bytes"/>
</request>
<enum name="error">
<entry name="no_keymap" value="0" summary="No keymap was set"/>
</enum>
<request name="key">
<description summary="key event">
A key was pressed or released.
The time argument is a timestamp with millisecond granularity, with an
undefined base. All requests regarding a single object must share the
same clock.
Keymap must be set before issuing this request.
State carries a value from the key_state enumeration.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint" summary="key that produced the event"/>
<arg name="state" type="uint" summary="physical state of the key"/>
</request>
<request name="modifiers">
<description summary="modifier and group state">
Notifies the compositor that the modifier and/or group state has
changed, and it should update state.
The client should use wl_keyboard.modifiers event to synchronize its
internal state with seat state.
Keymap must be set before issuing this request.
</description>
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
<arg name="group" type="uint" summary="keyboard layout"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual keyboard keyboard object"/>
</request>
</interface>
<interface name="zwp_virtual_keyboard_manager_v1" version="1">
<description summary="virtual keyboard manager">
A virtual keyboard manager allows an application to provide keyboard
input events as if they came from a physical keyboard.
</description>
<enum name="error">
<entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
</enum>
<request name="create_virtual_keyboard">
<description summary="Create a new virtual keyboard">
Creates a new virtual keyboard associated to a seat.
If the compositor enables a keyboard to perform arbitrary actions, it
should present an error when an untrusted client requests a new
keyboard.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
</request>
</interface>
</protocol>

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_gamma_control_unstable_v1">
<copyright>
Copyright © 2015 Giulio camuffo
Copyright © 2018 Simon Ser
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<description summary="manage gamma tables of outputs">
This protocol allows a privileged client to set the gamma tables for
outputs.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_gamma_control_manager_v1" version="1">
<description summary="manager to create per-output gamma controls">
This interface is a manager that allows creating per-output gamma
controls.
</description>
<request name="get_gamma_control">
<description summary="get a gamma control for an output">
Create a gamma control that can be used to adjust gamma tables for the
provided output.
</description>
<arg name="id" type="new_id" interface="zwlr_gamma_control_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_gamma_control_v1" version="1">
<description summary="adjust gamma tables for an output">
This interface allows a client to adjust gamma tables for a particular
output.
The client will receive the gamma size, and will then be able to set gamma
tables. At any time the compositor can send a failed event indicating that
this object is no longer valid.
There can only be at most one gamma control object per output, which
has exclusive access to this particular output. When the gamma control
object is destroyed, the gamma table is restored to its original value.
</description>
<event name="gamma_size">
<description summary="size of gamma ramps">
Advertise the size of each gamma ramp.
This event is sent immediately when the gamma control object is created.
</description>
<arg name="size" type="uint" summary="number of elements in a ramp"/>
</event>
<enum name="error">
<entry name="invalid_gamma" value="1" summary="invalid gamma tables"/>
</enum>
<request name="set_gamma">
<description summary="set the gamma table">
Set the gamma table. The file descriptor can be memory-mapped to provide
the raw gamma table, which contains successive gamma ramps for the red,
green and blue channels. Each gamma ramp is an array of 16-byte unsigned
integers which has the same length as the gamma size.
The file descriptor data must have the same length as three times the
gamma size.
</description>
<arg name="fd" type="fd" summary="gamma table file descriptor"/>
</request>
<event name="failed">
<description summary="object no longer valid">
This event indicates that the gamma control is no longer valid. This
can happen for a number of reasons, including:
- The output doesn't support gamma tables
- Setting the gamma tables failed
- Another client already has exclusive gamma control for this output
- The compositor has transferred gamma control to another client
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this control">
Destroys the gamma control object. If the object is still valid, this
restores the original gamma tables.
</description>
</request>
</interface>
</protocol>

View File

@@ -0,0 +1,601 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_management_unstable_v1">
<copyright>
Copyright © 2019 Purism SPC
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<description summary="protocol to configure output devices">
This protocol exposes interfaces to obtain and modify output device
configuration.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_output_manager_v1" version="4">
<description summary="output device configuration manager">
This interface is a manager that allows reading and writing the current
output device configuration.
Output devices that display pixels (e.g. a physical monitor or a virtual
output in a window) are represented as heads. Heads cannot be created nor
destroyed by the client, but they can be enabled or disabled and their
properties can be changed. Each head may have one or more available modes.
Whenever a head appears (e.g. a monitor is plugged in), it will be
advertised via the head event. Immediately after the output manager is
bound, all current heads are advertised.
Whenever a head's properties change, the relevant wlr_output_head events
will be sent. Not all head properties will be sent: only properties that
have changed need to.
Whenever a head disappears (e.g. a monitor is unplugged), a
wlr_output_head.finished event will be sent.
After one or more heads appear, change or disappear, the done event will
be sent. It carries a serial which can be used in a create_configuration
request to update heads properties.
The information obtained from this protocol should only be used for output
configuration purposes. This protocol is not designed to be a generic
output property advertisement protocol for regular clients. Instead,
protocols such as xdg-output should be used.
</description>
<event name="head">
<description summary="introduce a new head">
This event introduces a new head. This happens whenever a new head
appears (e.g. a monitor is plugged in) or after the output manager is
bound.
</description>
<arg name="head" type="new_id" interface="zwlr_output_head_v1"/>
</event>
<event name="done">
<description summary="sent all information about current configuration">
This event is sent after all information has been sent after binding to
the output manager object and after any subsequent changes. This applies
to child head and mode objects as well. In other words, this event is
sent whenever a head or mode is created or destroyed and whenever one of
their properties has been changed. Not all state is re-sent each time
the current configuration changes: only the actual changes are sent.
This allows changes to the output configuration to be seen as atomic,
even if they happen via multiple events.
A serial is sent to be used in a future create_configuration request.
</description>
<arg name="serial" type="uint" summary="current configuration serial"/>
</event>
<request name="create_configuration">
<description summary="create a new output configuration object">
Create a new output configuration object. This allows to update head
properties.
</description>
<arg name="id" type="new_id" interface="zwlr_output_configuration_v1"/>
<arg name="serial" type="uint"/>
</request>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for output
configuration changes. However the compositor may emit further events,
until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
<event name="finished" type="destructor">
<description summary="the compositor has finished with the manager">
This event indicates that the compositor is done sending manager events.
The compositor will destroy the object immediately after sending this
event, so it will become invalid and the client should release any
resources associated with it.
</description>
</event>
</interface>
<interface name="zwlr_output_head_v1" version="4">
<description summary="output device">
A head is an output device. The difference between a wl_output object and
a head is that heads are advertised even if they are turned off. A head
object only advertises properties and cannot be used directly to change
them.
A head has some read-only properties: modes, name, description and
physical_size. These cannot be changed by clients.
Other properties can be updated via a wlr_output_configuration object.
Properties sent via this interface are applied atomically via the
wlr_output_manager.done event. No guarantees are made regarding the order
in which properties are sent.
</description>
<event name="name">
<description summary="head name">
This event describes the head name.
The naming convention is compositor defined, but limited to alphanumeric
characters and dashes (-). Each name is unique among all wlr_output_head
objects, but if a wlr_output_head object is destroyed the same name may
be reused later. The names will also remain consistent across sessions
with the same hardware and software configuration.
Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
not assume that the name is a reflection of an underlying DRM
connector, X11 connection, etc.
If the compositor implements the xdg-output protocol and this head is
enabled, the xdg_output.name event must report the same name.
The name event is sent after a wlr_output_head object is created. This
event is only sent once per object, and the name does not change over
the lifetime of the wlr_output_head object.
</description>
<arg name="name" type="string"/>
</event>
<event name="description">
<description summary="head description">
This event describes a human-readable description of the head.
The description is a UTF-8 string with no convention defined for its
contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11
output via :1'. However, do not assume that the name is a reflection of
the make, model, serial of the underlying DRM connector or the display
name of the underlying X11 connection, etc.
If the compositor implements xdg-output and this head is enabled,
the xdg_output.description must report the same description.
The description event is sent after a wlr_output_head object is created.
This event is only sent once per object, and the description does not
change over the lifetime of the wlr_output_head object.
</description>
<arg name="description" type="string"/>
</event>
<event name="physical_size">
<description summary="head physical size">
This event describes the physical size of the head. This event is only
sent if the head has a physical size (e.g. is not a projector or a
virtual device).
</description>
<arg name="width" type="int" summary="width in millimeters of the output"/>
<arg name="height" type="int" summary="height in millimeters of the output"/>
</event>
<event name="mode">
<description summary="introduce a mode">
This event introduces a mode for this head. It is sent once per
supported mode.
</description>
<arg name="mode" type="new_id" interface="zwlr_output_mode_v1"/>
</event>
<event name="enabled">
<description summary="head is enabled or disabled">
This event describes whether the head is enabled. A disabled head is not
mapped to a region of the global compositor space.
When a head is disabled, some properties (current_mode, position,
transform and scale) are irrelevant.
</description>
<arg name="enabled" type="int" summary="zero if disabled, non-zero if enabled"/>
</event>
<event name="current_mode">
<description summary="current mode">
This event describes the mode currently in use for this head. It is only
sent if the output is enabled.
</description>
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
</event>
<event name="position">
<description summary="current position">
This events describes the position of the head in the global compositor
space. It is only sent if the output is enabled.
</description>
<arg name="x" type="int"
summary="x position within the global compositor space"/>
<arg name="y" type="int"
summary="y position within the global compositor space"/>
</event>
<event name="transform">
<description summary="current transformation">
This event describes the transformation currently applied to the head.
It is only sent if the output is enabled.
</description>
<arg name="transform" type="int" enum="wl_output.transform"/>
</event>
<event name="scale">
<description summary="current scale">
This events describes the scale of the head in the global compositor
space. It is only sent if the output is enabled.
</description>
<arg name="scale" type="fixed"/>
</event>
<event name="finished">
<description summary="the head has disappeared">
This event indicates that the head is no longer available. The head
object becomes inert. Clients should send a destroy request and release
any resources associated with it.
</description>
</event>
<!-- Version 2 additions -->
<event name="make" since="2">
<description summary="head manufacturer">
This event describes the manufacturer of the head.
This must report the same make as the wl_output interface does in its
geometry event.
Together with the model and serial_number events the purpose is to
allow clients to recognize heads from previous sessions and for example
load head-specific configurations back.
It is not guaranteed this event will be ever sent. A reason for that
can be that the compositor does not have information about the make of
the head or the definition of a make is not sensible in the current
setup, for example in a virtual session. Clients can still try to
identify the head by available information from other events but should
be aware that there is an increased risk of false positives.
It is not recommended to display the make string in UI to users. For
that the string provided by the description event should be preferred.
</description>
<arg name="make" type="string"/>
</event>
<event name="model" since="2">
<description summary="head model">
This event describes the model of the head.
This must report the same model as the wl_output interface does in its
geometry event.
Together with the make and serial_number events the purpose is to
allow clients to recognize heads from previous sessions and for example
load head-specific configurations back.
It is not guaranteed this event will be ever sent. A reason for that
can be that the compositor does not have information about the model of
the head or the definition of a model is not sensible in the current
setup, for example in a virtual session. Clients can still try to
identify the head by available information from other events but should
be aware that there is an increased risk of false positives.
It is not recommended to display the model string in UI to users. For
that the string provided by the description event should be preferred.
</description>
<arg name="model" type="string"/>
</event>
<event name="serial_number" since="2">
<description summary="head serial number">
This event describes the serial number of the head.
Together with the make and model events the purpose is to allow clients
to recognize heads from previous sessions and for example load head-
specific configurations back.
It is not guaranteed this event will be ever sent. A reason for that
can be that the compositor does not have information about the serial
number of the head or the definition of a serial number is not sensible
in the current setup. Clients can still try to identify the head by
available information from other events but should be aware that there
is an increased risk of false positives.
It is not recommended to display the serial_number string in UI to
users. For that the string provided by the description event should be
preferred.
</description>
<arg name="serial_number" type="string"/>
</event>
<!-- Version 3 additions -->
<request name="release" type="destructor" since="3">
<description summary="destroy the head object">
This request indicates that the client will no longer use this head
object.
</description>
</request>
<!-- Version 4 additions -->
<enum name="adaptive_sync_state" since="4">
<entry name="disabled" value="0" summary="adaptive sync is disabled"/>
<entry name="enabled" value="1" summary="adaptive sync is enabled"/>
</enum>
<event name="adaptive_sync" since="4">
<description summary="current adaptive sync state">
This event describes whether adaptive sync is currently enabled for
the head or not. Adaptive sync is also known as Variable Refresh
Rate or VRR.
</description>
<arg name="state" type="uint" enum="adaptive_sync_state"/>
</event>
</interface>
<interface name="zwlr_output_mode_v1" version="3">
<description summary="output mode">
This object describes an output mode.
Some heads don't support output modes, in which case modes won't be
advertised.
Properties sent via this interface are applied atomically via the
wlr_output_manager.done event. No guarantees are made regarding the order
in which properties are sent.
</description>
<event name="size">
<description summary="mode size">
This event describes the mode size. The size is given in physical
hardware units of the output device. This is not necessarily the same as
the output size in the global compositor space. For instance, the output
may be scaled or transformed.
</description>
<arg name="width" type="int" summary="width of the mode in hardware units"/>
<arg name="height" type="int" summary="height of the mode in hardware units"/>
</event>
<event name="refresh">
<description summary="mode refresh rate">
This event describes the mode's fixed vertical refresh rate. It is only
sent if the mode has a fixed refresh rate.
</description>
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
</event>
<event name="preferred">
<description summary="mode is preferred">
This event advertises this mode as preferred.
</description>
</event>
<event name="finished">
<description summary="the mode has disappeared">
This event indicates that the mode is no longer available. The mode
object becomes inert. Clients should send a destroy request and release
any resources associated with it.
</description>
</event>
<!-- Version 3 additions -->
<request name="release" type="destructor" since="3">
<description summary="destroy the mode object">
This request indicates that the client will no longer use this mode
object.
</description>
</request>
</interface>
<interface name="zwlr_output_configuration_v1" version="4">
<description summary="output configuration">
This object is used by the client to describe a full output configuration.
First, the client needs to setup the output configuration. Each head can
be either enabled (and configured) or disabled. It is a protocol error to
send two enable_head or disable_head requests with the same head. It is a
protocol error to omit a head in a configuration.
Then, the client can apply or test the configuration. The compositor will
then reply with a succeeded, failed or cancelled event. Finally the client
should destroy the configuration object.
</description>
<enum name="error">
<entry name="already_configured_head" value="1"
summary="head has been configured twice"/>
<entry name="unconfigured_head" value="2"
summary="head has not been configured"/>
<entry name="already_used" value="3"
summary="request sent after configuration has been applied or tested"/>
</enum>
<request name="enable_head">
<description summary="enable and configure a head">
Enable a head. This request creates a head configuration object that can
be used to change the head's properties.
</description>
<arg name="id" type="new_id" interface="zwlr_output_configuration_head_v1"
summary="a new object to configure the head"/>
<arg name="head" type="object" interface="zwlr_output_head_v1"
summary="the head to be enabled"/>
</request>
<request name="disable_head">
<description summary="disable a head">
Disable a head.
</description>
<arg name="head" type="object" interface="zwlr_output_head_v1"
summary="the head to be disabled"/>
</request>
<request name="apply">
<description summary="apply the configuration">
Apply the new output configuration.
In case the configuration is successfully applied, there is no guarantee
that the new output state matches completely the requested
configuration. For instance, a compositor might round the scale if it
doesn't support fractional scaling.
After this request has been sent, the compositor must respond with an
succeeded, failed or cancelled event. Sending a request that isn't the
destructor is a protocol error.
</description>
</request>
<request name="test">
<description summary="test the configuration">
Test the new output configuration. The configuration won't be applied,
but will only be validated.
Even if the compositor succeeds to test a configuration, applying it may
fail.
After this request has been sent, the compositor must respond with an
succeeded, failed or cancelled event. Sending a request that isn't the
destructor is a protocol error.
</description>
</request>
<event name="succeeded">
<description summary="configuration changes succeeded">
Sent after the compositor has successfully applied the changes or
tested them.
Upon receiving this event, the client should destroy this object.
If the current configuration has changed, events to describe the changes
will be sent followed by a wlr_output_manager.done event.
</description>
</event>
<event name="failed">
<description summary="configuration changes failed">
Sent if the compositor rejects the changes or failed to apply them. The
compositor should revert any changes made by the apply request that
triggered this event.
Upon receiving this event, the client should destroy this object.
</description>
</event>
<event name="cancelled">
<description summary="configuration has been cancelled">
Sent if the compositor cancels the configuration because the state of an
output changed and the client has outdated information (e.g. after an
output has been hotplugged).
The client can create a new configuration with a newer serial and try
again.
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the output configuration">
Using this request a client can tell the compositor that it is not going
to use the configuration object anymore. Any changes to the outputs
that have not been applied will be discarded.
This request also destroys wlr_output_configuration_head objects created
via this object.
</description>
</request>
</interface>
<interface name="zwlr_output_configuration_head_v1" version="4">
<description summary="head configuration">
This object is used by the client to update a single head's configuration.
It is a protocol error to set the same property twice.
</description>
<enum name="error">
<entry name="already_set" value="1" summary="property has already been set"/>
<entry name="invalid_mode" value="2" summary="mode doesn't belong to head"/>
<entry name="invalid_custom_mode" value="3" summary="mode is invalid"/>
<entry name="invalid_transform" value="4" summary="transform value outside enum"/>
<entry name="invalid_scale" value="5" summary="scale negative or zero"/>
<entry name="invalid_adaptive_sync_state" value="6" since="4"
summary="invalid enum value used in the set_adaptive_sync request"/>
</enum>
<request name="set_mode">
<description summary="set the mode">
This request sets the head's mode.
</description>
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
</request>
<request name="set_custom_mode">
<description summary="set a custom mode">
This request assigns a custom mode to the head. The size is given in
physical hardware units of the output device. If set to zero, the
refresh rate is unspecified.
It is a protocol error to set both a mode and a custom mode.
</description>
<arg name="width" type="int" summary="width of the mode in hardware units"/>
<arg name="height" type="int" summary="height of the mode in hardware units"/>
<arg name="refresh" type="int" summary="vertical refresh rate in mHz or zero"/>
</request>
<request name="set_position">
<description summary="set the position">
This request sets the head's position in the global compositor space.
</description>
<arg name="x" type="int" summary="x position in the global compositor space"/>
<arg name="y" type="int" summary="y position in the global compositor space"/>
</request>
<request name="set_transform">
<description summary="set the transform">
This request sets the head's transform.
</description>
<arg name="transform" type="int" enum="wl_output.transform"/>
</request>
<request name="set_scale">
<description summary="set the scale">
This request sets the head's scale.
</description>
<arg name="scale" type="fixed"/>
</request>
<!-- Version 4 additions -->
<request name="set_adaptive_sync" since="4">
<description summary="enable/disable adaptive sync">
This request enables/disables adaptive sync. Adaptive sync is also
known as Variable Refresh Rate or VRR.
</description>
<arg name="state" type="uint" enum="zwlr_output_head_v1.adaptive_sync_state"/>
</request>
</interface>
</protocol>

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_virtual_pointer_unstable_v1">
<copyright>
Copyright © 2019 Josef Gajdusek
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="zwlr_virtual_pointer_v1" version="2">
<description summary="virtual pointer">
This protocol allows clients to emulate a physical pointer device. The
requests are mostly mirror opposites of those specified in wl_pointer.
</description>
<enum name="error">
<entry name="invalid_axis" value="0"
summary="client sent invalid axis enumeration value" />
<entry name="invalid_axis_source" value="1"
summary="client sent invalid axis source enumeration value" />
</enum>
<request name="motion">
<description summary="pointer relative motion event">
The pointer has moved by a relative amount to the previous request.
Values are in the global compositor space.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="displacement on the x-axis"/>
<arg name="dy" type="fixed" summary="displacement on the y-axis"/>
</request>
<request name="motion_absolute">
<description summary="pointer absolute motion event">
The pointer has moved in an absolute coordinate frame.
Value of x can range from 0 to x_extent, value of y can range from 0
to y_extent.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="x" type="uint" summary="position on the x-axis"/>
<arg name="y" type="uint" summary="position on the y-axis"/>
<arg name="x_extent" type="uint" summary="extent of the x-axis"/>
<arg name="y_extent" type="uint" summary="extent of the y-axis"/>
</request>
<request name="button">
<description summary="button event">
A button was pressed or released.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="button" type="uint" summary="button that produced the event"/>
<arg name="state" type="uint" enum="wl_pointer.button_state" summary="physical state of the button"/>
</request>
<request name="axis">
<description summary="axis event">
Scroll and other axis requests.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/>
<arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/>
</request>
<request name="frame">
<description summary="end of a pointer event sequence">
Indicates the set of events that logically belong together.
</description>
</request>
<request name="axis_source">
<description summary="axis source event">
Source information for scroll and other axis.
</description>
<arg name="axis_source" type="uint" enum="wl_pointer.axis_source" summary="source of the axis event"/>
</request>
<request name="axis_stop">
<description summary="axis stop event">
Stop notification for scroll and other axes.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="the axis stopped with this event"/>
</request>
<request name="axis_discrete">
<description summary="axis click event">
Discrete step information for scroll and other axes.
This event allows the client to extend data normally sent using the axis
event with discrete value.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/>
<arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/>
<arg name="discrete" type="int" summary="number of steps"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual pointer object"/>
</request>
</interface>
<interface name="zwlr_virtual_pointer_manager_v1" version="2">
<description summary="virtual pointer manager">
This object allows clients to create individual virtual pointer objects.
</description>
<request name="create_virtual_pointer">
<description summary="Create a new virtual pointer">
Creates a new virtual pointer. The optional seat is a suggestion to the
compositor.
</description>
<arg name="seat" type="object" interface="wl_seat" allow-null="true"/>
<arg name="id" type="new_id" interface="zwlr_virtual_pointer_v1"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual pointer manager"/>
</request>
<!-- Version 2 additions -->
<request name="create_virtual_pointer_with_output" since="2">
<description summary="Create a new virtual pointer">
Creates a new virtual pointer. The seat and the output arguments are
optional. If the seat argument is set, the compositor should assign the
input device to the requested seat. If the output argument is set, the
compositor should map the input device to the requested output.
</description>
<arg name="seat" type="object" interface="wl_seat" allow-null="true"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="id" type="new_id" interface="zwlr_virtual_pointer_v1"/>
</request>
</interface>
</protocol>

View File

@@ -2,11 +2,12 @@
cp -fr ./src/version.h.in ./src/version.h
HASH=$(git rev-parse HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BRANCH=$(git branch --show-current)
MESSAGE=$(git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g')
DATE=$(git show ${GIT_COMMIT_HASH} --no-patch --format=%cd --date=local)
DIRTY=$(git diff-index --quiet HEAD -- || echo dirty)
TAG=$(git describe --tags)
COMMITS=$(git rev-list --count HEAD)
sed -i -e "s#@HASH@#${HASH}#" ./src/version.h
sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h
@@ -14,3 +15,4 @@ sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h
sed -i -e "s#@DATE@#${DATE}#" ./src/version.h
sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h
sed -i -e "s#@TAG@#${TAG}#" ./src/version.h
sed -i -e "s#@COMMITS@#${COMMITS}#" ./src/version.h

View File

@@ -1,21 +1,13 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 71766a8c..bd8cdc0e 100755
index f54cdf5d..ad7c3e73 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -101,7 +101,7 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
-pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2 hyprcursor) # we do not check for wlroots, as we provide it ourselves
+pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2 hyprcursor libffi) # we do not check for wlroots, as we provide it ourselves
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
@@ -121,6 +121,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
@@ -130,6 +130,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan)
+ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a)
+ pkg_check_modules(ffidep REQUIRED IMPORTED_TARGET libffi)
+ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a PkgConfig::ffidep)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -51,50 +51,33 @@ class CCompositor {
wlr_data_device_manager* m_sWLRDataDevMgr;
wlr_drm* m_sWRLDRM;
wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr;
wlr_xdg_activation_v1* m_sWLRXDGActivation;
wlr_output_layout* m_sWLROutputLayout;
wlr_idle_notifier_v1* m_sWLRIdleNotifier;
wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell;
wlr_cursor* m_sWLRCursor;
wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr;
wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation;
wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_pointer_constraints_v1* m_sWLRPointerConstraints;
wlr_relative_pointer_manager_v1* m_sWLRRelPointerMgr;
wlr_server_decoration_manager* m_sWLRServerDecoMgr;
wlr_xdg_decoration_manager_v1* m_sWLRXDGDecoMgr;
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
wlr_foreign_toplevel_manager_v1* m_sWLRToplevelMgr;
wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
wlr_idle_inhibit_manager_v1* m_sWLRIdleInhibitMgr;
wlr_pointer_gestures_v1* m_sWLRPointerGestures;
wlr_output_power_manager_v1* m_sWLROutputPowerMgr;
wlr_input_method_manager_v2* m_sWLRIMEMgr;
wlr_text_input_manager_v3* m_sWLRTextInputMgr;
wlr_xdg_activation_v1* m_sWLRActivation;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend;
wlr_session_lock_manager_v1* m_sWLRSessionLockMgr;
wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr;
wlr_cursor_shape_manager_v1* m_sWLRCursorShapeMgr;
wlr_tearing_control_manager_v1* m_sWLRTearingControlMgr;
// ------------------------------------------------- //
std::string m_szHyprTempDataRoot = "";
std::string m_szWLDisplaySocket = "";
std::string m_szInstanceSignature = "";
std::string m_szInstancePath = "";
std::string m_szCurrentSplash = "error";
std::vector<std::shared_ptr<CMonitor>> m_vMonitors;
std::vector<std::shared_ptr<CMonitor>> m_vRealMonitors; // for all monitors, even those turned off
std::vector<std::unique_ptr<CWindow>> m_vWindows;
std::vector<std::unique_ptr<CWorkspace>> m_vWorkspaces;
std::vector<CWindow*> m_vWindowsFadingOut;
std::vector<SLayerSurface*> m_vSurfacesFadingOut;
std::vector<SP<CMonitor>> m_vMonitors;
std::vector<SP<CMonitor>> m_vRealMonitors; // for all monitors, even those turned off
std::vector<PHLWINDOW> m_vWindows;
std::vector<PHLWORKSPACE> m_vWorkspaces;
std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
std::vector<PHLLSREF> m_vSurfacesFadingOut;
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
@@ -105,10 +88,10 @@ class CCompositor {
void removeLockFile();
wlr_surface* m_pLastFocus = nullptr;
CWindow* m_pLastWindow = nullptr;
PHLWINDOWREF m_pLastWindow;
CMonitor* m_pLastMonitor = nullptr;
std::vector<CWindow*> m_vWindowFocusHistory; // first element is the most recently focused.
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
SSeat m_sSeat;
@@ -128,41 +111,40 @@ class CCompositor {
CMonitor* getMonitorFromDesc(const std::string&);
CMonitor* getMonitorFromCursor();
CMonitor* getMonitorFromVector(const Vector2D&);
void removeWindowFromVectorSafe(CWindow*);
void focusWindow(CWindow*, wlr_surface* pSurface = nullptr);
void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr);
bool windowExists(CWindow*);
bool windowValidMapped(CWindow*);
void removeWindowFromVectorSafe(PHLWINDOW);
void focusWindow(PHLWINDOW, wlr_surface* pSurface = nullptr);
void focusSurface(wlr_surface*, PHLWINDOW pWindowOwner = nullptr);
bool monitorExists(CMonitor*);
CWindow* vectorToWindowUnified(const Vector2D&, uint8_t properties, CWindow* pIgnoreWindow = nullptr);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**);
wlr_surface* vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, SLayerSurface**);
wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*);
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<PHLLS>*, Vector2D*, PHLLS*);
wlr_surface* vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*);
wlr_surface* vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, wlr_surface*);
CMonitor* getMonitorFromOutput(wlr_output*);
CMonitor* getRealMonitorFromOutput(wlr_output*);
CWindow* getWindowFromSurface(wlr_surface*);
CWindow* getWindowFromHandle(uint32_t);
CWindow* getWindowFromZWLRHandle(wl_resource*);
bool isWorkspaceVisible(const int&);
CWorkspace* getWorkspaceByID(const int&);
CWorkspace* getWorkspaceByName(const std::string&);
CWorkspace* getWorkspaceByString(const std::string&);
PHLWINDOW getWindowFromSurface(wlr_surface*);
PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE);
PHLWORKSPACE getWorkspaceByID(const int&);
PHLWORKSPACE getWorkspaceByName(const std::string&);
PHLWORKSPACE getWorkspaceByString(const std::string&);
void sanityCheckWorkspaces();
void updateWorkspaceWindowDecos(const int&);
int getWindowsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {});
CWindow* getUrgentWindow();
void updateWorkspaceSpecialRenderData(const int&);
int getWindowsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroupsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
PHLWINDOW getUrgentWindow();
bool hasUrgentWindowOnWorkspace(const int&);
CWindow* getFirstWindowOnWorkspace(const int&);
CWindow* getTopLeftWindowOnWorkspace(const int&);
CWindow* getFullscreenWindowOnWorkspace(const int&);
PHLWINDOW getFirstWindowOnWorkspace(const int&);
PHLWINDOW getTopLeftWindowOnWorkspace(const int&);
PHLWINDOW getFullscreenWindowOnWorkspace(const int&);
bool doesSeatAcceptInput(wlr_surface*);
bool isWindowActive(CWindow*);
void changeWindowZOrder(CWindow*, bool);
bool isWindowActive(PHLWINDOW);
void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const int& monid);
CWindow* getWindowInDirection(CWindow*, char);
CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
int getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
@@ -170,41 +152,40 @@ class CCompositor {
CMonitor* getMonitorInDirection(CMonitor*, const char&);
void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const int64_t& id);
void updateWindowAnimatedDecorationValues(CWindow*);
void updateWindowAnimatedDecorationValues(PHLWINDOW);
int getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false);
void moveWorkspaceToMonitor(PHLWORKSPACE, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&);
void setWindowFullscreen(CWindow*, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
void updateFullscreenFadeOnWorkspace(CWorkspace*);
CWindow* getX11Parent(CWindow*);
void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*);
void addToFadingOutSafe(SLayerSurface*);
void addToFadingOutSafe(CWindow*);
CWindow* getWindowByRegex(const std::string&);
void addToFadingOutSafe(PHLLS);
void addToFadingOutSafe(PHLWINDOW);
PHLWINDOW getWindowByRegex(const std::string&);
void warpCursorTo(const Vector2D&, bool force = false);
SLayerSurface* getLayerSurfaceFromWlr(wlr_layer_surface_v1*);
SLayerSurface* getLayerSurfaceFromSurface(wlr_surface*);
void closeWindow(CWindow*);
PHLLS getLayerSurfaceFromWlr(wlr_layer_surface_v1*);
PHLLS getLayerSurfaceFromSurface(wlr_surface*);
void closeWindow(PHLWINDOW);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&);
CWorkspace* createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused!
PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const int&, const std::string& name = "");
void setActiveMonitor(CMonitor*);
bool isWorkspaceSpecial(const int&);
int getNewSpecialID();
void performUserChecks();
void moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace);
CWindow* getForceFocus();
void notifyIdleActivity();
void setIdleActivityInhibit(bool inhibit);
void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace);
PHLWINDOW getForceFocus();
void arrangeMonitors();
void enterUnsafeState();
void leaveUnsafeState();
void setPreferredScaleForSurface(wlr_surface* pSurface, double scale);
void setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform);
void updateSuspendedStates();
PHLWINDOW windowForCPointer(CWindow*);
std::string explicitConfigPath;

View File

@@ -70,3 +70,5 @@ struct SHyprCtlCommand {
bool exact = true;
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
};
typedef std::function<void(void*, SCallbackInfo&, std::any)> HOOK_CALLBACK_FN;

View File

@@ -16,6 +16,7 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <ranges>
extern "C" char** environ;
@@ -318,6 +319,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("general:layout", {"dwindle"});
m_pConfig->addConfigValue("general:allow_tearing", Hyprlang::INT{0});
m_pConfig->addConfigValue("general:resize_corner", Hyprlang::INT{0});
m_pConfig->addConfigValue("general:default_cursor_monitor", {STRVAL_EMPTY});
m_pConfig->addConfigValue("misc:disable_hyprland_logo", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:disable_splash_rendering", Hyprlang::INT{0});
@@ -350,6 +352,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:enable_hyprcursor", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:hide_cursor_on_key_press", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:initial_workspace_tracking", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1});
@@ -376,6 +379,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("debug:error_limit", Hyprlang::INT{5});
m_pConfig->addConfigValue("debug:watchdog_timeout", Hyprlang::INT{5});
m_pConfig->addConfigValue("debug:disable_scale_checks", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:colored_stdout_logs", Hyprlang::INT{1});
m_pConfig->addConfigValue("decoration:rounding", Hyprlang::INT{0});
m_pConfig->addConfigValue("decoration:blur:enabled", Hyprlang::INT{1});
@@ -440,6 +444,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("input:follow_mouse", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:mouse_refocus", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:special_fallthrough", Hyprlang::INT{0});
m_pConfig->addConfigValue("input:off_window_axis_events", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:sensitivity", {0.f});
m_pConfig->addConfigValue("input:accel_profile", {STRVAL_EMPTY});
m_pConfig->addConfigValue("input:kb_file", {STRVAL_EMPTY});
@@ -502,7 +507,6 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock", Hyprlang::INT{1});
m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock_threshold", Hyprlang::INT{10});
m_pConfig->addConfigValue("gestures:workspace_swipe_forever", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_numbered", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_use_r", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_touch", Hyprlang::INT{0});
@@ -591,7 +595,7 @@ CConfigManager::CConfigManager() {
setDefaultAnimationVars();
resetHLConfig();
Debug::log(LOG,
Debug::log(INFO,
"!!!!HEY YOU, YES YOU!!!!: further logs to stdout / logfile are disabled by default. BEFORE SENDING THIS LOG, ENABLE THEM. Use debug:disable_logs = false to do so: "
"https://wiki.hyprland.org/Configuring/Variables/#debug");
@@ -646,6 +650,10 @@ void CConfigManager::setDefaultAnimationVars() {
INITANIMCFG("windowsOut");
INITANIMCFG("windowsMove");
// layers
INITANIMCFG("layersIn");
INITANIMCFG("layersOut");
// fade
INITANIMCFG("fadeIn");
INITANIMCFG("fadeOut");
@@ -669,6 +677,9 @@ void CConfigManager::setDefaultAnimationVars() {
CREATEANIMCFG("borderangle", "global");
CREATEANIMCFG("workspaces", "global");
CREATEANIMCFG("layersIn", "layers");
CREATEANIMCFG("layersOut", "layers");
CREATEANIMCFG("windowsIn", "windows");
CREATEANIMCFG("windowsOut", "windows");
CREATEANIMCFG("windowsMove", "windows");
@@ -679,6 +690,8 @@ void CConfigManager::setDefaultAnimationVars() {
CREATEANIMCFG("fadeShadow", "fade");
CREATEANIMCFG("fadeDim", "fade");
CREATEANIMCFG("fadeLayers", "fade");
CREATEANIMCFG("fadeLayersIn", "fadeLayers");
CREATEANIMCFG("fadeLayersOut", "fadeLayers");
CREATEANIMCFG("specialWorkspace", "workspaces");
}
@@ -782,12 +795,11 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
refreshGroupBarGradients();
// Updates dynamic window and workspace rules
for (auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped)
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->inert())
continue;
w->updateDynamicRules();
w->updateSpecialRenderData();
g_pCompositor->updateWorkspaceWindows(w->m_iID);
g_pCompositor->updateWorkspaceSpecialRenderData(w->m_iID);
}
// Update window border colors
@@ -809,6 +821,8 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
if (Debug::disableStdout && isFirstLaunch)
Debug::log(LOG, "Disabling stdout logs! Check the log for further logs.");
Debug::coloredLogs = reinterpret_cast<int64_t* const*>(m_pConfig->getConfigValuePtr("debug:colored_stdout_logs")->getDataStaticPtr());
for (auto& m : g_pCompositor->m_vMonitors) {
// mark blur dirty
g_pHyprOpenGL->markBlurDirtyForMonitor(m.get());
@@ -817,6 +831,10 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
// Force the compositor to fully re-render all monitors
m->forceFullFrames = 2;
// also force mirrors, as the aspect ratio could've changed
for (auto& mirror : m->mirrors)
mirror->forceFullFrames = 3;
}
// Reset no monitor reload
@@ -937,7 +955,7 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s
}
SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) {
for (auto& r : m_dMonitorRules) {
for (auto& r : m_dMonitorRules | std::views::reverse) {
if (PMONITOR.matchesStaticSelector(r.name)) {
return r;
}
@@ -953,20 +971,69 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) {
Debug::log(WARN, "No rules configured. Using the default hardcoded one.");
return SMonitorRule{.name = "", .resolution = Vector2D(0, 0), .offset = Vector2D(-INT32_MAX, -INT32_MAX), .scale = -1}; // 0, 0 is preferred and -1, -1 is auto
return SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT,
.name = "",
.resolution = Vector2D(0, 0),
.offset = Vector2D(-INT32_MAX, -INT32_MAX),
.scale = -1}; // 0, 0 is preferred and -1, -1 is auto
}
std::vector<SWorkspaceRule> CConfigManager::getWorkspaceRulesFor(CWorkspace* pWorkspace) {
std::vector<SWorkspaceRule> results;
SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) {
SWorkspaceRule mergedRule{};
for (auto& rule : m_dWorkspaceRules) {
if (pWorkspace->matchesStaticSelector(rule.workspaceString))
results.push_back(rule);
}
return results;
if (!pWorkspace->matchesStaticSelector(rule.workspaceString))
continue;
mergedRule = mergeWorkspaceRules(mergedRule, rule);
}
std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow, bool dynamic, bool shadowExec) {
if (!g_pCompositor->windowExists(pWindow))
return mergedRule;
}
SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1, const SWorkspaceRule& rule2) {
SWorkspaceRule mergedRule = rule1;
if (rule1.monitor.empty())
mergedRule.monitor = rule2.monitor;
if (rule1.workspaceString.empty())
mergedRule.workspaceString = rule2.workspaceString;
if (rule1.workspaceName.empty())
mergedRule.workspaceName = rule2.workspaceName;
if (rule1.workspaceId == WORKSPACE_INVALID)
mergedRule.workspaceId = rule2.workspaceId;
if (rule2.isDefault)
mergedRule.isDefault = true;
if (rule2.isPersistent)
mergedRule.isPersistent = true;
if (rule2.gapsIn.has_value())
mergedRule.gapsIn = rule2.gapsIn;
if (rule2.gapsOut.has_value())
mergedRule.gapsOut = rule2.gapsOut;
if (rule2.borderSize.has_value())
mergedRule.borderSize = rule2.borderSize;
if (rule2.border.has_value())
mergedRule.border = rule2.border;
if (rule2.rounding.has_value())
mergedRule.rounding = rule2.rounding;
if (rule2.decorate.has_value())
mergedRule.decorate = rule2.decorate;
if (rule2.shadow.has_value())
mergedRule.shadow = rule2.shadow;
if (rule2.onCreatedEmptyRunCmd.has_value())
mergedRule.onCreatedEmptyRunCmd = rule2.onCreatedEmptyRunCmd;
if (rule2.defaultName.has_value())
mergedRule.defaultName = rule2.defaultName;
if (!rule2.layoutopts.empty()) {
for (const auto& layoutopt : rule2.layoutopts) {
mergedRule.layoutopts[layoutopt.first] = layoutopt.second;
}
}
return mergedRule;
}
std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) {
if (!valid(pWindow))
return std::vector<SWindowRule>();
std::vector<SWindowRule> returns;
@@ -1051,18 +1118,18 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow, bool
}
if (rule.bFocus != -1) {
if (rule.bFocus != (g_pCompositor->m_pLastWindow == pWindow))
if (rule.bFocus != (g_pCompositor->m_pLastWindow.lock() == pWindow))
continue;
}
if (!rule.szOnWorkspace.empty()) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule.szOnWorkspace))
continue;
}
if (!rule.szWorkspace.empty()) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (!PWORKSPACE)
continue;
@@ -1121,7 +1188,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow, bool
return returns;
}
std::vector<SLayerRule> CConfigManager::getMatchingRules(SLayerSurface* pLS) {
std::vector<SLayerRule> CConfigManager::getMatchingRules(PHLLS pLS) {
std::vector<SLayerRule> returns;
if (!pLS->layerSurface || pLS->fadingOut)
@@ -1129,7 +1196,7 @@ std::vector<SLayerRule> CConfigManager::getMatchingRules(SLayerSurface* pLS) {
for (auto& lr : m_dLayerRules) {
if (lr.targetNamespace.starts_with("address:0x")) {
if (std::format("address:0x{:x}", (uintptr_t)pLS) != lr.targetNamespace)
if (std::format("address:0x{:x}", (uintptr_t)pLS.get()) != lr.targetNamespace)
continue;
} else {
std::regex NSCHECK(lr.targetNamespace);
@@ -1179,6 +1246,10 @@ void CConfigManager::dispatchExecOnce() {
g_pCompositor->performUserChecks();
}
void CConfigManager::appendMonitorRule(const SMonitorRule& r) {
m_dMonitorRules.emplace_back(r);
}
void CConfigManager::performMonitorReload() {
bool overAgain = false;
@@ -1287,7 +1358,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
/* fullscreen */
m->vrrActive = true;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m->activeWorkspace);
const auto PWORKSPACE = m->activeWorkspace;
if (!PWORKSPACE)
return; // ???
@@ -1597,6 +1668,21 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
if (ARGS[2].starts_with("auto")) {
newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX);
// If this is the first monitor rule needs to be on the right.
if (ARGS[2] == "auto-right" || ARGS[2] == "auto" || m_dMonitorRules.empty())
newrule.autoDir = eAutoDirs::DIR_AUTO_RIGHT;
else if (ARGS[2] == "auto-left")
newrule.autoDir = eAutoDirs::DIR_AUTO_LEFT;
else if (ARGS[2] == "auto-up")
newrule.autoDir = eAutoDirs::DIR_AUTO_UP;
else if (ARGS[2] == "auto-down")
newrule.autoDir = eAutoDirs::DIR_AUTO_DOWN;
else {
Debug::log(WARN,
"Invalid auto direction. Valid options are 'auto',"
"'auto-up', 'auto-down', 'auto-left', and 'auto-right'.");
error += "invalid auto direction ";
}
} else {
if (!ARGS[2].contains("x")) {
error += "invalid offset ";
@@ -1742,12 +1828,15 @@ std::optional<std::string> CConfigManager::handleAnimation(const std::string& co
PANIM->second.overridden = true;
PANIM->second.pValues = &PANIM->second;
// on/off
PANIM->second.internalEnabled = ARGS[1] == "1";
// This helper casts strings like "1", "true", "off", "yes"... to int.
int64_t enabledInt = configStringToInt(ARGS[1]) == 1;
if (ARGS[1] != "0" && ARGS[1] != "1")
// Checking that the int is 1 or 0 because the helper can return integers out of range.
if (enabledInt != 0 && enabledInt != 1)
return "invalid animation on/off state";
PANIM->second.internalEnabled = configStringToInt(ARGS[1]) == 1;
if (PANIM->second.internalEnabled) {
// speed
if (isNumber(ARGS[2], true)) {
@@ -1906,12 +1995,12 @@ bool windowRuleValid(const std::string& RULE) {
RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" ||
RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || RULE == "forcergbx" ||
RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") ||
RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent");
RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent") || RULE.starts_with("plugin:");
}
bool layerRuleValid(const std::string& RULE) {
return RULE == "noanim" || RULE == "blur" || RULE == "blurpopups" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE.starts_with("xray") ||
RULE.starts_with("animation");
return RULE == "noanim" || RULE == "blur" || RULE == "blurpopups" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE == "dimaround" ||
RULE.starts_with("xray") || RULE.starts_with("animation");
}
std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
@@ -2045,7 +2134,7 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
result = removeBeginEndSpacesTabs(result);
if (result.back() == ',')
if (!result.empty() && result.back() == ',')
result.pop_back();
return result;
@@ -2198,8 +2287,8 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
// rules = value.substr(WORKSPACE_DELIM + 1);
// }
const static std::string ruleOnCreatedEmtpy = "on-created-empty:";
const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length();
const static std::string ruleOnCreatedEmpty = "on-created-empty:";
const static int ruleOnCreatedEmptyLen = ruleOnCreatedEmpty.length();
auto assignRule = [&](std::string rule) -> std::optional<std::string> {
size_t delim = std::string::npos;
@@ -2235,8 +2324,8 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
wsRule.isPersistent = configStringToInt(rule.substr(delim + 11));
else if ((delim = rule.find("defaultName:")) != std::string::npos)
wsRule.defaultName = rule.substr(delim + 12);
else if ((delim = rule.find(ruleOnCreatedEmtpy)) != std::string::npos)
wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmtpyLen));
else if ((delim = rule.find(ruleOnCreatedEmpty)) != std::string::npos)
wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmptyLen));
else if ((delim = rule.find("layoutopt:")) != std::string::npos) {
std::string opt = rule.substr(delim + 10);
if (!opt.contains(":")) {
@@ -2269,7 +2358,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
if (IT == m_dWorkspaceRules.end())
m_dWorkspaceRules.emplace_back(wsRule);
else
*IT = wsRule;
*IT = mergeWorkspaceRules(*IT, wsRule);
return {};
}

View File

@@ -16,6 +16,8 @@
#include "../helpers/WLClasses.hpp"
#include "../helpers/Monitor.hpp"
#include "../helpers/VarList.hpp"
#include "../desktop/Window.hpp"
#include "../desktop/LayerSurface.hpp"
#include "defaultConfig.hpp"
#include "ConfigDataValues.hpp"
@@ -27,8 +29,6 @@
#define HANDLE void*
class CWindow;
struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
@@ -105,15 +105,15 @@ class CConfigManager {
static std::string getMainConfigPath();
SMonitorRule getMonitorRuleFor(const CMonitor&);
std::vector<SWorkspaceRule> getWorkspaceRulesFor(CWorkspace*);
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
std::string getDefaultWorkspaceFor(const std::string&);
CMonitor* getBoundMonitorForWS(const std::string&);
std::string getBoundMonitorStringForWS(const std::string&);
const std::deque<SWorkspaceRule>& getAllWorkspaceRules();
std::vector<SWindowRule> getMatchingRules(CWindow*, bool dynamic = true, bool shadowExec = false);
std::vector<SLayerRule> getMatchingRules(SLayerSurface*);
std::vector<SWindowRule> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
std::vector<SLayerRule> getMatchingRules(PHLLS);
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
@@ -127,6 +127,7 @@ class CConfigManager {
void dispatchExecOnce();
void performMonitorReload();
void appendMonitorRule(const SMonitorRule&);
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
@@ -204,6 +205,7 @@ class CConfigManager {
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
void reload();
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
};
inline std::unique_ptr<CConfigManager> g_pConfigManager;

View File

@@ -5,6 +5,7 @@
#include <hyprlang.hpp>
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include "ConfigManager.hpp"
template <typename T>
class CConfigValue {

View File

@@ -9,87 +9,116 @@ inline const std::string AUTOCONFIG = R"#(
# OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS.
# #######################################################################################
#
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/
# Please note not all available settings / options are set here.
# For a full list, see the wiki
#
autogenerated = 1 # remove this line to remove the warning
# You can split this configuration into multiple files
# Create your files separately and then link them to this file like this:
# source = ~/.config/hypr/myColors.conf
################
### MONITORS ###
################
# See https://wiki.hyprland.org/Configuring/Monitors/
monitor=,preferred,auto,auto
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
###################
### MY PROGRAMS ###
###################
# Execute your favorite apps at launch
# exec-once = waybar & hyprpaper & firefox
# Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf
# See https://wiki.hyprland.org/Configuring/Keywords/
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars.
#################
### AUTOSTART ###
#################
# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:
# exec-once = $terminal
# exec-once = nm-applet &
# exec-once = waybar & hyprpaper & firefox
#############################
### ENVIRONMENT VARIABLES ###
#############################
# See https://wiki.hyprland.org/Configuring/Environment-variables/
env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that
env = HYPRCURSOR_SIZE,24
# For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
#####################
### LOOK AND FEEL ###
#####################
touchpad {
natural_scroll = no
}
sensitivity = 0 # -1.0 to 1.0, 0 means no modification.
}
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
gaps_in = 5
gaps_out = 20
border_size = 2
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
col.inactive_border = rgba(595959aa)
layout = dwindle
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
rounding = 10
# Change transparency of focused and unfocused windows
active_opacity = 1.0
inactive_opacity = 1.0
drop_shadow = true
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
}
drop_shadow = yes
shadow_range = 4
shadow_render_power = 3
col.shadow = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = yes
enabled = true
# Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = myBezier, 0.05, 0.9, 0.1, 1.05
@@ -101,25 +130,48 @@ animations {
animation = workspaces, 1, 6, default
}
dwindle {
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
pseudotile = yes # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = yes # you probably want this
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = true # You probably want this
}
master {
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_is_master = true
}
gestures {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
workspace_swipe = off
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
#############
### INPUT ###
#############
# https://wiki.hyprland.org/Configuring/Variables/#input
input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
touchpad {
natural_scroll = false
}
}
# https://wiki.hyprland.org/Configuring/Variables/#gestures
gestures {
workspace_swipe = false
}
# Example per-device config
@@ -129,16 +181,13 @@ device {
sensitivity = -0.5
}
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
####################
### KEYBINDINGSS ###
####################
# See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER
# See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, $terminal
@@ -191,4 +240,20 @@ bind = $mainMod, mouse_up, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
##############################
### WINDOWS AND WORKSPACES ###
##############################
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
)#";

View File

@@ -1,19 +1,19 @@
#include "CrashReporter.hpp"
#include <random>
#include <fcntl.h>
#include <sys/utsname.h>
#include <fstream>
#include <signal.h>
#include <link.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include "../plugins/PluginSystem.hpp"
#include "../signal-safe.hpp"
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/sysctl.h>
#endif
std::string getRandomMessage() {
const std::vector<std::string> MESSAGES = {"Sorry, didn't mean to...",
static char const* const MESSAGES[] = {"Sorry, didn't mean to...",
"This was an accident, I swear!",
"Calm down, it was a misinput! MISINPUT!",
"Oops",
@@ -28,32 +28,100 @@ std::string getRandomMessage() {
"I hope you didn't have any unsaved progress.",
"All these computers..."};
std::random_device dev;
std::mt19937 engine(dev());
std::uniform_int_distribution<> distribution(0, MESSAGES.size() - 1);
// <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[distribution(engine)];
[[noreturn]] inline void exit_with_error(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.
perror("");
abort();
}
void CrashReporter::createAndSaveCrash(int sig) {
int reportFd;
// get the backtrace
const int PID = getpid();
// 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;
std::string finalCrashReport = "";
const auto HOME = sig_getenv("HOME");
const auto CACHE_HOME = sig_getenv("XDG_CACHE_HOME");
if (CACHE_HOME && CACHE_HOME[0] != '\0') {
reportPath += CACHE_HOME;
reportPath += "/hyprland";
} else if (HOME && HOME[0] != '\0') {
reportPath += HOME;
reportPath += "/.cache/hyprland";
} else {
exit_with_error("$CACHE_HOME and $HOME not set, nowhere to report crash\n");
return;
}
int ret = mkdir(reportPath.get_str(), S_IRWXU);
//__asm__("int $3");
if (ret < 0 && errno != EEXIST) {
exit_with_error("failed to mkdir() crash report directory\n");
}
reportPath += "/hyprlandCrashReport";
reportPath.write_num(getpid());
reportPath += ".txt";
{
BufFileWriter<64> stderr(2);
stderr += "Hyprland has crashed :( Consult the crash report at ";
if (!reportPath.boundsExceeded()) {
stderr += reportPath.get_str();
} else {
stderr += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]";
}
stderr += " for more information.\n";
stderr.flush();
}
reportFd = open(reportPath.get_str(), O_WRONLY | O_CREAT, S_IRWXU);
if (reportFd < 0) {
exit_with_error("Failed to open crash report path for writing");
}
}
BufFileWriter<512> finalCrashReport(reportFd);
finalCrashReport += "--------------------------------------------\n Hyprland Crash Report\n--------------------------------------------\n";
finalCrashReport += getRandomMessage() + "\n\n";
finalCrashReport += getRandomMessage();
finalCrashReport += "\n\n";
finalCrashReport += std::format("Hyprland received signal {} ({})\n\n", sig, (const char*)strsignal(sig));
finalCrashReport += "Hyprland received signal ";
finalCrashReport.writeNum(sig);
finalCrashReport += '(';
finalCrashReport += sig_strsignal(sig);
finalCrashReport += ")\nVersion: ";
finalCrashReport += GIT_COMMIT_HASH;
finalCrashReport += "\nTag: ";
finalCrashReport += GIT_TAG;
finalCrashReport += "\n\n";
finalCrashReport += std::format("Version: {}\nTag: {}\n\n", GIT_COMMIT_HASH, GIT_TAG);
if (g_pPluginSystem && !g_pPluginSystem->getAllPlugins().empty()) {
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";
for (auto& p : g_pPluginSystem->getAllPlugins()) {
finalCrashReport += std::format("\t{} ({}) {}\n", p->name, p->author, p->version);
size_t count = g_pPluginSystem->pluginCount();
CPlugin* plugins[count];
g_pPluginSystem->sig_getPlugins(plugins, count);
for (size_t i = 0; i < count; i++) {
auto p = plugins[i];
finalCrashReport += '\t';
finalCrashReport += p->name;
finalCrashReport += " (";
finalCrashReport += p->author;
finalCrashReport += ") ";
finalCrashReport += p->version;
finalCrashReport += '\n';
}
finalCrashReport += "\n\n";
@@ -61,21 +129,36 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "System info:\n";
{
struct utsname unameInfo;
uname(&unameInfo);
finalCrashReport += std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", std::string{unameInfo.sysname}, std::string{unameInfo.nodename},
std::string{unameInfo.release}, std::string{unameInfo.version});
finalCrashReport += "\tSystem name: ";
finalCrashReport += unameInfo.sysname;
finalCrashReport += "\n\tNode name: ";
finalCrashReport += unameInfo.nodename;
finalCrashReport += "\n\tRelease: ";
finalCrashReport += unameInfo.release;
finalCrashReport += "\n\tVersion: ";
finalCrashReport += unameInfo.version;
finalCrashReport += "\n\n";
}
finalCrashReport += "GPU:\n\t";
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
finalCrashReport.writeCmdOutput("pciconf -lv | fgrep -A4 vga");
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
finalCrashReport.writeCmdOutput("lspci -vnn | grep VGA");
#endif
finalCrashReport += "GPU:\n\t" + GPUINFO;
finalCrashReport += "\n\nos-release:\n";
finalCrashReport.writeCmdOutput("cat /etc/os-release | sed 's/^/\t/'");
finalCrashReport += std::format("\n\nos-release:\n\t{}\n\n\n", replaceInString(execAndGet("cat /etc/os-release"), "\n", "\n\t"));
// dladdr1()/backtrace_symbols()/this entire section allocates, and hence is NOT async-signal-safe.
// Make sure that we save the current known crash report information,
// so that if we are caught in a deadlock during a call to malloc(),
// there is still something to debug from.
finalCrashReport.flush();
finalCrashReport += "Backtrace:\n";
@@ -132,7 +215,10 @@ void CrashReporter::createAndSaveCrash(int sig) {
std::stringstream ssin(ADDR2LINE);
for (size_t i = 0; i < CALLSTACK.size(); ++i) {
finalCrashReport += std::format("\t#{} | {}", i, CALLSTACK[i].desc);
finalCrashReport += "\t#";
finalCrashReport.writeNum(i);
finalCrashReport += " | ";
finalCrashReport += CALLSTACK[i].desc;
std::string functionInfo;
std::string fileLineInfo;
std::getline(ssin, functionInfo);
@@ -142,32 +228,5 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += Debug::rollingLog.substr(Debug::rollingLog.find("\n") + 1);
const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME");
if (!HOME)
return;
std::ofstream ofs;
std::string reportDir;
if (!CACHE_HOME || std::string(CACHE_HOME).empty())
reportDir = std::string(HOME) + "/.cache/hyprland";
else
reportDir = std::string(CACHE_HOME) + "/hyprland";
if (!std::filesystem::exists(reportDir))
std::filesystem::create_directory(reportDir);
const auto path = reportDir + "/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc);
ofs << finalCrashReport;
ofs.close();
Debug::disableStdout = false;
Debug::log(CRIT, "Hyprland has crashed :( Consult the crash report at {} for more information.", path);
finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find("\n") + 1);
}

View File

@@ -15,24 +15,19 @@
#include <string>
#include <typeindex>
#include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp"
#include "../managers/CursorManager.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
str.pop_back();
}
static std::string getWorkspaceNameFromSpecialID(const int workspaceID) {
if (workspaceID == 0)
return "";
const auto* workspace = g_pCompositor->getWorkspaceByID(workspaceID);
if (!workspace)
return "";
return workspace->m_szName;
}
static std::string formatToString(uint32_t drmFormat) {
switch (drmFormat) {
case DRM_FORMAT_XRGB2101010: return "XRGB2101010";
@@ -111,16 +106,18 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
"dpmsStatus": {},
"vrr": {},
"activelyTearing": {},
"disabled": {},
"currentFormat": "{}",
"availableModes": [{}]
}},)#",
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
m->activeWorkspace, (m->activeWorkspace == -1 ? "" : escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName)), m->specialWorkspaceID,
escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), escapeJSONStrings(m->output->make ? m->output->make : ""),
escapeJSONStrings(m->output->model ? m->output->model : ""), escapeJSONStrings(m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x,
(int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspaceID(),
(!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), m->activeSpecialWorkspaceID(),
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.get() == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"),
m->tearingState.activelyTearing ? "true" : "false", formatToString(m->drmFormat), availableModesForOutput(m.get(), format));
(m->tearingState.activelyTearing ? "true" : "false"), (m->m_bEnabled ? "false" : "true"), formatToString(m->drmFormat), availableModesForOutput(m.get(), format));
}
trimTrailingComma(result);
@@ -131,39 +128,38 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
if (!m->output || m->ID == -1ull)
continue;
result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial "
result += std::format(
"Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial "
"workspace: {} ({})\n\treserved: {} "
"{} {} {}\n\tscale: {:.2f}\n\ttransform: "
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n",
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\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->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspace,
(m->activeWorkspace == -1 ? "" : g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName), m->specialWorkspaceID,
getWorkspaceNameFromSpecialID(m->specialWorkspaceID), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x,
(int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus,
(int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED), m->tearingState.activelyTearing, formatToString(m->drmFormat),
availableModesForOutput(m.get(), format));
(m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? 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.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, (int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->drmFormat), availableModesForOutput(m.get(), format));
}
}
return result;
}
static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) {
static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
if (!w->m_sGroupData.pNextWindow)
if (w->m_sGroupData.pNextWindow.expired())
return isJson ? "" : "0";
std::ostringstream result;
CWindow* head = w->getGroupHead();
CWindow* curr = head;
PHLWINDOW head = w->getGroupHead();
PHLWINDOW curr = head;
while (true) {
if (isJson)
result << std::format("\"0x{:x}\"", (uintptr_t)curr);
result << std::format("\"0x{:x}\"", (uintptr_t)curr.get());
else
result << std::format("{:x}", (uintptr_t)curr);
curr = curr->m_sGroupData.pNextWindow;
result << std::format("{:x}", (uintptr_t)curr.get());
curr = curr->m_sGroupData.pNextWindow.lock();
// We've wrapped around to the start, break out without trailing comma
if (curr == head)
break;
@@ -173,10 +169,10 @@ static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) {
return result.str();
}
static std::string getWindowData(CWindow* w, eHyprCtlOutputFormat format) {
auto getFocusHistoryID = [](CWindow* wnd) -> int {
static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) {
if (g_pCompositor->m_vWindowFocusHistory[i] == wnd)
if (g_pCompositor->m_vWindowFocusHistory[i].lock() == wnd)
return i;
}
return -1;
@@ -210,31 +206,24 @@ static std::string getWindowData(CWindow* w, eHyprCtlOutputFormat format) {
"swallowing": "0x{:x}",
"focusHistoryID": {}
}},)#",
(uintptr_t)w, (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_iWorkspaceID,
escapeJSONStrings(w->m_iWorkspaceID == -1 ? "" :
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName :
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID))),
((int)w->m_bIsFloating == 1 ? "true" : "false"), (int64_t)w->m_iMonitorID, escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)),
escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(),
((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"),
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? (int)g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
(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"), (int64_t)w->m_iMonitorID,
escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)), escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass),
escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
(w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
return std::format("Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
(uintptr_t)w, 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_iWorkspaceID,
(w->m_iWorkspaceID == -1 ? "" :
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName :
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID))),
(int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle,
w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
(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, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w),
g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0), (int)w->m_bFakeFullscreenState, getGroupedData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
}
}
@@ -247,7 +236,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all)
continue;
result += getWindowData(w.get(), format);
result += getWindowData(w, format);
}
trimTrailingComma(result);
@@ -258,13 +247,13 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all)
continue;
result += getWindowData(w.get(), format);
result += getWindowData(w, format);
}
}
return result;
}
static std::string getWorkspaceData(CWorkspace* w, eHyprCtlOutputFormat format) {
static std::string getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@@ -280,11 +269,11 @@ static std::string getWorkspaceData(CWorkspace* w, eHyprCtlOutputFormat format)
}})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"),
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
((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, PLASTW ? PLASTW->m_szTitle : "");
(int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_szTitle : "");
}
}
@@ -339,9 +328,9 @@ std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string requ
return "unsafe state";
std::string result = "";
auto w = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
auto w = g_pCompositor->m_pLastMonitor->activeWorkspace;
if (!w)
if (!valid(w))
return "internal error";
return getWorkspaceData(w, format);
@@ -353,7 +342,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format);
result += getWorkspaceData(w, format);
result += ",";
}
@@ -361,7 +350,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
result += "]";
} else {
for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format);
result += getWorkspaceData(w, format);
}
}
@@ -389,9 +378,9 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
}
std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_pLastWindow;
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!g_pCompositor->windowValidMapped(PWINDOW))
if (!validMapped(PWINDOW))
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
auto result = getWindowData(PWINDOW, format);
@@ -528,23 +517,24 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "{\n";
result += "\"mice\": [\n";
for (auto& m : g_pInputManager->m_lMice) {
for (auto& m : g_pInputManager->m_vPointers) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}",
"defaultSpeed": {:.5f}
}},)#",
(uintptr_t)&m, escapeJSONStrings(m.name),
wlr_input_device_is_libinput(m.mouse) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(m.mouse)) : 0.f);
(uintptr_t)m.get(), escapeJSONStrings(m->hlName),
wlr_input_device_is_libinput(&m->wlr()->base) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f);
}
trimTrailingComma(result);
result += "\n],\n";
result += "\"keyboards\": [\n";
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
for (auto& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout();
result += std::format(
R"#( {{
"address": "0x{:x}",
@@ -557,9 +547,9 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"active_keymap": "{}",
"main": {}
}},)#",
(uintptr_t)&k, escapeJSONStrings(k.name), 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"));
(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"));
}
trimTrailingComma(result);
@@ -604,13 +594,13 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"touch\": [\n";
for (auto& d : g_pInputManager->m_lTouchDevices) {
for (auto& d : g_pInputManager->m_vTouches) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, d.name);
(uintptr_t)d.get(), escapeJSONStrings(d->hlName));
}
trimTrailingComma(result);
@@ -624,7 +614,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, d.pWlrDevice ? d.pWlrDevice->name : "");
(uintptr_t)&d, escapeJSONStrings(d.pWlrDevice ? d.pWlrDevice->name : ""));
}
trimTrailingComma(result);
@@ -635,19 +625,20 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
} else {
result += "mice:\n";
for (auto& m : g_pInputManager->m_lMice) {
result += std::format(
"\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)&m, m.name,
(wlr_input_device_is_libinput(m.mouse) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(m.mouse)) : 0.f));
for (auto& m : g_pInputManager->m_vPointers) {
result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName,
(wlr_input_device_is_libinput(&m->wlr()->base) ?
libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f));
}
result += "\n\nKeyboards:\n";
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
for (auto& 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, k.name, k.currentRules.rules, k.currentRules.model, k.currentRules.layout, k.currentRules.variant, k.currentRules.options, KM,
(k.active ? "yes" : "no"));
(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 += "\n\nTablets:\n";
@@ -666,8 +657,8 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nTouch:\n";
for (auto& d : g_pInputManager->m_lTouchDevices) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.name);
for (auto& d : g_pInputManager->m_vTouches) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->hlName);
}
result += "\n\nSwitches:\n";
@@ -709,8 +700,8 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
"speed": {:.2f},
"style": "{}"
}},)#",
ac.first, ac.second.overridden ? "true" : "false", ac.second.internalBezier, ac.second.internalEnabled ? "true" : "false", ac.second.internalSpeed,
ac.second.internalStyle);
ac.first, ac.second.overridden ? "true" : "false", escapeJSONStrings(ac.second.internalBezier), ac.second.internalEnabled ? "true" : "false",
ac.second.internalSpeed, escapeJSONStrings(ac.second.internalStyle));
}
ret[ret.length() - 1] = ']';
@@ -722,7 +713,7 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
{{
"name": "{}"
}},)#",
bz.first);
escapeJSONStrings(bz.first));
}
trimTrailingComma(ret);
@@ -827,7 +818,7 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
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 + "\n\nflags: (if any)\n";
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + ", commits: " + GIT_COMMITS + "\n\nflags: (if any)\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
@@ -849,8 +840,9 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"commits": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG);
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS);
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
@@ -1082,13 +1074,13 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
const auto CMD = vars[2];
// get kb
const auto PKEYBOARD = std::find_if(g_pInputManager->m_lKeyboards.begin(), g_pInputManager->m_lKeyboards.end(),
[&](const SKeyboard& other) { return other.name == g_pInputManager->deviceNameToInternalString(KB); });
const auto PKEYBOARD = std::find_if(g_pInputManager->m_vKeyboards.begin(), g_pInputManager->m_vKeyboards.end(),
[&](const auto& other) { return other->hlName == g_pInputManager->deviceNameToInternalString(KB); });
if (PKEYBOARD == g_pInputManager->m_lKeyboards.end())
if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found";
const auto PWLRKEYBOARD = wlr_keyboard_from_input_device(PKEYBOARD->keyboard);
const auto PWLRKEYBOARD = (*PKEYBOARD)->wlr();
const auto LAYOUTS = xkb_keymap_num_layouts(PWLRKEYBOARD->keymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
@@ -1156,7 +1148,7 @@ std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
if (vars.size() < 4)
return "not enough args";
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow;
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
if (!PWINDOW)
@@ -1225,10 +1217,27 @@ std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
PWINDOW->m_sSpecialRenderData.alphaInactiveOverride.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "alphainactive") {
PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor") {
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "inactivebordercolor") {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "alphafullscreenoverride") {
PWINDOW->m_sSpecialRenderData.alphaFullscreenOverride.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "alphafullscreen") {
PWINDOW->m_sSpecialRenderData.alphaFullscreen.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor" || PROP == "inactivebordercolor") {
CGradientValueData colorData = {};
if (vars.size() > 4) {
for (int i = 3; i < static_cast<int>(lock ? vars.size() - 1 : 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_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(colorData, lock);
else
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(colorData, lock);
} else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") {
@@ -1743,7 +1752,7 @@ void CHyprCtl::startHyprCtlSocket() {
sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};
std::string socketPath = "/tmp/hypr/" + g_pCompositor->m_szInstanceSignature + "/.socket.sock";
std::string socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock";
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());

View File

@@ -3,7 +3,7 @@
#include <pango/pangocairo.h>
CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (m_dNotifications.size() == 0)
return;
@@ -36,6 +36,13 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() {
m_szIconFontName = fonts.substr(COLON + 2, LASTCHAR - (COLON + 2));
}
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
if (m_pCairo)
cairo_destroy(m_pCairo);
if (m_pCairoSurface)
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();
@@ -76,7 +83,7 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
const auto SCALE = pMonitor->scale;
const auto MONSIZE = pMonitor->vecPixelSize;
const auto MONSIZE = pMonitor->vecTransformedSize;
cairo_text_extents_t cairoExtents;
int iconW = 0, iconH = 0;
@@ -185,16 +192,19 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
if (m_pLastMonitor != pMonitor || !m_pCairo || !m_pCairoSurface) {
const auto MONSIZE = pMonitor->vecTransformedSize;
if (m_pLastMonitor != pMonitor || m_vecLastSize != MONSIZE || !m_pCairo || !m_pCairoSurface) {
if (m_pCairo && m_pCairoSurface) {
cairo_destroy(m_pCairo);
cairo_surface_destroy(m_pCairoSurface);
}
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MONSIZE.x, MONSIZE.y);
m_pCairo = cairo_create(m_pCairoSurface);
m_pLastMonitor = pMonitor;
m_vecLastSize = MONSIZE;
}
// Draw the notifications
@@ -232,9 +242,9 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
CBox pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y};
CBox pMonBox = {0, 0, MONSIZE.x, MONSIZE.y};
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
}

View File

@@ -39,6 +39,7 @@ struct SNotification {
class CHyprNotificationOverlay {
public:
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);
@@ -55,6 +56,7 @@ class CHyprNotificationOverlay {
cairo_t* m_pCairo = nullptr;
CMonitor* m_pLastMonitor = nullptr;
Vector2D m_vecLastSize = Vector2D(-1, -1);
CTexture m_tTexture;

View File

@@ -6,7 +6,7 @@
#include <iostream>
void Debug::init(const std::string& IS) {
logFile = "/tmp/hypr/" + IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
}
void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
@@ -32,3 +32,57 @@ void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
if (!disableStdout)
std::cout << output << "\n";
}
void Debug::log(LogLevel level, std::string str) {
if (level == TRACE && !trace)
return;
if (shuttingDown)
return;
std::string coloredStr = str;
switch (level) {
case LOG:
str = "[LOG] " + str;
coloredStr = str;
break;
case WARN:
str = "[WARN] " + str;
coloredStr = "\033[1;33m" + str + "\033[0m"; // yellow
break;
case ERR:
str = "[ERR] " + str;
coloredStr = "\033[1;31m" + str + "\033[0m"; // red
break;
case CRIT:
str = "[CRITICAL] " + str;
coloredStr = "\033[1;35m" + str + "\033[0m"; // magenta
break;
case INFO:
str = "[INFO] " + str;
coloredStr = "\033[1;32m" + str + "\033[0m"; // green
break;
case TRACE:
str = "[TRACE] " + str;
coloredStr = "\033[1;34m" + str + "\033[0m"; // blue
break;
default: break;
}
rollingLog += str + "\n";
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
if (!disableLogs || !**disableLogs) {
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << str << "\n";
ofs.close();
}
// log it to the stdout too.
if (!disableStdout)
std::cout << ((coloredLogs && !**coloredLogs) ? str : coloredStr) << "\n";
}

View File

@@ -27,10 +27,15 @@ namespace Debug {
inline bool disableStdout = false;
inline bool trace = false;
inline bool shuttingDown = false;
inline int64_t* const* coloredLogs = nullptr;
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
void init(const std::string& IS);
//
void log(LogLevel level, std::string str);
template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
if (level == TRACE && !trace)
@@ -41,16 +46,6 @@ namespace Debug {
std::string logMsg = "";
switch (level) {
case LOG: logMsg += "[LOG] "; break;
case WARN: logMsg += "[WARN] "; break;
case ERR: logMsg += "[ERR] "; break;
case CRIT: logMsg += "[CRITICAL] "; break;
case INFO: logMsg += "[INFO] "; break;
case TRACE: logMsg += "[TRACE] "; break;
default: break;
}
// print date and time to the ofs
if (disableTime && !**disableTime) {
#ifndef _LIBCPP_VERSION
@@ -58,7 +53,6 @@ namespace Debug {
#else
auto c = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())};
logMsg += std::format("{:%H}:{:%M}:{:%S}", c.hours(), c.minutes(), c.subseconds());
#endif
}
@@ -69,22 +63,7 @@ namespace Debug {
// 3. this is actually what std::format in stdlib does
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
rollingLog += logMsg + "\n";
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
if (!disableLogs || !**disableLogs) {
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << logMsg << "\n";
ofs.close();
}
// log it to the stdout too.
if (!disableStdout)
std::cout << logMsg << "\n";
log(level, logMsg);
}
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);

View File

@@ -3,3 +3,16 @@
#include "helpers/WLListener.hpp"
#include "helpers/Color.hpp"
#include "macros.hpp"
class CWindow;
class CLayerSurface;
/* Shared pointer to a window */
typedef SP<CWindow> PHLWINDOW;
/* Weak pointer to a window */
typedef WP<CWindow> PHLWINDOWREF;
/* Shared pointer to a layer surface */
typedef SP<CLayerSurface> PHLLS;
/* Weak pointer to a layer surface */
typedef WP<CLayerSurface> PHLLSREF;

View File

@@ -1,128 +0,0 @@
#include "Constraint.hpp"
#include "WLSurface.hpp"
#include "../Compositor.hpp"
CConstraint::CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner) : m_pOwner(owner), m_pConstraint(constraint) {
initSignals();
m_vCursorPosOnActivate = g_pInputManager->getMouseCoordsInternal();
g_pInputManager->m_vConstraints.push_back(this);
if (g_pCompositor->m_pLastFocus == m_pOwner->wlr())
activate();
}
CConstraint::~CConstraint() {
std::erase(g_pInputManager->m_vConstraints, this);
}
static void onConstraintDestroy(void* owner, void* data) {
const auto CONSTRAINT = (CConstraint*)owner;
CONSTRAINT->onDestroy();
}
static void onConstraintSetRegion(void* owner, void* data) {
const auto CONSTRAINT = (CConstraint*)owner;
CONSTRAINT->onSetRegion();
}
void CConstraint::initSignals() {
hyprListener_setConstraintRegion.initCallback(&m_pConstraint->events.set_region, ::onConstraintSetRegion, this, "CConstraint");
hyprListener_destroyConstraint.initCallback(&m_pConstraint->events.destroy, ::onConstraintDestroy, this, "CConstraint");
}
void CConstraint::onDestroy() {
hyprListener_setConstraintRegion.removeCallback();
hyprListener_destroyConstraint.removeCallback();
if (active() && isLocked())
g_pCompositor->warpCursorTo(logicPositionHint(), true);
// this is us
m_pOwner->m_pConstraint.reset();
}
void CConstraint::onSetRegion() {
if (!m_bActive)
return;
m_rRegion.set(&m_pConstraint->region);
m_vPositionHint = m_rRegion.closestPoint(m_vPositionHint);
g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss
}
void CConstraint::onCommit() {
if (!m_bActive)
return;
const auto COMMITTED = m_pConstraint->current.committed;
if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
m_bHintSet = true;
m_vPositionHint = {m_pConstraint->current.cursor_hint.x, m_pConstraint->current.cursor_hint.y};
g_pInputManager->simulateMouseMovement();
}
if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_REGION)
onSetRegion();
}
CRegion CConstraint::logicConstraintRegion() {
CRegion rg = m_rRegion;
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
rg.translate(CONSTRAINTPOS);
return rg;
}
CWLSurface* CConstraint::owner() {
return m_pOwner;
}
bool CConstraint::isLocked() {
return m_pConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
}
Vector2D CConstraint::logicPositionHint() {
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
return m_bHintSet ? CONSTRAINTPOS + m_vPositionHint : m_vCursorPosOnActivate;
}
void CConstraint::deactivate() {
if (!m_bActive)
return;
m_bActive = false;
wlr_pointer_constraint_v1_send_deactivated(m_pConstraint);
if (isLocked())
g_pCompositor->warpCursorTo(logicPositionHint(), true);
if (m_pConstraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT)
m_bDead = true;
}
void CConstraint::activate() {
if (m_bActive || m_bDead)
return;
m_bActive = true;
// TODO: hack, probably not a super duper great idea
if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != m_pOwner->wlr()) {
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, m_pOwner->wlr(), LOCAL.x, LOCAL.y);
}
g_pCompositor->warpCursorTo(logicPositionHint(), true);
wlr_pointer_constraint_v1_send_activated(m_pConstraint);
}
bool CConstraint::active() {
return m_bActive;
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include "../includes.hpp"
#include "../helpers/Region.hpp"
#include "../helpers/WLListener.hpp"
class CWLSurface;
class CConstraint {
public:
CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner);
~CConstraint();
void onCommit();
void onDestroy();
void onSetRegion();
CRegion logicConstraintRegion();
bool isLocked();
Vector2D logicPositionHint();
void deactivate();
void activate();
bool active();
CWLSurface* owner();
private:
bool m_bActive = false;
CWLSurface* m_pOwner = nullptr;
wlr_pointer_constraint_v1* m_pConstraint;
CRegion m_rRegion;
bool m_bHintSet = false;
Vector2D m_vPositionHint = {-1, -1};
Vector2D m_vCursorPosOnActivate = {-1, -1};
// for oneshot constraints that have been activated once
bool m_bDead = false;
DYNLISTENER(destroyConstraint);
DYNLISTENER(setConstraintRegion);
void initSignals();
};

View File

@@ -0,0 +1,6 @@
#pragma once
#include <memory>
class CWorkspace;
typedef std::shared_ptr<CWorkspace> PHLWORKSPACE;

View File

@@ -0,0 +1,589 @@
#include "LayerSurface.hpp"
#include "../Compositor.hpp"
#include "../events/Events.hpp"
void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
const auto WLRLAYERSURFACE = (wlr_layer_surface_v1*)data;
if (!WLRLAYERSURFACE->output) {
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
if (!PMONITOR) {
Debug::log(ERR, "No monitor at cursor on new layer without a monitor. Ignoring.");
wlr_layer_surface_v1_destroy(WLRLAYERSURFACE);
return;
}
Debug::log(LOG, "New LayerSurface has no preferred monitor. Assigning Monitor {}", PMONITOR->szName);
WLRLAYERSURFACE->output = PMONITOR->output;
}
auto PMONITOR = g_pCompositor->getMonitorFromOutput(WLRLAYERSURFACE->output);
if (!WLRLAYERSURFACE->output || !PMONITOR || PMONITOR->pMirrorOf) {
PMONITOR = g_pCompositor->m_vMonitors.front().get();
WLRLAYERSURFACE->output = PMONITOR->output; // TODO: current mon
}
const auto PLS = PMONITOR->m_aLayerSurfaceLayers[WLRLAYERSURFACE->pending.layer].emplace_back(CLayerSurface::create(WLRLAYERSURFACE));
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)WLRLAYERSURFACE, WLRLAYERSURFACE->_namespace, (int)PLS->layer, PMONITOR->szName);
}
static void onCommit(void* owner, void* data) {
const auto LS = ((CLayerSurface*)owner)->self.lock();
LS->onCommit();
}
static void onMap(void* owner, void* data) {
const auto LS = ((CLayerSurface*)owner)->self.lock();
LS->onMap();
}
static void onUnmap(void* owner, void* data) {
const auto LS = ((CLayerSurface*)owner)->self.lock();
LS->onUnmap();
}
static void onDestroy(void* owner, void* data) {
const auto LS = ((CLayerSurface*)owner)->self.lock();
LS->onDestroy();
}
// IMPL
PHLLS CLayerSurface::create(wlr_layer_surface_v1* pWLRLS) {
PHLLS pLS = std::shared_ptr<CLayerSurface>(new CLayerSurface);
auto PMONITOR = g_pCompositor->getMonitorFromOutput(pWLRLS->output);
pLS->self = pLS;
pLS->szNamespace = pWLRLS->_namespace;
pLS->hyprListener_commitLayerSurface.initCallback(&pWLRLS->surface->events.commit, ::onCommit, pLS.get(), "layerSurface");
pLS->hyprListener_destroyLayerSurface.initCallback(&pWLRLS->events.destroy, ::onDestroy, pLS.get(), "layerSurface");
pLS->hyprListener_mapLayerSurface.initCallback(&pWLRLS->surface->events.map, ::onMap, pLS.get(), "layerSurface");
pLS->hyprListener_unmapLayerSurface.initCallback(&pWLRLS->surface->events.unmap, ::onUnmap, pLS.get(), "layerSurface");
pLS->layerSurface = pWLRLS;
pLS->layer = pWLRLS->current.layer;
pWLRLS->data = pLS.get();
pLS->monitorID = PMONITOR->ID;
pLS->popupHead = std::make_unique<CPopup>(pLS);
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
pLS->alpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->realPosition.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->realSize.create(g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
pLS->alpha.registerVar();
pLS->realPosition.registerVar();
pLS->realSize.registerVar();
pLS->registerCallbacks();
pLS->alpha.setValueAndWarp(0.f);
pLS->surface.assign(pWLRLS->surface);
return pLS;
}
void CLayerSurface::registerCallbacks() {
alpha.setUpdateCallback([this](void*) {
if (dimAround)
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(monitorID));
});
}
CLayerSurface::CLayerSurface() {
;
}
CLayerSurface::~CLayerSurface() {
if (!g_pHyprOpenGL)
return;
surface.unassign();
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); });
}
void CLayerSurface::onDestroy() {
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface);
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
popupHead.reset();
if (!g_pCompositor->getMonitorFromID(monitorID))
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
if (!fadingOut) {
if (mapped) {
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
onUnmap();
} else {
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
alpha.setValueAndWarp(0.f);
fadingOut = true;
g_pCompositor->addToFadingOutSafe(self.lock());
}
}
noProcess = true;
hyprListener_commitLayerSurface.removeCallback();
hyprListener_destroyLayerSurface.removeCallback();
hyprListener_mapLayerSurface.removeCallback();
hyprListener_unmapLayerSurface.removeCallback();
// rearrange to fix the reserved areas
if (PMONITOR) {
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
PMONITOR->scheduledRecalc = true;
// and damage
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
}
readyToDelete = true;
layerSurface = nullptr;
surface.unassign();
}
void CLayerSurface::onMap() {
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface);
mapped = true;
keyboardExclusive = layerSurface->current.keyboard_interactive;
// fix if it changed its mon
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
if (!PMONITOR)
return;
applyRules();
if ((uint64_t)monitorID != PMONITOR->ID) {
const auto POLDMON = g_pCompositor->getMonitorFromID(monitorID);
for (auto it = POLDMON->m_aLayerSurfaceLayers[layer].begin(); it != POLDMON->m_aLayerSurfaceLayers[layer].end(); it++) {
if (*it == self.lock()) {
PMONITOR->m_aLayerSurfaceLayers[layer].emplace_back(std::move(*it));
POLDMON->m_aLayerSurfaceLayers[layer].erase(it);
break;
}
}
monitorID = PMONITOR->ID;
PMONITOR->scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(POLDMON->ID);
}
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
wlr_surface_send_enter(surface.wlr(), PMONITOR->output);
if (layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(self);
const bool GRABSFOCUS = layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained());
if (GRABSFOCUS) {
g_pInputManager->releaseAllMouseButtons();
g_pCompositor->focusSurface(surface.wlr());
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, surface.wlr(), LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pInputManager->m_bEmptyFocusCursorSet = false;
}
position = Vector2D(geometry.x, geometry.y);
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = PMONITOR->activeWorkspace;
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
readyToDelete = false;
fadingOut = false;
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", szNamespace});
EMIT_HOOK_EVENT("openLayer", self.lock());
g_pCompositor->setPreferredScaleForSurface(surface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(surface.wlr(), PMONITOR->transform);
}
void CLayerSurface::onUnmap() {
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layerSurface);
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layerSurface->_namespace ? layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", self.lock());
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
if (!g_pInputManager->m_dExclusiveLSes.empty())
g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0].lock()->layerSurface->surface);
if (!g_pCompositor->getMonitorFromID(monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
g_pCompositor->addToFadingOutSafe(self.lock());
mapped = false;
startAnimation(false);
return;
}
// make a snapshot and start fade
g_pHyprOpenGL->makeLayerSnapshot(self.lock());
startAnimation(false);
mapped = false;
g_pCompositor->addToFadingOutSafe(self.lock());
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layerSurface->output);
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == layerSurface->surface;
surface = nullptr;
if (!PMONITOR)
return;
// refocus if needed
if (WASLASTFOCUS) {
g_pInputManager->releaseAllMouseButtons();
Vector2D surfaceCoords;
PHLLS pFoundLayerSurface;
wlr_surface* foundSurface = nullptr;
g_pCompositor->m_pLastFocus = nullptr;
// find LS-es to focus
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->isWorkspaceVisible(g_pCompositor->m_pLastWindow.lock()->m_pWorkspace)) {
// if there isn't any, focus the last window
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
g_pCompositor->focusWindow(nullptr);
g_pCompositor->focusWindow(PLASTWINDOW);
} else {
// otherwise, full refocus
g_pInputManager->refocus();
}
}
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.width,
(int)layerSurface->surface->current.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused();
}
void CLayerSurface::onCommit() {
if (!layerSurface || !layerSurface->output)
return;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layerSurface->output);
if (!PMONITOR)
return;
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
CBox geomFixed = {geometry.x, geometry.y, geometry.width, geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
// fix if it changed its mon
if ((uint64_t)monitorID != PMONITOR->ID) {
const auto POLDMON = g_pCompositor->getMonitorFromID(monitorID);
for (auto it = POLDMON->m_aLayerSurfaceLayers[layer].begin(); it != POLDMON->m_aLayerSurfaceLayers[layer].end(); it++) {
if (*it == self.lock()) {
PMONITOR->m_aLayerSurfaceLayers[layer].emplace_back(std::move(*it));
POLDMON->m_aLayerSurfaceLayers[layer].erase(it);
break;
}
}
monitorID = PMONITOR->ID;
PMONITOR->scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(POLDMON->ID);
}
if (layerSurface->current.committed != 0) {
if (layer != layerSurface->current.layer) {
for (auto it = PMONITOR->m_aLayerSurfaceLayers[layer].begin(); it != PMONITOR->m_aLayerSurfaceLayers[layer].end(); it++) {
if (*it == self.lock()) {
PMONITOR->m_aLayerSurfaceLayers[layerSurface->current.layer].emplace_back(std::move(*it));
PMONITOR->m_aLayerSurfaceLayers[layer].erase(it);
break;
}
}
layer = layerSurface->current.layer;
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
}
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
PMONITOR->scheduledRecalc = true;
} else {
position = Vector2D(geometry.x, geometry.y);
// update geom if it changed
if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.has_dst) {
// fractional scaling. Dirty hack.
geometry = {geometry.x, geometry.y, (int)(layerSurface->surface->current.viewport.dst_width), (int)(layerSurface->surface->current.viewport.dst_height)};
} else {
// this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly.
geometry = {geometry.x, geometry.y, (int)layerSurface->surface->current.width, (int)layerSurface->surface->current.height};
}
}
if (realPosition.goal() != geometry.pos()) {
if (realPosition.isBeingAnimated())
realPosition = geometry.pos();
else
realPosition.setValueAndWarp(geometry.pos());
}
if (realSize.goal() != geometry.size()) {
if (realSize.isBeingAnimated())
realSize = geometry.size();
else
realSize.setValueAndWarp(geometry.size());
}
if (layerSurface->current.keyboard_interactive && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !keyboardExclusive && mapped) {
g_pCompositor->focusSurface(layerSurface->surface);
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pInputManager->m_bEmptyFocusCursorSet = false;
} else if (!layerSurface->current.keyboard_interactive && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) {
g_pInputManager->refocus();
}
keyboardExclusive = layerSurface->current.keyboard_interactive;
g_pHyprRenderer->damageSurface(layerSurface->surface, position.x, position.y);
g_pCompositor->setPreferredScaleForSurface(layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layerSurface->surface, PMONITOR->transform);
}
void CLayerSurface::applyRules() {
noAnimations = false;
forceBlur = false;
ignoreAlpha = false;
ignoreAlphaValue = 0.f;
dimAround = false;
xray = -1;
animationStyle.reset();
for (auto& 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);
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];
}
}
}
void CLayerSurface::startAnimation(bool in, bool instant) {
const auto ANIMSTYLE = animationStyle.value_or(realPosition.m_pConfig->pValues->internalStyle);
if (in) {
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersIn");
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn");
} else {
realPosition.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
realSize.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("layersOut");
alpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut");
}
if (ANIMSTYLE.starts_with("slide")) {
// get closest edge
const auto MIDDLE = geometry.middle();
const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE);
int force = -1;
CVarList args(ANIMSTYLE, 0, 's');
if (args.size() > 1) {
const auto ARG2 = args[1];
if (ARG2 == "top")
force = 0;
else if (ARG2 == "bottom")
force = 1;
else if (ARG2 == "left")
force = 2;
else if (ARG2 == "right")
force = 3;
}
const std::array<Vector2D, 4> edgePoints = {
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, 0},
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, PMONITOR->vecSize.y},
PMONITOR->vecPosition + Vector2D{0, PMONITOR->vecSize.y},
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x, PMONITOR->vecSize.y / 2},
};
float closest = std::numeric_limits<float>::max();
int leader = force;
if (leader == -1) {
for (size_t i = 0; i < 4; ++i) {
float dist = MIDDLE.distance(edgePoints[i]);
if (dist < closest) {
leader = i;
closest = dist;
}
}
}
realSize.setValueAndWarp(geometry.size());
alpha.setValueAndWarp(in ? 0.f : 1.f);
alpha = in ? 1.f : 0.f;
Vector2D prePos;
switch (leader) {
case 0:
// TOP
prePos = {geometry.x, PMONITOR->vecPosition.y - geometry.h};
break;
case 1:
// BOTTOM
prePos = {geometry.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y};
break;
case 2:
// LEFT
prePos = {PMONITOR->vecPosition.x - geometry.w, geometry.y};
break;
case 3:
// RIGHT
prePos = {PMONITOR->vecPosition.x + PMONITOR->vecSize.x, geometry.y};
break;
default: UNREACHABLE();
}
if (in) {
realPosition.setValueAndWarp(prePos);
realPosition = geometry.pos();
} else {
realPosition.setValueAndWarp(geometry.pos());
realPosition = prePos;
}
} else if (ANIMSTYLE.starts_with("popin")) {
float minPerc = 0.f;
if (ANIMSTYLE.find("%") != std::string::npos) {
try {
auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' '));
minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
} catch (std::exception& e) {
; // oops
}
}
minPerc *= 0.01;
const auto GOALSIZE = (geometry.size() * minPerc).clamp({5, 5});
const auto GOALPOS = geometry.pos() + (geometry.size() - GOALSIZE) / 2.f;
alpha.setValueAndWarp(in ? 0.f : 1.f);
alpha = in ? 1.f : 0.f;
if (in) {
realSize.setValueAndWarp(GOALSIZE);
realPosition.setValueAndWarp(GOALPOS);
realSize = geometry.size();
realPosition = geometry.pos();
} else {
realSize.setValueAndWarp(geometry.size());
realPosition.setValueAndWarp(geometry.pos());
realSize = GOALSIZE;
realPosition = GOALPOS;
}
} else {
// fade
realPosition.setValueAndWarp(geometry.pos());
realSize.setValueAndWarp(geometry.size());
alpha = in ? 1.f : 0.f;
}
if (!in)
fadingOut = true;
}
bool CLayerSurface::isFadedOut() {
if (!fadingOut)
return false;
return !realPosition.isBeingAnimated() && !realSize.isBeingAnimated() && !alpha.isBeingAnimated();
}
int CLayerSurface::popupsCount() {
if (!layerSurface || !mapped || fadingOut)
return 0;
int no = 0;
wlr_layer_surface_v1_for_each_popup_surface(
layerSurface, [](wlr_surface* s, int x, int y, void* data) { *(int*)data += 1; }, &no);
return no;
}

View File

@@ -0,0 +1,85 @@
#pragma once
#include <string>
#include "../defines.hpp"
#include "WLSurface.hpp"
#include "../helpers/AnimatedVariable.hpp"
#include "wlr-layer-shell-unstable-v1-protocol.h"
struct SLayerRule {
std::string targetNamespace = "";
std::string rule = "";
};
class CLayerSurface {
public:
static PHLLS create(wlr_layer_surface_v1*);
private:
CLayerSurface();
public:
~CLayerSurface();
void applyRules();
void startAnimation(bool in, bool instant = false);
bool isFadedOut();
int popupsCount();
CAnimatedVariable<Vector2D> realPosition;
CAnimatedVariable<Vector2D> realSize;
CAnimatedVariable<float> alpha;
wlr_layer_surface_v1* layerSurface;
wl_list link;
bool keyboardExclusive = false;
CWLSurface surface;
bool mapped = false;
int monitorID = -1;
bool fadingOut = false;
bool readyToDelete = false;
bool noProcess = false;
bool noAnimations = false;
bool forceBlur = false;
bool forceBlurPopups = false;
int xray = -1;
bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f;
bool dimAround = false;
std::optional<std::string> animationStyle;
zwlr_layer_shell_v1_layer layer;
PHLLSREF self;
CBox geometry = {0, 0, 0, 0};
Vector2D position;
std::string szNamespace = "";
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
private:
std::unique_ptr<CPopup> popupHead;
DYNLISTENER(destroyLayerSurface);
DYNLISTENER(mapLayerSurface);
DYNLISTENER(unmapLayerSurface);
DYNLISTENER(commitLayerSurface);
void registerCallbacks();
// For the list lookup
bool operator==(const CLayerSurface& rhs) const {
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
}
};

View File

@@ -1,11 +1,12 @@
#include "Popup.hpp"
#include "../config/ConfigValue.hpp"
#include "../Compositor.hpp"
CPopup::CPopup(CWindow* pOwner) : m_pWindowOwner(pOwner) {
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
initAllSignals();
}
CPopup::CPopup(SLayerSurface* pOwner) : m_pLayerOwner(pOwner) {
CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) {
initAllSignals();
}
@@ -68,8 +69,8 @@ static void onRepositionPopup(void* owner, void* data) {
void CPopup::initAllSignals() {
if (!m_pWLR) {
if (m_pWindowOwner)
hyprListener_newPopup.initCallback(&m_pWindowOwner->m_uSurface.xdg->events.new_popup, ::onNewPopup, this, "CPopup Head");
if (!m_pWindowOwner.expired())
hyprListener_newPopup.initCallback(&m_pWindowOwner.lock()->m_uSurface.xdg->events.new_popup, ::onNewPopup, this, "CPopup Head");
else if (m_pLayerOwner)
hyprListener_newPopup.initCallback(&m_pLayerOwner->layerSurface->events.new_popup, ::onNewPopup, this, "CPopup Head");
else
@@ -145,6 +146,18 @@ void CPopup::onCommit(bool ignoreSiblings) {
return;
}
if (!m_pWindowOwner.expired() && (!m_pWindowOwner.lock()->m_bIsMapped || !m_pWindowOwner.lock()->m_pWorkspace->m_bVisible)) {
m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height};
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowOwner.lock());
return;
}
if (!m_pWLR->base->surface->mapped)
return;
const auto COORDS = coordsGlobal();
const auto COORDSLOCAL = coordsRelativeToParent();
@@ -158,7 +171,7 @@ void CPopup::onCommit(bool ignoreSiblings) {
m_vLastPos = COORDSLOCAL;
}
if (!ignoreSiblings)
if (!ignoreSiblings && m_pSubsurfaceHead)
m_pSubsurfaceHead->recheckDamageForSubsurfaces();
g_pHyprRenderer->damageSurface(m_sWLSurface.wlr(), COORDS.x, COORDS.y);
@@ -217,8 +230,8 @@ Vector2D CPopup::localToGlobal(const Vector2D& rel) {
}
Vector2D CPopup::t1ParentCoords() {
if (m_pWindowOwner)
return m_pWindowOwner->m_vRealPosition.value();
if (!m_pWindowOwner.expired())
return m_pWindowOwner.lock()->m_vRealPosition.value();
if (m_pLayerOwner)
return m_pLayerOwner->realPosition.value();
@@ -247,8 +260,8 @@ Vector2D CPopup::size() {
}
void CPopup::sendScale() {
if (m_pWindowOwner)
g_pCompositor->setPreferredScaleForSurface(m_sWLSurface.wlr(), m_pWindowOwner->m_pWLSurface.m_fLastScale);
if (!m_pWindowOwner.expired())
g_pCompositor->setPreferredScaleForSurface(m_sWLSurface.wlr(), m_pWindowOwner.lock()->m_pWLSurface.m_fLastScale);
else if (m_pLayerOwner)
g_pCompositor->setPreferredScaleForSurface(m_sWLSurface.wlr(), m_pLayerOwner->surface.m_fLastScale);
else

View File

@@ -4,13 +4,11 @@
#include <memory>
#include "Subsurface.hpp"
struct SLayerSurface;
class CPopup {
public:
// dummy head nodes
CPopup(CWindow* pOwner);
CPopup(SLayerSurface* pOwner);
CPopup(PHLWINDOW pOwner);
CPopup(PHLLS pOwner);
// real nodes
CPopup(wlr_xdg_popup* popup, CPopup* pOwner);
@@ -35,8 +33,8 @@ class CPopup {
private:
// T1 owners, each popup has to have one of these
CWindow* m_pWindowOwner = nullptr;
SLayerSurface* m_pLayerOwner = nullptr;
PHLWINDOWREF m_pWindowOwner;
PHLLS m_pLayerOwner;
// T2 owners
CPopup* m_pParent = nullptr;

View File

@@ -5,40 +5,26 @@
static void onNewSubsurface(void* owner, void* data);
CSubsurface::CSubsurface(CWindow* pOwner) : m_pWindowParent(pOwner) {
CSubsurface::CSubsurface(PHLWINDOW pOwner) : m_pWindowParent(pOwner) {
initSignals();
wlr_subsurface* wlrSubsurface;
wl_list_for_each(wlrSubsurface, &pOwner->m_pWLSurface.wlr()->current.subsurfaces_below, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
wl_list_for_each(wlrSubsurface, &pOwner->m_pWLSurface.wlr()->current.subsurfaces_above, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
initExistingSubsurfaces(pOwner->m_pWLSurface.wlr());
}
CSubsurface::CSubsurface(CPopup* pOwner) : m_pPopupParent(pOwner) {
initSignals();
wlr_subsurface* wlrSubsurface;
wl_list_for_each(wlrSubsurface, &pOwner->m_sWLSurface.wlr()->current.subsurfaces_below, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
wl_list_for_each(wlrSubsurface, &pOwner->m_sWLSurface.wlr()->current.subsurfaces_above, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
initExistingSubsurfaces(pOwner->m_sWLSurface.wlr());
}
CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, CWindow* pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) {
CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, PHLWINDOW pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) {
m_sWLSurface.assign(pSubsurface->surface, this);
initSignals();
initExistingSubsurfaces();
initExistingSubsurfaces(pSubsurface->surface);
}
CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, CPopup* pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) {
m_sWLSurface.assign(pSubsurface->surface, this);
initSignals();
initExistingSubsurfaces();
initExistingSubsurfaces(pSubsurface->surface);
}
CSubsurface::~CSubsurface() {
@@ -47,6 +33,8 @@ CSubsurface::~CSubsurface() {
if (!m_pSubsurface)
return;
m_pSubsurface->data = nullptr;
hyprListener_commitSubsurface.removeCallback();
hyprListener_destroySubsurface.removeCallback();
}
@@ -78,14 +66,15 @@ static void onUnmapSubsurface(void* owner, void* data) {
void CSubsurface::initSignals() {
if (m_pSubsurface) {
m_pSubsurface->data = this;
hyprListener_commitSubsurface.initCallback(&m_pSubsurface->surface->events.commit, &onCommitSubsurface, this, "CSubsurface");
hyprListener_destroySubsurface.initCallback(&m_pSubsurface->events.destroy, &onDestroySubsurface, this, "CSubsurface");
hyprListener_newSubsurface.initCallback(&m_pSubsurface->surface->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface");
hyprListener_mapSubsurface.initCallback(&m_pSubsurface->surface->events.map, &onMapSubsurface, this, "CSubsurface");
hyprListener_unmapSubsurface.initCallback(&m_pSubsurface->surface->events.unmap, &onUnmapSubsurface, this, "CSubsurface");
} else {
if (m_pWindowParent)
hyprListener_newSubsurface.initCallback(&m_pWindowParent->m_pWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface Head");
if (!m_pWindowParent.expired())
hyprListener_newSubsurface.initCallback(&m_pWindowParent.lock()->m_pWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface Head");
else if (m_pPopupParent)
hyprListener_newSubsurface.initCallback(&m_pPopupParent->m_sWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface Head");
else
@@ -97,7 +86,7 @@ void CSubsurface::checkSiblingDamage() {
if (!m_pParent)
return; // ??????????
const double SCALE = m_pWindowParent && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
const double SCALE = m_pWindowParent.lock() && m_pWindowParent.lock()->m_bIsX11 ? 1.0 / m_pWindowParent.lock()->m_fX11SurfaceScaledBy : 1.0;
for (auto& n : m_pParent->m_vChildren) {
if (n.get() == this)
@@ -117,12 +106,12 @@ void CSubsurface::recheckDamageForSubsurfaces() {
void CSubsurface::onCommit() {
// no damaging if it's not visible
if (m_pWindowParent && !g_pHyprRenderer->shouldRenderWindow(m_pWindowParent)) {
if (!m_pWindowParent.expired() && (!m_pWindowParent.lock()->m_bIsMapped || !m_pWindowParent.lock()->m_pWorkspace->m_bVisible)) {
m_vLastSize = Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height};
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowParent);
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowParent.lock());
return;
}
@@ -132,8 +121,8 @@ void CSubsurface::onCommit() {
if (m_pPopupParent)
m_pPopupParent->recheckTree();
if (m_pWindowParent) // I hate you firefox why are you doing this
m_pWindowParent->m_pPopupHead->recheckTree();
if (!m_pWindowParent.expired()) // I hate you firefox why are you doing this
m_pWindowParent.lock()->m_pPopupHead->recheckTree();
// I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox)
checkSiblingDamage();
@@ -163,8 +152,8 @@ void CSubsurface::onDestroy() {
void CSubsurface::onNewSubsurface(wlr_subsurface* pSubsurface) {
CSubsurface* PSUBSURFACE = nullptr;
if (m_pWindowParent)
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pWindowParent)).get();
if (!m_pWindowParent.expired())
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pWindowParent.lock())).get();
else if (m_pPopupParent)
PSUBSURFACE = m_vChildren.emplace_back(std::make_unique<CSubsurface>(pSubsurface, m_pPopupParent)).get();
PSUBSURFACE->m_pParent = this;
@@ -180,8 +169,8 @@ void CSubsurface::onMap() {
box.expand(4);
g_pHyprRenderer->damageBox(&box);
if (m_pWindowParent)
m_pWindowParent->updateSurfaceScaleTransformDetails();
if (!m_pWindowParent.expired())
m_pWindowParent.lock()->updateSurfaceScaleTransformDetails();
}
void CSubsurface::onUnmap() {
@@ -217,23 +206,20 @@ Vector2D CSubsurface::coordsRelativeToParent() {
Vector2D CSubsurface::coordsGlobal() {
Vector2D coords = coordsRelativeToParent();
if (m_pWindowParent)
coords += m_pWindowParent->m_vRealPosition.value();
if (!m_pWindowParent.expired())
coords += m_pWindowParent.lock()->m_vRealPosition.value();
else if (m_pPopupParent)
coords += m_pPopupParent->coordsGlobal();
return coords;
}
void CSubsurface::initExistingSubsurfaces() {
if (m_pWindowParent)
return;
void CSubsurface::initExistingSubsurfaces(wlr_surface* pSurface) {
wlr_subsurface* wlrSubsurface;
wl_list_for_each(wlrSubsurface, &m_sWLSurface.wlr()->current.subsurfaces_below, current.link) {
wl_list_for_each(wlrSubsurface, &pSurface->current.subsurfaces_below, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
wl_list_for_each(wlrSubsurface, &m_sWLSurface.wlr()->current.subsurfaces_above, current.link) {
wl_list_for_each(wlrSubsurface, &pSurface->current.subsurfaces_above, current.link) {
::onNewSubsurface(this, wlrSubsurface);
}
}

View File

@@ -4,17 +4,16 @@
#include <vector>
#include "WLSurface.hpp"
class CWindow;
class CPopup;
class CSubsurface {
public:
// root dummy nodes
CSubsurface(CWindow* pOwner);
CSubsurface(PHLWINDOW pOwner);
CSubsurface(CPopup* pOwner);
// real nodes
CSubsurface(wlr_subsurface* pSubsurface, CWindow* pOwner);
CSubsurface(wlr_subsurface* pSubsurface, PHLWINDOW pOwner);
CSubsurface(wlr_subsurface* pSubsurface, CPopup* pOwner);
~CSubsurface();
@@ -46,7 +45,7 @@ class CSubsurface {
// if nullptr, means it's a dummy node
CSubsurface* m_pParent = nullptr;
CWindow* m_pWindowParent = nullptr;
PHLWINDOWREF m_pWindowParent;
CPopup* m_pPopupParent = nullptr;
std::vector<std::unique_ptr<CSubsurface>> m_vChildren;
@@ -54,6 +53,6 @@ class CSubsurface {
bool m_bInert = false;
void initSignals();
void initExistingSubsurfaces();
void initExistingSubsurfaces(wlr_surface* pSurface);
void checkSiblingDamage();
};

View File

@@ -7,14 +7,14 @@ void CWLSurface::assign(wlr_surface* pSurface) {
m_bInert = false;
}
void CWLSurface::assign(wlr_surface* pSurface, CWindow* pOwner) {
void CWLSurface::assign(wlr_surface* pSurface, PHLWINDOW pOwner) {
m_pWindowOwner = pOwner;
m_pWLRSurface = pSurface;
init();
m_bInert = false;
}
void CWLSurface::assign(wlr_surface* pSurface, SLayerSurface* pOwner) {
void CWLSurface::assign(wlr_surface* pSurface, PHLLS pOwner) {
m_pLayerOwner = pOwner;
m_pWLRSurface = pSurface;
init();
@@ -52,20 +52,22 @@ wlr_surface* CWLSurface::wlr() const {
}
bool CWLSurface::small() const {
if (!m_pWindowOwner || !exists())
if (!validMapped(m_pWindowOwner) || !exists())
return false;
return m_pWindowOwner->m_vReportedSize.x > m_pWLRSurface->current.buffer_width + 1 || m_pWindowOwner->m_vReportedSize.y > m_pWLRSurface->current.buffer_height + 1;
const auto O = m_pWindowOwner.lock();
return O->m_vReportedSize.x > m_pWLRSurface->current.buffer_width + 1 || O->m_vReportedSize.y > m_pWLRSurface->current.buffer_height + 1;
}
Vector2D CWLSurface::correctSmallVec() const {
if (!m_pWindowOwner || !exists() || !small() || m_bFillIgnoreSmall)
if (!validMapped(m_pWindowOwner) || !exists() || !small() || m_bFillIgnoreSmall)
return {};
const auto SIZE = getViewporterCorrectedSize();
const auto O = m_pWindowOwner.lock();
return Vector2D{(m_pWindowOwner->m_vReportedSize.x - SIZE.x) / 2, (m_pWindowOwner->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) *
(m_pWindowOwner->m_vRealSize.value() / m_pWindowOwner->m_vReportedSize);
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize.value() / O->m_vReportedSize);
}
Vector2D CWLSurface::getViewporterCorrectedSize() const {
@@ -103,13 +105,15 @@ void CWLSurface::destroy() {
if (!m_pWLRSurface)
return;
events.destroy.emit();
m_pConstraint.reset();
hyprListener_destroy.removeCallback();
hyprListener_commit.removeCallback();
m_pWLRSurface->data = nullptr;
m_pWindowOwner = nullptr;
m_pLayerOwner = nullptr;
m_pWindowOwner.reset();
m_pLayerOwner.reset();
m_pPopupOwner = nullptr;
m_pSubsurfaceOwner = nullptr;
m_bInert = true;
@@ -146,12 +150,12 @@ void CWLSurface::init() {
Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this);
}
CWindow* CWLSurface::getWindow() {
return m_pWindowOwner;
PHLWINDOW CWLSurface::getWindow() {
return m_pWindowOwner.lock();
}
SLayerSurface* CWLSurface::getLayer() {
return m_pLayerOwner;
PHLLS CWLSurface::getLayer() {
return m_pLayerOwner.lock();
}
CPopup* CWLSurface::getPopup() {
@@ -163,17 +167,17 @@ CSubsurface* CWLSurface::getSubsurface() {
}
bool CWLSurface::desktopComponent() {
return m_pLayerOwner || m_pWindowOwner || m_pSubsurfaceOwner || m_pPopupOwner;
return !m_pLayerOwner.expired() || !m_pWindowOwner.expired() || m_pSubsurfaceOwner || m_pPopupOwner;
}
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
if (!desktopComponent())
return {};
if (m_pWindowOwner)
return m_pWindowOwner->getWindowMainSurfaceBox();
if (m_pLayerOwner)
return m_pLayerOwner->geometry;
if (!m_pWindowOwner.expired())
return m_pWindowOwner.lock()->getWindowMainSurfaceBox();
if (!m_pLayerOwner.expired())
return m_pLayerOwner.lock()->geometry;
if (m_pPopupOwner)
return CBox{m_pPopupOwner->coordsGlobal(), m_pPopupOwner->size()};
if (m_pSubsurfaceOwner)
@@ -182,15 +186,14 @@ std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
return {};
}
void CWLSurface::appendConstraint(wlr_pointer_constraint_v1* constraint) {
m_pConstraint = std::make_unique<CConstraint>(constraint, this);
void CWLSurface::appendConstraint(std::weak_ptr<CPointerConstraint> constraint) {
m_pConstraint = constraint;
}
void CWLSurface::onCommit() {
if (m_pConstraint)
m_pConstraint->onCommit();
;
}
CConstraint* CWLSurface::constraint() {
return m_pConstraint.get();
std::shared_ptr<CPointerConstraint> CWLSurface::constraint() {
return m_pConstraint.lock();
}

View File

@@ -2,12 +2,11 @@
#include "../defines.hpp"
#include "../helpers/Region.hpp"
#include "Constraint.hpp"
#include "../helpers/signal/Signal.hpp"
class CWindow;
struct SLayerSurface;
class CSubsurface;
class CPopup;
class CPointerConstraint;
class CWLSurface {
public:
@@ -16,8 +15,8 @@ class CWLSurface {
// anonymous surfaces are non-desktop components, e.g. a cursor surface or a DnD
void assign(wlr_surface* pSurface);
void assign(wlr_surface* pSurface, CWindow* pOwner);
void assign(wlr_surface* pSurface, SLayerSurface* pOwner);
void assign(wlr_surface* pSurface, PHLWINDOW pOwner);
void assign(wlr_surface* pSurface, PHLLS pOwner);
void assign(wlr_surface* pSurface, CSubsurface* pOwner);
void assign(wlr_surface* pSurface, CPopup* pOwner);
void unassign();
@@ -36,15 +35,15 @@ class CWLSurface {
void onCommit();
// getters for owners.
CWindow* getWindow();
SLayerSurface* getLayer();
PHLWINDOW getWindow();
PHLLS getLayer();
CPopup* getPopup();
CSubsurface* getSubsurface();
// desktop components misc utils
std::optional<CBox> getSurfaceBoxGlobal();
void appendConstraint(wlr_pointer_constraint_v1* constraint);
CConstraint* constraint();
void appendConstraint(std::weak_ptr<CPointerConstraint> constraint);
std::shared_ptr<CPointerConstraint> constraint();
// allow stretching. Useful for plugins.
bool m_bFillIgnoreSmall = false;
@@ -81,18 +80,25 @@ class CWLSurface {
return (CWLSurface*)pSurface->data;
}
// used by the alpha-modifier protocol
float m_pAlphaModifier = 1.F;
struct {
CSignal destroy;
} events;
private:
bool m_bInert = true;
wlr_surface* m_pWLRSurface = nullptr;
CWindow* m_pWindowOwner = nullptr;
SLayerSurface* m_pLayerOwner = nullptr;
PHLWINDOWREF m_pWindowOwner;
PHLLSREF m_pLayerOwner;
CPopup* m_pPopupOwner = nullptr;
CSubsurface* m_pSubsurfaceOwner = nullptr;
//
std::unique_ptr<CConstraint> m_pConstraint;
std::weak_ptr<CPointerConstraint> m_pConstraint;
void destroy();
void init();
@@ -101,5 +107,5 @@ class CWLSurface {
DYNLISTENER(destroy);
DYNLISTENER(commit);
friend class CConstraint;
friend class CPointerConstraint;
};

View File

@@ -4,32 +4,46 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "../render/decorations/CHyprBorderDecoration.hpp"
#include "../config/ConfigValue.hpp"
#include <any>
#include "../managers/TokenManager.hpp"
PHLWINDOW CWindow::create() {
PHLWINDOW pWindow = std::shared_ptr<CWindow>(new CWindow);
pWindow->m_pSelf = pWindow;
pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fBorderFadeAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER);
pWindow->m_fBorderAngleAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER);
pWindow->m_fAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow));
return pWindow;
}
CWindow::CWindow() {
m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), this, AVARDAMAGE_ENTIRE);
m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), this, AVARDAMAGE_ENTIRE);
m_fBorderFadeAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("border"), this, AVARDAMAGE_BORDER);
m_fBorderAngleAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("borderangle"), this, AVARDAMAGE_BORDER);
m_fAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeIn"), this, AVARDAMAGE_ENTIRE);
m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), this, AVARDAMAGE_ENTIRE);
m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), this, AVARDAMAGE_SHADOW);
m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), this, AVARDAMAGE_ENTIRE);
addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(this));
addWindowDeco(std::make_unique<CHyprBorderDecoration>(this));
;
}
CWindow::~CWindow() {
if (g_pCompositor->isWindowActive(this)) {
if (g_pCompositor->m_pLastWindow.lock().get() == this) {
g_pCompositor->m_pLastFocus = nullptr;
g_pCompositor->m_pLastWindow = nullptr;
g_pCompositor->m_pLastWindow.reset();
}
events.destroy.emit();
if (!g_pHyprOpenGL)
return;
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first == this; });
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return !other.first.lock() || other.first.lock().get() == this; });
}
SWindowDecorationExtents CWindow::getFullWindowExtents() {
@@ -46,7 +60,7 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this);
const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock());
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
@@ -149,11 +163,11 @@ CBox CWindow::getWindowBoxUnified(uint64_t properties) {
SWindowDecorationExtents EXTENTS = {{0, 0}, {0, 0}};
if (properties & RESERVED_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(this));
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(m_pSelf.lock()));
if (properties & INPUT_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, true));
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock(), true));
if (properties & FULL_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, false));
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock(), false));
CBox box = {m_vRealPosition.value().x, m_vRealPosition.value().y, m_vRealSize.value().x, m_vRealSize.value().y};
box.addExtents(EXTENTS);
@@ -166,11 +180,10 @@ CBox CWindow::getWindowMainSurfaceBox() {
}
SWindowDecorationExtents CWindow::getFullWindowReservedArea() {
return g_pDecorationPositioner->getWindowDecorationReserved(this);
return g_pDecorationPositioner->getWindowDecorationReserved(m_pSelf.lock());
}
void CWindow::updateWindowDecos() {
bool recalc = false;
if (!m_bIsMapped || isHidden())
return;
@@ -180,17 +193,13 @@ void CWindow::updateWindowDecos() {
if (it->get() == wd) {
g_pDecorationPositioner->uncacheDecoration(it->get());
it = m_dWindowDecorations.erase(it);
recalc = true;
if (it == m_dWindowDecorations.end())
break;
}
}
}
g_pDecorationPositioner->onWindowUpdate(this);
if (recalc)
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pDecorationPositioner->onWindowUpdate(m_pSelf.lock());
m_vDecosToRemove.clear();
@@ -202,22 +211,22 @@ void CWindow::updateWindowDecos() {
}
for (auto& wd : decos) {
wd->updateWindow(this);
wd->updateWindow(m_pSelf.lock());
}
}
void CWindow::addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco) {
m_dWindowDecorations.emplace_back(std::move(deco));
g_pDecorationPositioner->forceRecalcFor(this);
g_pDecorationPositioner->forceRecalcFor(m_pSelf.lock());
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pSelf.lock());
}
void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
m_vDecosToRemove.push_back(deco);
g_pDecorationPositioner->forceRecalcFor(this);
g_pDecorationPositioner->forceRecalcFor(m_pSelf.lock());
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pSelf.lock());
}
void CWindow::uncacheWindowDecos() {
@@ -270,66 +279,8 @@ IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) {
return nullptr;
}
void CWindow::createToplevelHandle() {
if (m_bIsX11 && (m_bX11DoesntWantBorders || m_iX11Type == 2))
return; // don't create a toplevel
m_phForeignToplevel = wlr_foreign_toplevel_handle_v1_create(g_pCompositor->m_sWLRToplevelMgr);
wlr_foreign_toplevel_handle_v1_set_app_id(m_phForeignToplevel, g_pXWaylandManager->getAppIDClass(this).c_str());
wlr_foreign_toplevel_handle_v1_output_enter(m_phForeignToplevel, g_pCompositor->getMonitorFromID(m_iMonitorID)->output);
wlr_foreign_toplevel_handle_v1_set_title(m_phForeignToplevel, m_szTitle.c_str());
wlr_foreign_toplevel_handle_v1_set_maximized(m_phForeignToplevel, false);
wlr_foreign_toplevel_handle_v1_set_minimized(m_phForeignToplevel, false);
wlr_foreign_toplevel_handle_v1_set_fullscreen(m_phForeignToplevel, false);
// handle events
hyprListener_toplevelActivate.initCallback(
&m_phForeignToplevel->events.request_activate, [&](void* owner, void* data) { g_pLayoutManager->getCurrentLayout()->requestFocusForWindow(this); }, this, "Toplevel");
hyprListener_toplevelFullscreen.initCallback(
&m_phForeignToplevel->events.request_fullscreen,
[&](void* owner, void* data) {
const auto EV = (wlr_foreign_toplevel_handle_v1_fullscreen_event*)data;
g_pCompositor->setWindowFullscreen(this, EV->fullscreen, FULLSCREEN_FULL);
},
this, "Toplevel");
hyprListener_toplevelClose.initCallback(
&m_phForeignToplevel->events.request_close, [&](void* owner, void* data) { g_pCompositor->closeWindow(this); }, this, "Toplevel");
m_iLastToplevelMonitorID = m_iMonitorID;
}
void CWindow::destroyToplevelHandle() {
if (!m_phForeignToplevel)
return;
hyprListener_toplevelActivate.removeCallback();
hyprListener_toplevelClose.removeCallback();
hyprListener_toplevelFullscreen.removeCallback();
wlr_foreign_toplevel_handle_v1_destroy(m_phForeignToplevel);
m_phForeignToplevel = nullptr;
}
void CWindow::updateToplevel() {
updateSurfaceScaleTransformDetails();
if (!m_phForeignToplevel)
return;
wlr_foreign_toplevel_handle_v1_set_title(m_phForeignToplevel, m_szTitle.c_str());
wlr_foreign_toplevel_handle_v1_set_fullscreen(m_phForeignToplevel, m_bIsFullscreen);
if (m_iLastToplevelMonitorID != m_iMonitorID) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iLastToplevelMonitorID); PMONITOR && PMONITOR->m_bEnabled)
wlr_foreign_toplevel_handle_v1_output_leave(m_phForeignToplevel, PMONITOR->output);
wlr_foreign_toplevel_handle_v1_output_enter(m_phForeignToplevel, g_pCompositor->getMonitorFromID(m_iMonitorID)->output);
m_iLastToplevelMonitorID = m_iMonitorID;
}
}
void sendEnterIter(wlr_surface* pSurface, int x, int y, void* data) {
@@ -377,47 +328,65 @@ void CWindow::updateSurfaceScaleTransformDetails() {
this);
}
void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID == workspaceID)
void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
if (m_pWorkspace == pWorkspace)
return;
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
if (!m_szInitialWorkspaceToken.empty()) {
const auto TOKEN = g_pTokenManager->getToken(m_szInitialWorkspaceToken);
if (TOKEN) {
if (*PINITIALWSTRACKING == 2) {
// persistent
SInitialWorkspaceToken token = std::any_cast<SInitialWorkspaceToken>(TOKEN->data);
if (token.primaryOwner.lock().get() == this) {
token.workspace = pWorkspace->getConfigName();
TOKEN->data = token;
}
}
}
}
static auto PCLOSEONLASTSPECIAL = CConfigValue<Hyprlang::INT>("misc:close_special_on_empty");
const int OLDWORKSPACE = m_iWorkspaceID;
const auto OLDWORKSPACE = m_pWorkspace;
m_iWorkspaceID = workspaceID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
m_pWorkspace = pWorkspace;
setAnimationsToMove();
updateSpecialRenderData();
g_pCompositor->updateWorkspaceWindows(OLDWORKSPACE->m_iID);
g_pCompositor->updateWorkspaceSpecialRenderData(OLDWORKSPACE->m_iID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(OLDWORKSPACE->m_iMonitorID);
if (PWORKSPACE) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", std::format("{:x},{}", (uintptr_t)this, PWORKSPACE->m_szName)});
g_pEventManager->postEvent(SHyprIPCEvent{"movewindowv2", std::format("{:x},{},{}", (uintptr_t)this, PWORKSPACE->m_iID, PWORKSPACE->m_szName)});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{this, PWORKSPACE}));
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceSpecialRenderData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
if (valid(pWorkspace)) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", std::format("{:x},{}", (uintptr_t)this, pWorkspace->m_szName)});
g_pEventManager->postEvent(SHyprIPCEvent{"movewindowv2", std::format("{:x},{},{}", (uintptr_t)this, pWorkspace->m_iID, pWorkspace->m_szName)});
EMIT_HOOK_EVENT("moveWindow", (std::vector<std::any>{m_pSelf.lock(), pWorkspace}));
}
if (m_pSwallowed) {
m_pSwallowed->moveToWorkspace(workspaceID);
m_pSwallowed->m_iMonitorID = m_iMonitorID;
if (const auto SWALLOWED = m_pSwallowed.lock()) {
SWALLOWED->moveToWorkspace(pWorkspace);
SWALLOWED->m_iMonitorID = m_iMonitorID;
}
// update xwayland coords
g_pXWaylandManager->setWindowSize(this, m_vRealSize.value());
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.value());
if (g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE) == 0 && *PCLOSEONLASTSPECIAL) {
const auto PWS = g_pCompositor->getWorkspaceByID(OLDWORKSPACE);
if (PWS) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWS->m_iMonitorID); PMONITOR)
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)
PMONITOR->setSpecialWorkspace(nullptr);
}
}
}
CWindow* CWindow::X11TransientFor() {
PHLWINDOW CWindow::X11TransientFor() {
if (!m_bIsX11)
return nullptr;
@@ -426,11 +395,11 @@ CWindow* CWindow::X11TransientFor() {
auto PPARENT = g_pCompositor->getWindowFromSurface(m_uSurface.xwayland->parent->surface);
while (g_pCompositor->windowValidMapped(PPARENT) && PPARENT->m_uSurface.xwayland->parent) {
while (validMapped(PPARENT) && PPARENT->m_uSurface.xwayland->parent) {
PPARENT = g_pCompositor->getWindowFromSurface(PPARENT->m_uSurface.xwayland->parent->surface);
}
if (!g_pCompositor->windowValidMapped(PPARENT))
if (!validMapped(PPARENT))
return nullptr;
return PPARENT;
@@ -452,8 +421,26 @@ void unregisterVar(void* ptr) {
void CWindow::onUnmap() {
static auto PCLOSEONLASTSPECIAL = CConfigValue<Hyprlang::INT>("misc:close_special_on_empty");
if (g_pCompositor->m_pLastWindow == this)
g_pCompositor->m_pLastWindow = nullptr;
if (g_pCompositor->m_pLastWindow.lock().get() == this)
g_pCompositor->m_pLastWindow.reset();
if (g_pInputManager->currentlyDraggedWindow.lock().get() == this)
g_pInputManager->currentlyDraggedWindow.reset();
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
if (!m_szInitialWorkspaceToken.empty()) {
const auto TOKEN = g_pTokenManager->getToken(m_szInitialWorkspaceToken);
if (TOKEN) {
if (*PINITIALWSTRACKING == 2) {
// persistent token, but the first window got removed so the token is gone
SInitialWorkspaceToken token = std::any_cast<SInitialWorkspaceToken>(TOKEN->data);
if (token.primaryOwner.lock().get() == this)
g_pTokenManager->removeToken(TOKEN);
}
}
}
m_iLastWorkspace = m_pWorkspace->m_iID;
m_vRealPosition.setCallbackOnEnd(unregisterVar);
m_vRealSize.setCallbackOnEnd(unregisterVar);
@@ -466,22 +453,27 @@ void CWindow::onUnmap() {
m_vRealSize.setCallbackOnBegin(nullptr);
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other == this; });
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other.expired() || other.lock().get() == this; });
hyprListener_unmapWindow.removeCallback();
if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(m_iWorkspaceID) == 0 && g_pCompositor->isWorkspaceSpecial(m_iWorkspaceID)) {
if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(workspaceID()) == 0 && onSpecialWorkspace()) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID)
if (PMONITOR && PMONITOR->activeSpecialWorkspace && PMONITOR->activeSpecialWorkspace == m_pWorkspace)
PMONITOR->setSpecialWorkspace(nullptr);
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient == this)
PMONITOR->solitaryClient = nullptr;
if (PMONITOR && PMONITOR->solitaryClient.lock().get() == this)
PMONITOR->solitaryClient.reset();
g_pCompositor->updateWorkspaceWindows(m_iWorkspaceID);
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceSpecialRenderData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
m_pWorkspace.reset();
if (m_bIsX11)
return;
@@ -515,7 +507,7 @@ void CWindow::onMap() {
m_fBorderAngleAnimationProgress.setValueAndWarp(0.f);
m_fBorderAngleAnimationProgress = 1.f;
g_pCompositor->m_vWindowFocusHistory.push_back(this);
g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf);
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this,
"CWindow");
@@ -523,19 +515,11 @@ void CWindow::onMap() {
m_vReportedSize = m_vPendingReportedSize;
m_bAnimatingIn = true;
for (const auto& ctrl : g_pHyprRenderer->m_vTearingControllers) {
if (ctrl->pWlrHint->surface != m_pWLSurface.wlr())
continue;
m_bTearingHint = ctrl->pWlrHint->current;
break;
}
if (m_bIsX11)
return;
m_pSubsurfaceHead = std::make_unique<CSubsurface>(this);
m_pPopupHead = std::make_unique<CPopup>(this);
m_pSubsurfaceHead = std::make_unique<CSubsurface>(m_pSelf.lock());
m_pPopupHead = std::make_unique<CPopup>(m_pSelf.lock());
}
void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -557,8 +541,8 @@ void CWindow::onBorderAngleAnimEnd(void* ptr) {
void CWindow::setHidden(bool hidden) {
m_bHidden = hidden;
if (hidden && g_pCompositor->m_pLastWindow == this) {
g_pCompositor->m_pLastWindow = nullptr;
if (hidden && g_pCompositor->m_pLastWindow.lock().get() == this) {
g_pCompositor->m_pLastWindow.reset();
}
setSuspended(hidden);
@@ -605,25 +589,36 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
continue;
if (r == "override") {
if (opacityIDX == 1) {
if (opacityIDX == 1)
m_sSpecialRenderData.alphaOverride = true;
else if (opacityIDX == 2)
m_sSpecialRenderData.alphaInactiveOverride = true;
} else if (opacityIDX == 2)
m_sSpecialRenderData.alphaInactiveOverride = true;
else if (opacityIDX == 3)
m_sSpecialRenderData.alphaFullscreenOverride = true;
} else {
if (opacityIDX == 0) {
m_sSpecialRenderData.alpha = std::stof(r);
m_sSpecialRenderData.alphaInactive = std::stof(r);
m_sSpecialRenderData.alphaOverride = false;
} else if (opacityIDX == 1) {
m_sSpecialRenderData.alphaInactive = std::stof(r);
m_sSpecialRenderData.alphaInactiveOverride = false;
} else if (opacityIDX == 2) {
m_sSpecialRenderData.alphaFullscreen = std::stof(r);
m_sSpecialRenderData.alphaFullscreenOverride = false;
} else {
throw std::runtime_error("more than 2 alpha values");
throw std::runtime_error("more than 3 alpha values");
}
opacityIDX++;
}
}
if (opacityIDX == 1) {
m_sSpecialRenderData.alphaInactiveOverride = m_sSpecialRenderData.alphaOverride;
m_sSpecialRenderData.alphaInactive = m_sSpecialRenderData.alpha;
m_sSpecialRenderData.alphaFullscreenOverride = m_sSpecialRenderData.alphaOverride;
m_sSpecialRenderData.alphaFullscreen = m_sSpecialRenderData.alpha;
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "noanim") {
m_sAdditionalConfigData.forceNoAnims = true;
@@ -706,8 +701,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.maxSize = VEC;
m_vRealSize = Vector2D(std::min((double)m_sAdditionalConfigData.maxSize.toUnderlying().x, m_vRealSize.goal().x),
std::min((double)m_sAdditionalConfigData.maxSize.toUnderlying().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(this, m_vRealSize.goal());
setHidden(false);
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 {
@@ -722,7 +716,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.minSize = VEC;
m_vRealSize = Vector2D(std::max((double)m_sAdditionalConfigData.minSize.toUnderlying().x, m_vRealSize.goal().x),
std::max((double)m_sAdditionalConfigData.minSize.toUnderlying().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(this, m_vRealSize.goal());
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()); }
}
@@ -753,11 +748,13 @@ void CWindow::updateDynamicRules() {
m_sAdditionalConfigData.nearestNeighbor = false;
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this);
for (auto& r : WINDOWRULES) {
m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock());
for (auto& r : m_vMatchedRules) {
applyDynamicRule(r);
}
EMIT_HOOK_EVENT("windowUpdateRules", m_pSelf.lock());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
}
@@ -817,7 +814,7 @@ void CWindow::applyGroupRules() {
if ((m_eGroupRules & GROUP_SET && m_bFirstMap) || m_eGroupRules & GROUP_SET_ALWAYS)
createGroup();
if (m_sGroupData.pNextWindow && ((m_eGroupRules & GROUP_LOCK && m_bFirstMap) || m_eGroupRules & GROUP_LOCK_ALWAYS))
if (m_sGroupData.pNextWindow.lock() && ((m_eGroupRules & GROUP_LOCK && m_bFirstMap) || m_eGroupRules & GROUP_LOCK_ALWAYS))
getGroupHead()->m_sGroupData.locked = true;
}
@@ -827,39 +824,46 @@ void CWindow::createGroup() {
return;
}
if (!m_sGroupData.pNextWindow) {
m_sGroupData.pNextWindow = this;
if (m_sGroupData.pNextWindow.expired()) {
m_sGroupData.pNextWindow = m_pSelf;
m_sGroupData.head = true;
m_sGroupData.locked = false;
m_sGroupData.deny = false;
addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(this));
addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(m_pSelf.lock()));
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceSpecialRenderData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
}
}
void CWindow::destroyGroup() {
if (m_sGroupData.pNextWindow == this) {
if (m_sGroupData.pNextWindow.lock().get() == this) {
if (m_eGroupRules & GROUP_SET_ALWAYS) {
Debug::log(LOG, "destoryGroup: window:{:x},title:{} has rule [group set always], ignored", (uintptr_t)this, this->m_szTitle);
return;
}
m_sGroupData.pNextWindow = nullptr;
m_sGroupData.pNextWindow.reset();
m_sGroupData.head = false;
updateWindowDecos();
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceSpecialRenderData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
return;
}
CWindow* curr = this;
std::vector<CWindow*> members;
PHLWINDOW curr = m_pSelf.lock();
std::vector<PHLWINDOW> members;
do {
const auto PLASTWIN = curr;
curr = curr->m_sGroupData.pNextWindow;
PLASTWIN->m_sGroupData.pNextWindow = nullptr;
curr = curr->m_sGroupData.pNextWindow.lock();
PLASTWIN->m_sGroupData.pNextWindow.reset();
curr->setHidden(false);
members.push_back(curr);
} while (curr != this);
} while (curr.get() != this);
for (auto& w : members) {
if (w->m_sGroupData.head)
@@ -874,81 +878,86 @@ void CWindow::destroyGroup() {
w->updateWindowDecos();
}
g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV;
g_pCompositor->updateWorkspaceWindows(workspaceID());
g_pCompositor->updateWorkspaceSpecialRenderData(workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
}
CWindow* CWindow::getGroupHead() {
CWindow* curr = this;
PHLWINDOW CWindow::getGroupHead() {
PHLWINDOW curr = m_pSelf.lock();
while (!curr->m_sGroupData.head)
curr = curr->m_sGroupData.pNextWindow;
curr = curr->m_sGroupData.pNextWindow.lock();
return curr;
}
CWindow* CWindow::getGroupTail() {
CWindow* curr = this;
while (!curr->m_sGroupData.pNextWindow->m_sGroupData.head)
curr = curr->m_sGroupData.pNextWindow;
PHLWINDOW CWindow::getGroupTail() {
PHLWINDOW curr = m_pSelf.lock();
while (!curr->m_sGroupData.pNextWindow.lock()->m_sGroupData.head)
curr = curr->m_sGroupData.pNextWindow.lock();
return curr;
}
CWindow* CWindow::getGroupCurrent() {
CWindow* curr = this;
PHLWINDOW CWindow::getGroupCurrent() {
PHLWINDOW curr = m_pSelf.lock();
while (curr->isHidden())
curr = curr->m_sGroupData.pNextWindow;
curr = curr->m_sGroupData.pNextWindow.lock();
return curr;
}
int CWindow::getGroupSize() {
int size = 1;
CWindow* curr = this;
while (curr->m_sGroupData.pNextWindow != this) {
curr = curr->m_sGroupData.pNextWindow;
PHLWINDOW curr = m_pSelf.lock();
while (curr->m_sGroupData.pNextWindow.lock().get() != this) {
curr = curr->m_sGroupData.pNextWindow.lock();
size++;
}
return size;
}
bool CWindow::canBeGroupedInto(CWindow* pWindow) {
bool CWindow::canBeGroupedInto(PHLWINDOW pWindow) {
return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged
&& ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or
|| (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked
&& !(m_sGroupData.pNextWindow && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !(m_sGroupData.pNextWindow.lock() && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !m_sGroupData.deny // source is not denied entry
&& !(m_eGroupRules & GROUP_BARRED && m_bFirstMap); // group rule doesn't prevent adding window
}
CWindow* CWindow::getGroupWindowByIndex(int index) {
PHLWINDOW CWindow::getGroupWindowByIndex(int index) {
const int SIZE = getGroupSize();
index = ((index % SIZE) + SIZE) % SIZE;
CWindow* curr = getGroupHead();
PHLWINDOW curr = getGroupHead();
while (index > 0) {
curr = curr->m_sGroupData.pNextWindow;
curr = curr->m_sGroupData.pNextWindow.lock();
index--;
}
return curr;
}
void CWindow::setGroupCurrent(CWindow* pWindow) {
CWindow* curr = this->m_sGroupData.pNextWindow;
void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
PHLWINDOW curr = m_sGroupData.pNextWindow.lock();
bool isMember = false;
while (curr != this) {
while (curr.get() != this) {
if (curr == pWindow) {
isMember = true;
break;
}
curr = curr->m_sGroupData.pNextWindow;
curr = curr->m_sGroupData.pNextWindow.lock();
}
if (!isMember && pWindow != this)
if (!isMember && pWindow.get() != this)
return;
const auto PCURRENT = getGroupCurrent();
const bool FULLSCREEN = PCURRENT->m_bIsFullscreen;
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PCURRENT->m_iWorkspaceID);
const auto WORKSPACE = PCURRENT->m_pWorkspace;
const auto PWINDOWSIZE = PCURRENT->m_vRealSize.goal();
const auto PWINDOWPOS = PCURRENT->m_vRealPosition.goal();
const auto CURRENTISFOCUS = PCURRENT == g_pCompositor->m_pLastWindow;
const auto CURRENTISFOCUS = PCURRENT == g_pCompositor->m_pLastWindow.lock();
if (FULLSCREEN)
g_pCompositor->setWindowFullscreen(PCURRENT, false, WORKSPACE->m_efFullscreenMode);
@@ -976,14 +985,14 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
pWindow->updateWindowDecos();
}
void CWindow::insertWindowToGroup(CWindow* pWindow) {
const auto BEGINAT = this;
const auto ENDAT = m_sGroupData.pNextWindow;
void CWindow::insertWindowToGroup(PHLWINDOW pWindow) {
const auto BEGINAT = m_pSelf.lock();
const auto ENDAT = m_sGroupData.pNextWindow.lock();
if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
if (!pWindow->m_sGroupData.pNextWindow) {
if (!pWindow->m_sGroupData.pNextWindow.lock()) {
BEGINAT->m_sGroupData.pNextWindow = pWindow;
pWindow->m_sGroupData.pNextWindow = ENDAT;
pWindow->m_sGroupData.head = false;
@@ -998,26 +1007,26 @@ void CWindow::insertWindowToGroup(CWindow* pWindow) {
STAIL->m_sGroupData.pNextWindow = ENDAT;
}
CWindow* CWindow::getGroupPrevious() {
CWindow* curr = m_sGroupData.pNextWindow;
PHLWINDOW CWindow::getGroupPrevious() {
PHLWINDOW curr = m_sGroupData.pNextWindow.lock();
while (curr != this && curr->m_sGroupData.pNextWindow != this)
curr = curr->m_sGroupData.pNextWindow;
while (curr != m_pSelf.lock() && curr->m_sGroupData.pNextWindow.lock().get() != this)
curr = curr->m_sGroupData.pNextWindow.lock();
return curr;
}
void CWindow::switchWithWindowInGroup(CWindow* pWindow) {
if (!m_sGroupData.pNextWindow || !pWindow->m_sGroupData.pNextWindow)
void CWindow::switchWithWindowInGroup(PHLWINDOW pWindow) {
if (!m_sGroupData.pNextWindow.lock() || !pWindow->m_sGroupData.pNextWindow.lock())
return;
if (m_sGroupData.pNextWindow == pWindow) { // A -> this -> pWindow -> B >> A -> pWindow -> this -> B
if (m_sGroupData.pNextWindow.lock() == pWindow) { // A -> this -> pWindow -> B >> A -> pWindow -> this -> B
getGroupPrevious()->m_sGroupData.pNextWindow = pWindow;
m_sGroupData.pNextWindow = pWindow->m_sGroupData.pNextWindow;
pWindow->m_sGroupData.pNextWindow = this;
pWindow->m_sGroupData.pNextWindow = m_pSelf;
} else if (pWindow->m_sGroupData.pNextWindow == this) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
pWindow->getGroupPrevious()->m_sGroupData.pNextWindow = this;
} else if (pWindow->m_sGroupData.pNextWindow.lock().get() == this) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
pWindow->getGroupPrevious()->m_sGroupData.pNextWindow = m_pSelf;
pWindow->m_sGroupData.pNextWindow = m_sGroupData.pNextWindow;
m_sGroupData.pNextWindow = pWindow;
@@ -1031,19 +1040,21 @@ void CWindow::switchWithWindowInGroup(CWindow* pWindow) {
}
void CWindow::updateGroupOutputs() {
if (!m_sGroupData.pNextWindow)
if (m_sGroupData.pNextWindow.expired())
return;
CWindow* curr = m_sGroupData.pNextWindow;
PHLWINDOW curr = m_sGroupData.pNextWindow.lock();
while (curr != this) {
const auto WS = m_pWorkspace;
while (curr.get() != this) {
curr->m_iMonitorID = m_iMonitorID;
curr->moveToWorkspace(m_iWorkspaceID);
curr->moveToWorkspace(WS);
curr->m_vRealPosition = m_vRealPosition.goal();
curr->m_vRealSize = m_vRealSize.goal();
curr = curr->m_sGroupData.pNextWindow;
curr = curr->m_sGroupData.pNextWindow.lock();
}
}
@@ -1058,7 +1069,7 @@ bool CWindow::opaque() {
if (m_vRealSize.goal().floor() != m_vReportedSize)
return false;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
const auto PWORKSPACE = m_pWorkspace;
if (m_pWLSurface.small() && !m_pWLSurface.m_bFillIgnoreSmall)
return false;
@@ -1088,37 +1099,27 @@ float CWindow::rounding() {
}
void CWindow::updateSpecialRenderData() {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
const auto WORKSPACERULES = PWORKSPACE ? g_pConfigManager->getWorkspaceRulesFor(PWORKSPACE) : std::vector<SWorkspaceRule>{};
bool border = true;
const auto PWORKSPACE = m_pWorkspace;
const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{};
updateSpecialRenderData(WORKSPACERULE);
}
void CWindow::updateSpecialRenderData(const SWorkspaceRule& workspaceRule) {
static auto PNOBORDERONFLOATING = CConfigValue<Hyprlang::INT>("general:no_border_on_floating");
bool border = true;
if (m_bIsFloating && *PNOBORDERONFLOATING == 1)
border = false;
m_sSpecialRenderData.border = border;
m_sSpecialRenderData.borderSize = -1;
m_sSpecialRenderData.decorate = true;
m_sSpecialRenderData.rounding = true;
m_sSpecialRenderData.shadow = true;
for (auto& wsRule : WORKSPACERULES) {
if (wsRule.border.has_value())
m_sSpecialRenderData.border = wsRule.border.value();
if (wsRule.borderSize.has_value())
m_sSpecialRenderData.borderSize = wsRule.borderSize.value();
if (wsRule.decorate.has_value())
m_sSpecialRenderData.decorate = wsRule.decorate.value();
if (wsRule.rounding.has_value())
m_sSpecialRenderData.rounding = wsRule.rounding.value();
if (wsRule.shadow.has_value())
m_sSpecialRenderData.shadow = wsRule.shadow.value();
}
m_sSpecialRenderData.border = workspaceRule.border.value_or(border);
m_sSpecialRenderData.borderSize = workspaceRule.borderSize.value_or(-1);
m_sSpecialRenderData.decorate = workspaceRule.decorate.value_or(true);
m_sSpecialRenderData.rounding = workspaceRule.rounding.value_or(true);
m_sSpecialRenderData.shadow = workspaceRule.shadow.value_or(true);
}
int CWindow::getRealBorderSize() {
if (!m_sSpecialRenderData.border || m_sAdditionalConfigData.forceNoBorder)
if (!m_sSpecialRenderData.border || m_sAdditionalConfigData.forceNoBorder || (m_pWorkspace && m_bIsFullscreen && (m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)))
return 0;
if (m_sAdditionalConfigData.borderSize.toUnderlying() != -1)
@@ -1137,7 +1138,7 @@ bool CWindow::canBeTorn() {
}
bool CWindow::shouldSendFullscreenState() {
const auto MODE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID)->m_efFullscreenMode;
const auto MODE = m_pWorkspace->m_efFullscreenMode;
return m_bDontSendFullscreen ? false : (m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL)));
}
@@ -1173,7 +1174,7 @@ void CWindow::onWorkspaceAnimUpdate() {
}
Vector2D offset;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
const auto PWORKSPACE = m_pWorkspace;
if (!PWORKSPACE)
return;
@@ -1212,3 +1213,68 @@ int CWindow::popupsCount() {
m_uSurface.xdg, [](wlr_surface* s, int x, int y, void* data) { *(int*)data += 1; }, &no);
return no;
}
int CWindow::workspaceID() {
return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace;
}
bool CWindow::onSpecialWorkspace() {
return m_pWorkspace ? m_pWorkspace->m_bIsSpecialWorkspace : g_pCompositor->isWorkspaceSpecial(m_iLastWorkspace);
}
std::unordered_map<std::string, std::string> CWindow::getEnv() {
const auto PID = getPID();
if (PID <= 1)
return {};
std::unordered_map<std::string, std::string> results;
//
std::string environFile = "/proc/" + std::to_string(PID) + "/environ";
std::ifstream ifs(environFile, std::ios::binary);
if (!ifs.good())
return {};
std::vector<char> buffer;
size_t needle = 0;
buffer.resize(512, '\0');
while (ifs.read(buffer.data() + needle, 512)) {
buffer.resize(buffer.size() + 512, '\0');
needle += 512;
}
std::replace(buffer.begin(), buffer.end() - 1, '\0', '\n');
CVarList envs(std::string{buffer.data(), needle - 1}, 0, '\n', true);
for (auto& e : envs) {
if (!e.contains('='))
continue;
const auto EQ = e.find_first_of('=');
results[e.substr(0, EQ)] = e.substr(EQ + 1);
}
return results;
}
void CWindow::activate() {
static auto PFOCUSONACTIVATE = CConfigValue<Hyprlang::INT>("misc:focus_on_activate");
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)this)});
EMIT_HOOK_EVENT("urgent", m_pSelf.lock());
m_bIsUrgent = true;
if (!*PFOCUSONACTIVATE || (m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE))
return;
if (m_bIsFloating)
g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true);
g_pCompositor->focusWindow(m_pSelf.lock());
g_pCompositor->warpCursorTo(middle());
}

View File

@@ -11,6 +11,8 @@
#include "Popup.hpp"
#include "../macros.hpp"
#include "../managers/XWaylandManager.hpp"
#include "DesktopTypes.hpp"
#include "../helpers/signal/Signal.hpp"
enum eIdleInhibitMode {
IDLEINHIBIT_NONE = 0,
@@ -129,6 +131,8 @@ struct SWindowSpecialRenderData {
CWindowOverridableVar<float> alpha = 1.f;
CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<bool> alphaFullscreenOverride = false;
CWindowOverridableVar<float> alphaFullscreen = -1.f; // -1 means unset
CWindowOverridableVar<CGradientValueData> activeBorderColor = CGradientValueData(); // empty color vector means unset
CWindowOverridableVar<CGradientValueData> inactiveBorderColor = CGradientValueData(); // empty color vector means unset
@@ -184,9 +188,19 @@ struct SWindowRule {
std::string szWorkspace = ""; // empty means any
};
struct SInitialWorkspaceToken {
PHLWINDOWREF primaryOwner;
std::string workspace;
};
class CWindow {
public:
static PHLWINDOW create();
private:
CWindow();
public:
~CWindow();
DYNLISTENER(commitWindow);
@@ -213,6 +227,10 @@ class CWindow {
CWLSurface m_pWLSurface;
struct {
CSignal destroy;
} events;
union {
wlr_xdg_surface* xdg;
wlr_xwayland_surface* xwayland;
@@ -254,7 +272,7 @@ class CWindow {
std::string m_szTitle = "";
std::string m_szInitialTitle = "";
std::string m_szInitialClass = "";
int m_iWorkspaceID = -1;
PHLWORKSPACE m_pWorkspace;
bool m_bIsMapped = false;
@@ -265,7 +283,7 @@ class CWindow {
// XWayland stuff
bool m_bIsX11 = false;
CWindow* m_pX11Parent = nullptr;
PHLWINDOWREF m_pX11Parent;
uint64_t m_iX11Type = 0;
bool m_bIsModal = false;
bool m_bX11DoesntWantBorders = false;
@@ -311,10 +329,7 @@ class CWindow {
bool m_bFakeFullscreenState = false;
// for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window.
CWindow* m_pLastCycledWindow = nullptr;
// Foreign Toplevel proto
wlr_foreign_toplevel_handle_v1* m_phForeignToplevel = nullptr;
PHLWINDOWREF m_pLastCycledWindow;
// Window decorations
std::deque<std::unique_ptr<IHyprWindowDecoration>> m_dWindowDecorations;
@@ -337,7 +352,7 @@ class CWindow {
CAnimatedVariable<float> m_fDimPercent;
// swallowing
CWindow* m_pSwallowed = nullptr;
PHLWINDOWREF m_pSwallowed;
// focus stuff
bool m_bStayFocused = false;
@@ -349,9 +364,12 @@ class CWindow {
// for idle inhibiting windows
eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE;
// initial token. Will be unregistered on workspace change or timeout of 2 minutes
std::string m_szInitialWorkspaceToken = "";
// for groups
struct SGroupData {
CWindow* pNextWindow = nullptr; // nullptr means no grouping. Self means single group.
PHLWINDOWREF pNextWindow; // nullptr means no grouping. Self means single group.
bool head = false;
bool locked = false; // per group lock
bool deny = false; // deny window from enter a group or made a group
@@ -360,6 +378,9 @@ class CWindow {
bool m_bTearingHint = false;
// stores the currently matched window rules
std::vector<SWindowRule> m_vMatchedRules;
// For the list lookup
bool operator==(const CWindow& rhs) {
return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
@@ -380,12 +401,10 @@ class CWindow {
pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType);
void removeDecorationByType(eDecorationType);
void createToplevelHandle();
void destroyToplevelHandle();
void updateToplevel();
void updateSurfaceScaleTransformDetails();
void moveToWorkspace(int);
CWindow* X11TransientFor();
void moveToWorkspace(PHLWORKSPACE);
PHLWINDOW X11TransientFor();
void onUnmap();
void onMap();
void setHidden(bool hidden);
@@ -400,9 +419,13 @@ class CWindow {
bool shouldSendFullscreenState();
void setSuspended(bool suspend);
bool visibleOnMonitor(CMonitor* pMonitor);
int workspaceID();
bool onSpecialWorkspace();
void activate();
int getRealBorderSize();
void updateSpecialRenderData();
void updateSpecialRenderData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(void* ptr);
bool isInCurvedCorner(double x, double y);
@@ -412,26 +435,53 @@ class CWindow {
void applyGroupRules();
void createGroup();
void destroyGroup();
CWindow* getGroupHead();
CWindow* getGroupTail();
CWindow* getGroupCurrent();
CWindow* getGroupPrevious();
CWindow* getGroupWindowByIndex(int);
PHLWINDOW getGroupHead();
PHLWINDOW getGroupTail();
PHLWINDOW getGroupCurrent();
PHLWINDOW getGroupPrevious();
PHLWINDOW getGroupWindowByIndex(int);
int getGroupSize();
bool canBeGroupedInto(CWindow* pWindow);
void setGroupCurrent(CWindow* pWindow);
void insertWindowToGroup(CWindow* pWindow);
bool canBeGroupedInto(PHLWINDOW pWindow);
void setGroupCurrent(PHLWINDOW pWindow);
void insertWindowToGroup(PHLWINDOW pWindow);
void updateGroupOutputs();
void switchWithWindowInGroup(CWindow* pWindow);
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
//
std::unordered_map<std::string, std::string> getEnv();
//
PHLWINDOWREF m_pSelf;
private:
// For hidden windows and stuff
bool m_bHidden = false;
bool m_bSuspended = false;
int m_iLastWorkspace = WORKSPACE_INVALID;
};
inline bool valid(PHLWINDOW w) {
return w.get();
}
inline bool valid(PHLWINDOWREF w) {
return w.lock().get();
}
inline bool validMapped(PHLWINDOW w) {
if (!valid(w))
return false;
return w->m_bIsMapped;
}
inline bool validMapped(PHLWINDOWREF w) {
if (!valid(w))
return false;
return w.lock()->m_bIsMapped;
}
/**
format specification
- 'x', only address, equivalent of (uintpr_t)CWindow*
@@ -441,7 +491,7 @@ class CWindow {
*/
template <typename CharT>
struct std::formatter<CWindow*, CharT> : std::formatter<CharT> {
struct std::formatter<PHLWINDOW, CharT> : std::formatter<CharT> {
bool formatAddressOnly = false;
bool formatWorkspace = false;
bool formatMonitor = false;
@@ -451,20 +501,20 @@ struct std::formatter<CWindow*, CharT> : std::formatter<CharT> {
FORMAT_FLAG('m', formatMonitor) //
FORMAT_FLAG('w', formatWorkspace) //
FORMAT_FLAG('c', formatClass),
CWindow*)
PHLWINDOW)
template <typename FormatContext>
auto format(CWindow* const& w, FormatContext& ctx) const {
auto format(PHLWINDOW const& w, FormatContext& ctx) const {
auto&& out = ctx.out();
if (formatAddressOnly)
return std::format_to(out, "{:x}", (uintptr_t)w);
return std::format_to(out, "{:x}", (uintptr_t)w.get());
if (!w)
return std::format_to(out, "[Window nullptr]");
std::format_to(out, "[");
std::format_to(out, "Window {:x}: title: \"{}\"", (uintptr_t)w, w->m_szTitle);
std::format_to(out, "Window {:x}: title: \"{}\"", (uintptr_t)w.get(), w->m_szTitle);
if (formatWorkspace)
std::format_to(out, ", workspace: {}", w->m_iWorkspaceID);
std::format_to(out, ", workspace: {}", w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID);
if (formatMonitor)
std::format_to(out, ", monitor: {}", w->m_iMonitorID);
if (formatClass)

View File

@@ -2,33 +2,50 @@
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
if (!PMONITOR) {
Debug::log(ERR, "Attempted a creation of CWorkspace with an invalid monitor?");
return;
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special) {
PHLWORKSPACE workspace = std::make_shared<CWorkspace>(id, monitorID, name, special);
workspace->init(workspace);
return workspace;
}
CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special) {
m_iMonitorID = monitorID;
m_iID = id;
m_szName = name;
m_bIsSpecialWorkspace = special;
}
m_vRenderOffset.create(special ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"), this,
AVARDAMAGE_ENTIRE);
m_fAlpha.create(AVARTYPE_FLOAT, special ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"), this,
void CWorkspace::init(PHLWORKSPACE self) {
m_pSelf = self;
m_vRenderOffset.create(m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"),
self, AVARDAMAGE_ENTIRE);
m_fAlpha.create(AVARTYPE_FLOAT,
m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"), self,
AVARDAMAGE_ENTIRE);
m_fAlpha.setValueAndWarp(1.f);
m_vRenderOffset.registerVar();
m_fAlpha.registerVar();
const auto RULESFORTHIS = g_pConfigManager->getWorkspaceRulesFor(this);
for (auto& rule : RULESFORTHIS) {
if (rule.defaultName.has_value())
m_szName = rule.defaultName.value();
}
const auto RULEFORTHIS = g_pConfigManager->getWorkspaceRuleFor(self);
if (RULEFORTHIS.defaultName.has_value())
m_szName = RULEFORTHIS.defaultName.value();
m_pFocusedWindowHook = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any param) {
const auto PWINDOW = std::any_cast<PHLWINDOW>(param);
if (PWINDOW == m_pLastFocusedWindow.lock())
m_pLastFocusedWindow.reset();
});
m_bInert = false;
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
m_bPersistent = WORKSPACERULE.isPersistent;
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
g_pKeybindManager->spawn(*cmd);
g_pEventManager->postEvent({"createworkspace", m_szName});
g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)});
@@ -40,10 +57,16 @@ CWorkspace::~CWorkspace() {
Debug::log(LOG, "Destroying workspace ID {}", m_iID);
// check if g_pHookSystem and g_pEventManager exist, they might be destroyed as in when the compositor is closing.
if (g_pHookSystem)
g_pHookSystem->unhook(m_pFocusedWindowHook);
if (g_pEventManager) {
g_pEventManager->postEvent({"destroyworkspace", m_szName});
g_pEventManager->postEvent({"destroyworkspacev2", std::format("{},{}", m_iID, m_szName)});
EMIT_HOOK_EVENT("destroyWorkspace", this);
}
}
void CWorkspace::startAnim(bool in, bool left, bool instant) {
const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle;
@@ -52,7 +75,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
// set floating windows offset callbacks
m_vRenderOffset.setUpdateCallback([&](void*) {
for (auto& w : g_pCompositor->m_vWindows) {
if (!g_pCompositor->windowValidMapped(w.get()) || w->m_iWorkspaceID != m_iID)
if (!validMapped(w) || w->workspaceID() != m_iID)
continue;
w->onWorkspaceAnimUpdate();
@@ -159,14 +182,14 @@ void CWorkspace::moveToMonitor(const int& id) {
; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40
}
CWindow* CWorkspace::getLastFocusedWindow() {
if (!g_pCompositor->windowValidMapped(m_pLastFocusedWindow) || m_pLastFocusedWindow->m_iWorkspaceID != m_iID)
PHLWINDOW CWorkspace::getLastFocusedWindow() {
if (!validMapped(m_pLastFocusedWindow) || m_pLastFocusedWindow.lock()->workspaceID() != m_iID)
return nullptr;
return m_pLastFocusedWindow;
return m_pLastFocusedWindow.lock();
}
void CWorkspace::rememberPrevWorkspace(const CWorkspace* prev) {
void CWorkspace::rememberPrevWorkspace(const PHLWORKSPACE& prev) {
if (!prev) {
m_sPrevWorkspace.iID = -1;
m_sPrevWorkspace.name = "";
@@ -184,7 +207,7 @@ void CWorkspace::rememberPrevWorkspace(const CWorkspace* prev) {
std::string CWorkspace::getConfigName() {
if (m_bIsSpecialWorkspace) {
return "special:" + m_szName;
return m_szName;
}
if (m_iID > 0)
@@ -211,7 +234,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
} else if (selector.starts_with("name:")) {
return m_szName == selector.substr(5);
} else if (selector.starts_with("special:")) {
} else if (selector.starts_with("special")) {
return m_szName == selector;
} else {
// parse selector
@@ -226,7 +249,11 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
// s - special: s[true]
// n - named: n[true] or n[s:string] or n[e:string]
// m - monitor: m[monitor_selector]
// w - windowCount: w[0-4] or w[1], optional flag t or f for tiled or floating, e.g. w[t0-1]
// w - windowCount: w[1-4] or w[1], optional flag t or f for tiled or floating and
// flag g to count groups instead of windows, e.g. w[t1-2], w[fg4]
// flag v will count only visible windows
// f - fullscreen state : f[-1], f[0], f[1], or f[2] for different fullscreen states
// -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);
@@ -310,10 +337,10 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
prop = prop.substr(2, prop.length() - 3);
if (prop.starts_with("s:"))
return m_szName.starts_with(prop.substr(2));
if (prop.starts_with("e:"))
return m_szName.ends_with(prop.substr(2));
if (prop.starts_with("s:") && !m_szName.starts_with(prop.substr(2)))
return false;
if (prop.starts_with("e:") && !m_szName.ends_with(prop.substr(2)))
return false;
const auto WANTSNAMED = configStringToInt(prop);
@@ -332,14 +359,28 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
prop = prop.substr(2, prop.length() - 3);
int wantsOnlyTiled = -1;
bool wantsCountGroup = false;
bool wantsCountVisible = false;
if (prop.starts_with("t")) {
int flagCount = 0;
for (auto& flag : prop) {
if (flag == 't' && wantsOnlyTiled == -1) {
wantsOnlyTiled = 1;
prop = prop.substr(1);
} else if (prop.starts_with("f")) {
flagCount++;
} else if (flag == 'f' && wantsOnlyTiled == -1) {
wantsOnlyTiled = 0;
prop = prop.substr(1);
flagCount++;
} else if (flag == 'g' && !wantsCountGroup) {
wantsCountGroup = true;
flagCount++;
} else if (flag == 'v' && !wantsCountVisible) {
wantsCountVisible = true;
flagCount++;
} else {
break;
}
}
prop = prop.substr(flagCount);
if (!prop.contains("-")) {
// try single
@@ -356,7 +397,17 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
return false;
}
return g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled)) == from;
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);
else
count = g_pCompositor->getWindowsOnWorkspace(m_iID, 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("-");
@@ -380,12 +431,57 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
return false;
}
const auto WINDOWSONWORKSPACE = g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled));
if (std::clamp(WINDOWSONWORKSPACE, from, to) != WINDOWSONWORKSPACE)
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);
else
count = g_pCompositor->getWindowsOnWorkspace(m_iID, 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;
continue;
}
if (cur == 'f') {
if (!prop.starts_with("f[") || !prop.ends_with("]")) {
Debug::log(LOG, "Invalid selector {}", selector);
return false;
}
prop = prop.substr(2, prop.length() - 3);
int FSSTATE = -1;
try {
FSSTATE = std::stoi(prop);
} catch (std::exception& e) {
Debug::log(LOG, "Invalid selector {}", selector);
return false;
}
switch (FSSTATE) {
case -1: // no fullscreen
if (m_bHasFullscreenWindow)
return false;
break;
case 0: // fullscreen full
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_FULL)
return false;
break;
case 1: // maximized
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_MAXIMIZED)
return false;
break;
case 2: // fullscreen without sending fullscreen state to window
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_FULL || !g_pCompositor->getFullscreenWindowOnWorkspace(m_iID) ||
!g_pCompositor->getFullscreenWindowOnWorkspace(m_iID)->m_bDontSendFullscreen)
return false;
break;
default: break;
}
continue;
}
Debug::log(LOG, "Invalid selector {}", selector);
return false;
}
@@ -396,3 +492,14 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
UNREACHABLE();
return false;
}
void CWorkspace::markInert() {
m_bInert = true;
m_iID = WORKSPACE_INVALID;
m_iMonitorID = -1;
m_bVisible = false;
}
bool CWorkspace::inert() {
return m_bInert;
}

View File

@@ -3,6 +3,7 @@
#include "../helpers/AnimatedVariable.hpp"
#include <string>
#include "../defines.hpp"
#include "DesktopTypes.hpp"
enum eFullscreenMode : int8_t {
FULLSCREEN_INVALID = -1,
@@ -14,6 +15,8 @@ class CWindow;
class CWorkspace {
public:
static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false);
// use create() don't use this
CWorkspace(int id, int monitorID, std::string name, bool special = false);
~CWorkspace();
@@ -39,11 +42,14 @@ class CWorkspace {
CAnimatedVariable<float> m_fAlpha;
bool m_bForceRendering = false;
// allows damage to propagate.
bool m_bVisible = false;
// "scratchpad"
bool m_bIsSpecialWorkspace = false;
// last window
CWindow* m_pLastFocusedWindow = nullptr;
PHLWINDOWREF m_pLastFocusedWindow;
// user-set
bool m_bDefaultFloating = false;
@@ -52,18 +58,36 @@ class CWorkspace {
// last monitor (used on reconnect)
std::string m_szLastMonitor = "";
// Whether the user configured command for on-created-empty has been executed, if any
bool m_bOnCreatedEmptyExecuted = false;
bool m_bPersistent = false;
// 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 int&);
CWindow* getLastFocusedWindow();
void rememberPrevWorkspace(const CWorkspace* prevWorkspace);
PHLWINDOW getLastFocusedWindow();
void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace);
std::string getConfigName();
bool matchesStaticSelector(const std::string& selector);
void markInert();
private:
void init(PHLWORKSPACE self);
std::shared_ptr<HOOK_CALLBACK_FN> m_pFocusedWindowHook;
bool m_bInert = true;
std::weak_ptr<CWorkspace> m_pSelf;
};
inline bool valid(const PHLWORKSPACE& ref) {
if (!ref)
return false;
return !ref->inert();
}

26
src/devices/IHID.hpp Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#include <string>
#include "../helpers/signal/Signal.hpp"
enum eHIDCapabilityType : uint32_t {
HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0),
HID_INPUT_CAPABILITY_POINTER = (1 << 1),
HID_INPUT_CAPABILITY_TOUCH = (1 << 2),
};
/*
Base class for a HID device.
This could be a keyboard, a mouse, or a touchscreen.
*/
class IHID {
public:
virtual uint32_t getCapabilities() = 0;
struct {
CSignal destroy;
} events;
std::string deviceName;
};

134
src/devices/IKeyboard.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include "IKeyboard.hpp"
#include "../defines.hpp"
#include "../helpers/VarList.hpp"
#include "../managers/input/InputManager.hpp"
uint32_t IKeyboard::getCapabilities() {
return HID_INPUT_CAPABILITY_KEYBOARD;
}
IKeyboard::~IKeyboard() {
events.destroy.emit();
if (!xkbTranslationState)
return;
xkb_state_unref(xkbTranslationState);
xkbTranslationState = nullptr;
}
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbTranslationState)
xkb_state_unref(xkbTranslationState);
if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbTranslationState = xkb_state_new(keymap);
return;
}
const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE)) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from an active index {}", (uintptr_t)this, i);
CVarList keyboardLayouts(currentRules.layout, 0, ',');
CVarList keyboardModels(currentRules.model, 0, ',');
CVarList keyboardVariants(currentRules.variant, 0, ',');
xkb_rule_names rules = {.rules = "", .model = "", .layout = "", .variant = "", .options = ""};
std::string layout, model, variant;
layout = keyboardLayouts[i % keyboardLayouts.size()];
model = keyboardModels[i % keyboardModels.size()];
variant = keyboardVariants[i % keyboardVariants.size()];
rules.layout = layout.c_str();
rules.model = model.c_str();
rules.variant = variant.c_str();
auto KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!KEYMAP) {
Debug::log(ERR, "updateXKBTranslationState: keymap failed 1, fallback without model/variant");
rules.model = "";
rules.variant = "";
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
if (!KEYMAP) {
Debug::log(ERR, "updateXKBTranslationState: keymap failed 2, fallback to us");
rules.layout = "us";
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
xkbTranslationState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);
return;
}
}
Debug::log(LOG, "Updating keyboard {:x}'s translation state from an unknown index", (uintptr_t)this);
xkb_rule_names rules = {
.rules = currentRules.rules.c_str(),
.model = currentRules.model.c_str(),
.layout = currentRules.layout.c_str(),
.variant = currentRules.variant.c_str(),
.options = currentRules.options.c_str(),
};
const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkbTranslationState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT);
}
std::string IKeyboard::getActiveLayout() {
const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE)) {
const auto LAYOUTNAME = xkb_keymap_layout_get_name(KEYMAP, i);
if (LAYOUTNAME)
return std::string(LAYOUTNAME);
return "error";
}
}
return "none";
}
void IKeyboard::updateLEDs() {
auto keyboard = wlr();
if (!keyboard || keyboard->xkb_state == nullptr)
return;
uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i]))
leds |= (1 << i);
}
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
return;
wlr_keyboard_led_update(wlr(), leds);
}

61
src/devices/IKeyboard.hpp Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/Vector2D.hpp"
#include <xkbcommon/xkbcommon.h>
struct wlr_keyboard;
class IKeyboard : public IHID {
public:
virtual ~IKeyboard();
virtual uint32_t getCapabilities();
virtual bool isVirtual() = 0;
virtual wlr_keyboard* wlr() = 0;
struct SKeyEvent {
uint32_t timeMs = 0;
uint32_t keycode = 0;
bool updateMods = false;
wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED;
};
struct {
CSignal key;
CSignal modifiers;
CSignal keymap;
CSignal repeatInfo;
} keyboardEvents;
struct SStringRuleNames {
std::string layout = "";
std::string model = "";
std::string variant = "";
std::string options = "";
std::string rules = "";
};
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout();
void updateLEDs();
bool active = false;
bool enabled = true;
xkb_layout_index_t activeLayout = 0;
xkb_state* xkbTranslationState = nullptr;
std::string hlName = "";
std::string xkbFilePath = "";
SStringRuleNames currentRules;
int repeatRate = 0;
int repeatDelay = 0;
int numlockOn = -1;
bool resolveBindsBySym = false;
WP<IKeyboard> self;
};

5
src/devices/IPointer.cpp Normal file
View File

@@ -0,0 +1,5 @@
#include "IPointer.hpp"
uint32_t IPointer::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER;
}

110
src/devices/IPointer.hpp Normal file
View File

@@ -0,0 +1,110 @@
#pragma once
#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/Vector2D.hpp"
struct wlr_pointer;
/*
Base class for a pointer.
*/
class IPointer : public IHID {
public:
virtual uint32_t getCapabilities();
virtual bool isVirtual() = 0;
virtual wlr_pointer* wlr() = 0;
struct SMotionEvent {
uint32_t timeMs = 0;
Vector2D delta, unaccel;
};
struct SMotionAbsoluteEvent {
uint32_t timeMs = 0;
Vector2D absolute; // 0.0 - 1.0
};
struct SButtonEvent {
uint32_t timeMs = 0;
uint32_t button = 0;
wl_pointer_button_state state = WL_POINTER_BUTTON_STATE_PRESSED;
};
struct SAxisEvent {
uint32_t timeMs = 0;
wl_pointer_axis_source source = WL_POINTER_AXIS_SOURCE_WHEEL;
wl_pointer_axis axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
wl_pointer_axis_relative_direction relativeDirection = WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL;
double delta = 0.0;
int32_t deltaDiscrete = 0;
};
struct SSwipeBeginEvent {
uint32_t timeMs = 0;
uint32_t fingers = 0;
};
struct SSwipeUpdateEvent {
uint32_t timeMs = 0;
uint32_t fingers = 0;
Vector2D delta;
};
struct SSwipeEndEvent {
uint32_t timeMs = 0;
bool cancelled = false;
};
struct SPinchBeginEvent {
uint32_t timeMs = 0;
uint32_t fingers = 0;
};
struct SPinchUpdateEvent {
uint32_t timeMs = 0;
uint32_t fingers = 0;
Vector2D delta;
double scale = 1.0, rotation = 0.0;
};
struct SPinchEndEvent {
uint32_t timeMs = 0;
bool cancelled = false;
};
struct SHoldBeginEvent {
uint32_t timeMs = 0;
uint32_t fingers = 0;
};
struct SHoldEndEvent {
uint32_t timeMs = 0;
bool cancelled = false;
};
struct {
CSignal motion;
CSignal motionAbsolute;
CSignal button;
CSignal axis;
CSignal frame;
CSignal swipeBegin;
CSignal swipeEnd;
CSignal swipeUpdate;
CSignal pinchBegin;
CSignal pinchEnd;
CSignal pinchUpdate;
CSignal holdBegin;
CSignal holdEnd;
} pointerEvents;
std::string hlName;
bool connected = false; // means connected to the cursor
WP<IPointer> self;
};

5
src/devices/ITouch.cpp Normal file
View File

@@ -0,0 +1,5 @@
#include "ITouch.hpp"
uint32_t ITouch::getCapabilities() {
return HID_INPUT_CAPABILITY_TOUCH;
}

50
src/devices/ITouch.hpp Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/Vector2D.hpp"
struct wlr_touch;
class ITouch : public IHID {
public:
virtual uint32_t getCapabilities();
virtual bool isVirtual() = 0;
virtual wlr_touch* wlr() = 0;
struct SDownEvent {
uint32_t timeMs = 0;
int32_t touchID = 0;
Vector2D pos;
};
struct SUpEvent {
uint32_t timeMs = 0;
int32_t touchID = 0;
};
struct SMotionEvent {
uint32_t timeMs = 0;
int32_t touchID = 0;
Vector2D pos;
};
struct SCancelEvent {
uint32_t timeMs = 0;
int32_t touchID = 0;
};
struct {
CSignal down;
CSignal up;
CSignal motion;
CSignal cancel;
CSignal frame;
} touchEvents;
std::string hlName = "";
std::string boundOutput = "";
WP<ITouch> self;
};

64
src/devices/Keyboard.cpp Normal file
View File

@@ -0,0 +1,64 @@
#include "Keyboard.hpp"
#include "../defines.hpp"
SP<CKeyboard> CKeyboard::create(wlr_keyboard* keeb) {
SP<CKeyboard> pKeeb = SP<CKeyboard>(new CKeyboard(keeb));
pKeeb->self = pKeeb;
return pKeeb;
}
bool CKeyboard::isVirtual() {
return false;
}
wlr_keyboard* CKeyboard::wlr() {
return keyboard;
}
CKeyboard::CKeyboard(wlr_keyboard* keeb) : keyboard(keeb) {
if (!keeb)
return;
// clang-format off
hyprListener_destroy.initCallback(&keeb->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
keyboard = nullptr;
events.destroy.emit();
}, this, "CKeyboard");
hyprListener_key.initCallback(&keeb->events.key, [this] (void* owner, void* data) {
auto E = (wlr_keyboard_key_event*)data;
keyboardEvents.key.emit(SKeyEvent{
.timeMs = E->time_msec,
.keycode = E->keycode,
.updateMods = E->update_state,
.state = E->state,
});
}, this, "CKeyboard");
hyprListener_keymap.initCallback(&keeb->events.keymap, [this] (void* owner, void* data) {
keyboardEvents.keymap.emit();
}, this, "CKeyboard");
hyprListener_modifiers.initCallback(&keeb->events.modifiers, [this] (void* owner, void* data) {
keyboardEvents.modifiers.emit();
}, this, "CKeyboard");
hyprListener_repeatInfo.initCallback(&keeb->events.repeat_info, [this] (void* owner, void* data) {
keyboardEvents.repeatInfo.emit();
}, this, "CKeyboard");
// clang-format on
deviceName = keeb->base.name ? keeb->base.name : "UNKNOWN";
}
void CKeyboard::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_key.removeCallback();
hyprListener_keymap.removeCallback();
hyprListener_repeatInfo.removeCallback();
hyprListener_modifiers.removeCallback();
}

24
src/devices/Keyboard.hpp Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include "IKeyboard.hpp"
class CKeyboard : public IKeyboard {
public:
static SP<CKeyboard> create(wlr_keyboard* keeb);
virtual bool isVirtual();
virtual wlr_keyboard* wlr();
private:
CKeyboard(wlr_keyboard* keeb);
wlr_keyboard* keyboard = nullptr;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(key);
DYNLISTENER(modifiers);
DYNLISTENER(keymap);
DYNLISTENER(repeatInfo);
};

169
src/devices/Mouse.cpp Normal file
View File

@@ -0,0 +1,169 @@
#include "Mouse.hpp"
#include "../defines.hpp"
SP<CMouse> CMouse::create(wlr_pointer* mouse) {
SP<CMouse> pMouse = SP<CMouse>(new CMouse(mouse));
pMouse->self = pMouse;
return pMouse;
}
CMouse::CMouse(wlr_pointer* mouse_) : mouse(mouse_) {
if (!mouse)
return;
// clang-format off
hyprListener_destroy.initCallback(&mouse->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
mouse = nullptr;
events.destroy.emit();
}, this, "CMouse");
hyprListener_motion.initCallback(&mouse->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_event*)data;
pointerEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.delta = {E->delta_x, E->delta_y},
.unaccel = {E->unaccel_dx, E->unaccel_dy},
});
}, this, "CMouse");
hyprListener_motionAbsolute.initCallback(&mouse->events.motion_absolute, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_absolute_event*)data;
pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{
.timeMs = E->time_msec,
.absolute = {E->x, E->y},
});
}, this, "CMouse");
hyprListener_button.initCallback(&mouse->events.button, [this] (void* owner, void* data) {
auto E = (wlr_pointer_button_event*)data;
pointerEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.state = (wl_pointer_button_state)E->state,
});
}, this, "CMouse");
hyprListener_axis.initCallback(&mouse->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_pointer_axis_event*)data;
pointerEvents.axis.emit(SAxisEvent{
.timeMs = E->time_msec,
.source = E->source,
.axis = E->orientation,
.relativeDirection = E->relative_direction,
.delta = E->delta,
.deltaDiscrete = E->delta_discrete,
});
}, this, "CMouse");
hyprListener_swipeBegin.initCallback(&mouse->events.swipe_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_begin_event*)data;
pointerEvents.swipeBegin.emit(SSwipeBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CMouse");
hyprListener_swipeEnd.initCallback(&mouse->events.swipe_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_end_event*)data;
pointerEvents.swipeEnd.emit(SSwipeEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CMouse");
hyprListener_swipeUpdate.initCallback(&mouse->events.swipe_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_update_event*)data;
pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
});
}, this, "CMouse");
hyprListener_pinchBegin.initCallback(&mouse->events.pinch_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_begin_event*)data;
pointerEvents.pinchBegin.emit(SPinchBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CMouse");
hyprListener_pinchEnd.initCallback(&mouse->events.pinch_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_end_event*)data;
pointerEvents.pinchEnd.emit(SPinchEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CMouse");
hyprListener_pinchUpdate.initCallback(&mouse->events.pinch_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_update_event*)data;
pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
.scale = E->scale,
.rotation = E->rotation,
});
}, this, "CMouse");
hyprListener_holdBegin.initCallback(&mouse->events.hold_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_begin_event*)data;
pointerEvents.holdBegin.emit(SHoldBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CMouse");
hyprListener_holdEnd.initCallback(&mouse->events.hold_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_end_event*)data;
pointerEvents.holdEnd.emit(SHoldEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CMouse");
// clang-format on
deviceName = mouse->base.name ? mouse->base.name : "UNKNOWN";
}
void CMouse::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_motionAbsolute.removeCallback();
hyprListener_button.removeCallback();
hyprListener_axis.removeCallback();
hyprListener_frame.removeCallback();
hyprListener_swipeBegin.removeCallback();
hyprListener_swipeEnd.removeCallback();
hyprListener_swipeUpdate.removeCallback();
hyprListener_pinchBegin.removeCallback();
hyprListener_pinchEnd.removeCallback();
hyprListener_pinchUpdate.removeCallback();
hyprListener_holdBegin.removeCallback();
hyprListener_holdEnd.removeCallback();
}
bool CMouse::isVirtual() {
return false;
}
wlr_pointer* CMouse::wlr() {
return mouse;
}

36
src/devices/Mouse.hpp Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include "IPointer.hpp"
class CMouse : public IPointer {
public:
static SP<CMouse> create(wlr_pointer* mouse);
virtual bool isVirtual();
virtual wlr_pointer* wlr();
private:
CMouse(wlr_pointer* mouse);
wlr_pointer* mouse = nullptr;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(motion);
DYNLISTENER(motionAbsolute);
DYNLISTENER(button);
DYNLISTENER(axis);
DYNLISTENER(frame);
DYNLISTENER(swipeBegin);
DYNLISTENER(swipeEnd);
DYNLISTENER(swipeUpdate);
DYNLISTENER(pinchBegin);
DYNLISTENER(pinchEnd);
DYNLISTENER(pinchUpdate);
DYNLISTENER(holdBegin);
DYNLISTENER(holdEnd);
};

View File

@@ -0,0 +1,85 @@
#include "TouchDevice.hpp"
#include "../defines.hpp"
SP<CTouchDevice> CTouchDevice::create(wlr_touch* touch) {
SP<CTouchDevice> pTouch = SP<CTouchDevice>(new CTouchDevice(touch));
pTouch->self = pTouch;
return pTouch;
}
CTouchDevice::CTouchDevice(wlr_touch* touch_) : touch(touch_) {
if (!touch)
return;
// clang-format off
hyprListener_destroy.initCallback(&touch->base.events.destroy, [this] (void* owner, void* data) {
events.destroy.emit();
disconnectCallbacks();
touch = nullptr;
}, this, "CTouchDevice");
hyprListener_down.initCallback(&touch->events.down, [this] (void* owner, void* data) {
auto E = (wlr_touch_down_event*)data;
touchEvents.down.emit(SDownEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id,
.pos = {E->x, E->y},
});
}, this, "CTouchDevice");
hyprListener_up.initCallback(&touch->events.up, [this] (void* owner, void* data) {
auto E = (wlr_touch_up_event*)data;
touchEvents.up.emit(SUpEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id
});
}, this, "CTouchDevice");
hyprListener_motion.initCallback(&touch->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_touch_motion_event*)data;
touchEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id,
.pos = {E->x, E->y},
});
}, this, "CTouchDevice");
hyprListener_cancel.initCallback(&touch->events.cancel, [this] (void* owner, void* data) {
auto E = (wlr_touch_cancel_event*)data;
touchEvents.cancel.emit(SCancelEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id
});
}, this, "CTouchDevice");
hyprListener_frame.initCallback(&touch->events.frame, [this] (void* owner, void* data) {
touchEvents.frame.emit();
}, this, "CTouchDevice");
// clang-format on
deviceName = touch->base.name ? touch->base.name : "UNKNOWN";
}
bool CTouchDevice::isVirtual() {
return false;
}
wlr_touch* CTouchDevice::wlr() {
return touch;
}
void CTouchDevice::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_down.removeCallback();
hyprListener_up.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_cancel.removeCallback();
hyprListener_frame.removeCallback();
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "ITouch.hpp"
class CTouchDevice : public ITouch {
public:
static SP<CTouchDevice> create(wlr_touch* touch);
virtual bool isVirtual();
virtual wlr_touch* wlr();
private:
CTouchDevice(wlr_touch* touch);
wlr_touch* touch = nullptr;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(down);
DYNLISTENER(up);
DYNLISTENER(motion);
DYNLISTENER(cancel);
DYNLISTENER(frame);
};

View File

@@ -0,0 +1,75 @@
#include "VirtualKeyboard.hpp"
#include "../defines.hpp"
#include "../protocols/VirtualKeyboard.hpp"
SP<CVirtualKeyboard> CVirtualKeyboard::create(SP<CVirtualKeyboardV1Resource> keeb) {
SP<CVirtualKeyboard> pKeeb = SP<CVirtualKeyboard>(new CVirtualKeyboard(keeb));
pKeeb->self = pKeeb;
return pKeeb;
}
CVirtualKeyboard::CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb_) : keyboard(keeb_) {
if (!keeb_)
return;
auto keeb = keeb_->wlr();
// clang-format off
hyprListener_destroy.initCallback(&keeb->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
keyboard.reset();
events.destroy.emit();
}, this, "CVirtualKeyboard");
hyprListener_key.initCallback(&keeb->events.key, [this] (void* owner, void* data) {
auto E = (wlr_keyboard_key_event*)data;
keyboardEvents.key.emit(SKeyEvent{
.timeMs = E->time_msec,
.keycode = E->keycode,
.updateMods = E->update_state,
.state = E->state,
});
}, this, "CVirtualKeyboard");
hyprListener_keymap.initCallback(&keeb->events.keymap, [this] (void* owner, void* data) {
keyboardEvents.keymap.emit();
}, this, "CVirtualKeyboard");
hyprListener_modifiers.initCallback(&keeb->events.modifiers, [this] (void* owner, void* data) {
keyboardEvents.modifiers.emit();
}, this, "CVirtualKeyboard");
hyprListener_repeatInfo.initCallback(&keeb->events.repeat_info, [this] (void* owner, void* data) {
keyboardEvents.repeatInfo.emit();
}, this, "CVirtualKeyboard");
// clang-format on
deviceName = keeb->base.name ? keeb->base.name : "UNKNOWN";
}
bool CVirtualKeyboard::isVirtual() {
return true;
}
wlr_keyboard* CVirtualKeyboard::wlr() {
if (keyboard.expired())
return nullptr;
return keyboard.lock()->wlr();
}
void CVirtualKeyboard::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_key.removeCallback();
hyprListener_keymap.removeCallback();
hyprListener_repeatInfo.removeCallback();
hyprListener_modifiers.removeCallback();
}
wl_client* CVirtualKeyboard::getClient() {
if (keyboard.expired())
return nullptr;
return keyboard.lock()->client();
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "IKeyboard.hpp"
class CVirtualKeyboardV1Resource;
class CVirtualKeyboard : public IKeyboard {
public:
static SP<CVirtualKeyboard> create(SP<CVirtualKeyboardV1Resource> keeb);
virtual bool isVirtual();
virtual wlr_keyboard* wlr();
wl_client* getClient();
private:
CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb);
WP<CVirtualKeyboardV1Resource> keyboard;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(key);
DYNLISTENER(modifiers);
DYNLISTENER(keymap);
DYNLISTENER(repeatInfo);
};

View File

@@ -0,0 +1,172 @@
#include "VirtualPointer.hpp"
#include "../protocols/VirtualPointer.hpp"
SP<CVirtualPointer> CVirtualPointer::create(SP<CVirtualPointerV1Resource> resource) {
SP<CVirtualPointer> pPointer = SP<CVirtualPointer>(new CVirtualPointer(resource));
pPointer->self = pPointer;
return pPointer;
}
CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : pointer(resource) {
if (!resource->good())
return;
auto mouse = resource->wlr();
// clang-format off
hyprListener_destroy.initCallback(&mouse->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
events.destroy.emit();
}, this, "CVirtualPointer");
hyprListener_motion.initCallback(&mouse->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_event*)data;
pointerEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.delta = {E->delta_x, E->delta_y},
.unaccel = {E->unaccel_dx, E->unaccel_dy},
});
}, this, "CVirtualPointer");
hyprListener_motionAbsolute.initCallback(&mouse->events.motion_absolute, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_absolute_event*)data;
pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{
.timeMs = E->time_msec,
.absolute = {E->x, E->y},
});
}, this, "CVirtualPointer");
hyprListener_button.initCallback(&mouse->events.button, [this] (void* owner, void* data) {
auto E = (wlr_pointer_button_event*)data;
pointerEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.state = (wl_pointer_button_state)E->state,
});
}, this, "CVirtualPointer");
hyprListener_axis.initCallback(&mouse->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_pointer_axis_event*)data;
pointerEvents.axis.emit(SAxisEvent{
.timeMs = E->time_msec,
.source = E->source,
.axis = E->orientation,
.relativeDirection = E->relative_direction,
.delta = E->delta,
.deltaDiscrete = E->delta_discrete,
});
}, this, "CVirtualPointer");
hyprListener_swipeBegin.initCallback(&mouse->events.swipe_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_begin_event*)data;
pointerEvents.swipeBegin.emit(SSwipeBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_swipeEnd.initCallback(&mouse->events.swipe_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_end_event*)data;
pointerEvents.swipeEnd.emit(SSwipeEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
hyprListener_swipeUpdate.initCallback(&mouse->events.swipe_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_update_event*)data;
pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
});
}, this, "CVirtualPointer");
hyprListener_pinchBegin.initCallback(&mouse->events.pinch_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_begin_event*)data;
pointerEvents.pinchBegin.emit(SPinchBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_pinchEnd.initCallback(&mouse->events.pinch_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_end_event*)data;
pointerEvents.pinchEnd.emit(SPinchEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
hyprListener_pinchUpdate.initCallback(&mouse->events.pinch_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_update_event*)data;
pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
.scale = E->scale,
.rotation = E->rotation,
});
}, this, "CVirtualPointer");
hyprListener_holdBegin.initCallback(&mouse->events.hold_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_begin_event*)data;
pointerEvents.holdBegin.emit(SHoldBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_holdEnd.initCallback(&mouse->events.hold_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_end_event*)data;
pointerEvents.holdEnd.emit(SHoldEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
// clang-format on
deviceName = mouse->base.name ? mouse->base.name : "UNKNOWN";
}
bool CVirtualPointer::isVirtual() {
return true;
}
void CVirtualPointer::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_motionAbsolute.removeCallback();
hyprListener_button.removeCallback();
hyprListener_axis.removeCallback();
hyprListener_frame.removeCallback();
hyprListener_swipeBegin.removeCallback();
hyprListener_swipeEnd.removeCallback();
hyprListener_swipeUpdate.removeCallback();
hyprListener_pinchBegin.removeCallback();
hyprListener_pinchEnd.removeCallback();
hyprListener_pinchUpdate.removeCallback();
hyprListener_holdBegin.removeCallback();
hyprListener_holdEnd.removeCallback();
}
wlr_pointer* CVirtualPointer::wlr() {
if (pointer.expired())
return nullptr;
return pointer.lock()->wlr();
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include "IPointer.hpp"
class CVirtualPointerV1Resource;
class CVirtualPointer : public IPointer {
public:
static SP<CVirtualPointer> create(SP<CVirtualPointerV1Resource> resource);
virtual bool isVirtual();
virtual wlr_pointer* wlr();
private:
CVirtualPointer(SP<CVirtualPointerV1Resource>);
WP<CVirtualPointerV1Resource> pointer;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(motion);
DYNLISTENER(motionAbsolute);
DYNLISTENER(button);
DYNLISTENER(axis);
DYNLISTENER(frame);
DYNLISTENER(swipeBegin);
DYNLISTENER(swipeEnd);
DYNLISTENER(swipeUpdate);
DYNLISTENER(pinchBegin);
DYNLISTENER(pinchEnd);
DYNLISTENER(pinchUpdate);
DYNLISTENER(holdBegin);
DYNLISTENER(holdEnd);
};

View File

@@ -4,6 +4,7 @@
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
#include "../protocols/PointerGestures.hpp"
// ---------------------------------------------------- //
// _____ ________ _______ _____ ______ _____ //
@@ -15,23 +16,6 @@
// //
// ---------------------------------------------------- //
void Events::listener_keyboardDestroy(void* owner, void* data) {
SKeyboard* PKEYBOARD = (SKeyboard*)owner;
g_pInputManager->destroyKeyboard(PKEYBOARD);
Debug::log(LOG, "Destroyed keyboard {:x}", (uintptr_t)PKEYBOARD);
}
void Events::listener_keyboardKey(void* owner, void* data) {
SKeyboard* PKEYBOARD = (SKeyboard*)owner;
g_pInputManager->onKeyboardKey((wlr_keyboard_key_event*)data, PKEYBOARD);
}
void Events::listener_keyboardMod(void* owner, void* data) {
SKeyboard* PKEYBOARD = (SKeyboard*)owner;
g_pInputManager->onKeyboardMod(data, PKEYBOARD);
}
void Events::listener_mouseFrame(wl_listener* listener, void* data) {
wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat);
}
@@ -92,35 +76,6 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
g_pInputManager->updateCapabilities();
}
void Events::listener_newConstraint(wl_listener* listener, void* data) {
const auto PCONSTRAINT = (wlr_pointer_constraint_v1*)data;
Debug::log(LOG, "New mouse constraint at {:x}", (uintptr_t)PCONSTRAINT);
const auto SURFACE = CWLSurface::surfaceFromWlr(PCONSTRAINT->surface);
if (!SURFACE) {
Debug::log(ERR, "Refusing a constraint from an unassigned wl_surface {:x}", (uintptr_t)PCONSTRAINT->surface);
return;
}
SURFACE->appendConstraint(PCONSTRAINT);
}
void Events::listener_newVirtPtr(wl_listener* listener, void* data) {
const auto EV = (wlr_virtual_pointer_v1_new_pointer_event*)data;
const auto POINTER = EV->new_pointer;
const auto DEVICE = &POINTER->pointer.base;
g_pInputManager->newMouse(DEVICE, true);
}
void Events::listener_destroyMouse(void* owner, void* data) {
const auto PMOUSE = (SMouse*)owner;
g_pInputManager->destroyMouse(PMOUSE->mouse);
}
void Events::listener_swipeBegin(wl_listener* listener, void* data) {
const auto EVENT = (wlr_pointer_swipe_begin_event*)data;
@@ -141,23 +96,17 @@ void Events::listener_swipeEnd(wl_listener* listener, void* data) {
void Events::listener_pinchBegin(wl_listener* listener, void* data) {
const auto EV = (wlr_pointer_pinch_begin_event*)data;
wlr_pointer_gestures_v1_send_pinch_begin(g_pCompositor->m_sWLRPointerGestures, g_pCompositor->m_sSeat.seat, EV->time_msec, EV->fingers);
PROTO::pointerGestures->pinchBegin(EV->time_msec, EV->fingers);
}
void Events::listener_pinchUpdate(wl_listener* listener, void* data) {
const auto EV = (wlr_pointer_pinch_update_event*)data;
wlr_pointer_gestures_v1_send_pinch_update(g_pCompositor->m_sWLRPointerGestures, g_pCompositor->m_sSeat.seat, EV->time_msec, EV->dx, EV->dy, EV->scale, EV->rotation);
PROTO::pointerGestures->pinchUpdate(EV->time_msec, {EV->dx, EV->dy}, EV->scale, EV->rotation);
}
void Events::listener_pinchEnd(wl_listener* listener, void* data) {
const auto EV = (wlr_pointer_pinch_end_event*)data;
wlr_pointer_gestures_v1_send_pinch_end(g_pCompositor->m_sWLRPointerGestures, g_pCompositor->m_sSeat.seat, EV->time_msec, EV->cancelled);
}
void Events::listener_newVirtualKeyboard(wl_listener* listener, void* data) {
const auto WLRKB = (wlr_virtual_keyboard_v1*)data;
g_pInputManager->newVirtualKeyboard(&WLRKB->keyboard.base);
PROTO::pointerGestures->pinchEnd(EV->time_msec, EV->cancelled);
}
void Events::listener_touchBegin(wl_listener* listener, void* data) {
@@ -177,9 +126,11 @@ void Events::listener_touchFrame(wl_listener* listener, void* data) {
}
void Events::listener_holdBegin(wl_listener* listener, void* data) {
g_pInputManager->onPointerHoldBegin((wlr_pointer_hold_begin_event*)data);
const auto EV = (wlr_pointer_hold_begin_event*)data;
PROTO::pointerGestures->holdBegin(EV->time_msec, EV->fingers);
}
void Events::listener_holdEnd(wl_listener* listener, void* data) {
g_pInputManager->onPointerHoldEnd((wlr_pointer_hold_end_event*)data);
const auto EV = (wlr_pointer_hold_end_event*)data;
PROTO::pointerGestures->holdEnd(EV->time_msec, EV->cancelled);
}

View File

@@ -17,14 +17,9 @@ namespace Events {
// Layer events
LISTENER(newLayerSurface);
DYNLISTENFUNC(destroyLayerSurface);
DYNLISTENFUNC(mapLayerSurface);
DYNLISTENFUNC(unmapLayerSurface);
DYNLISTENFUNC(commitLayerSurface);
// Surface XDG (window)
LISTENER(newXDGToplevel);
LISTENER(activateXDG);
// Window events
DYNLISTENFUNC(commitWindow);
@@ -59,23 +54,12 @@ namespace Events {
// Virt Ptr
LISTENER(newVirtPtr);
DYNLISTENFUNC(destroyMouse);
DYNLISTENFUNC(keyboardKey);
DYNLISTENFUNC(keyboardMod);
DYNLISTENFUNC(keyboardDestroy);
LISTENER(newConstraint);
// Various
LISTENER(requestMouse);
LISTENER(requestSetSel);
LISTENER(requestSetPrimarySel);
// outputMgr
LISTENER(outputMgrApply);
LISTENER(outputMgrTest);
// Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);
@@ -99,14 +83,9 @@ namespace Events {
DYNLISTENFUNC(destroyDragIcon);
DYNLISTENFUNC(commitDragIcon);
// Deco XDG
LISTENER(NewXDGDeco);
// Renderer destroy
LISTENER(RendererDestroy);
LISTENER(newIdleInhibitor);
// session
LISTENER(sessionActive);
@@ -118,14 +97,6 @@ namespace Events {
LISTENER(pinchUpdate);
LISTENER(pinchEnd);
// Power
LISTENER(powerMgrSetMode);
// IME
LISTENER(newIME);
LISTENER(newTextInput);
LISTENER(newVirtualKeyboard);
// Touch
LISTENER(touchBegin);
LISTENER(touchEnd);
@@ -137,16 +108,4 @@ namespace Events {
// Session Lock
LISTENER(newSessionLock);
// Gamma control
LISTENER(setGamma);
// Cursor shape
LISTENER(setCursorShape);
// Tearing hints
LISTENER(newTearingHint);
// Shortcut inhibitor
LISTENER(newShortcutInhibitor);
};

View File

@@ -1,364 +0,0 @@
#include "../Compositor.hpp"
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
#include "Events.hpp"
// --------------------------------------------- //
// _ __ ________ _____ _____ //
// | | /\\ \ / / ____| __ \ / ____| //
// | | / \\ \_/ /| |__ | |__) | (___ //
// | | / /\ \\ / | __| | _ / \___ \ //
// | |____ / ____ \| | | |____| | \ \ ____) | //
// |______/_/ \_\_| |______|_| \_\_____/ //
// //
// --------------------------------------------- //
void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
const auto WLRLAYERSURFACE = (wlr_layer_surface_v1*)data;
if (!WLRLAYERSURFACE->output) {
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
if (!PMONITOR) {
Debug::log(ERR, "No monitor at cursor on new layer without a monitor. Ignoring.");
wlr_layer_surface_v1_destroy(WLRLAYERSURFACE);
return;
}
Debug::log(LOG, "New LayerSurface has no preferred monitor. Assigning Monitor {}", PMONITOR->szName);
WLRLAYERSURFACE->output = PMONITOR->output;
}
auto PMONITOR = (CMonitor*)g_pCompositor->getMonitorFromOutput(WLRLAYERSURFACE->output);
if (!WLRLAYERSURFACE->output || !PMONITOR || PMONITOR->pMirrorOf) {
PMONITOR = g_pCompositor->m_vMonitors.front().get();
WLRLAYERSURFACE->output = PMONITOR->output; // TODO: current mon
}
SLayerSurface* layerSurface = PMONITOR->m_aLayerSurfaceLayers[WLRLAYERSURFACE->pending.layer].emplace_back(std::make_unique<SLayerSurface>()).get();
layerSurface->szNamespace = WLRLAYERSURFACE->_namespace;
layerSurface->hyprListener_commitLayerSurface.initCallback(&WLRLAYERSURFACE->surface->events.commit, &Events::listener_commitLayerSurface, layerSurface, "layerSurface");
layerSurface->hyprListener_destroyLayerSurface.initCallback(&WLRLAYERSURFACE->events.destroy, &Events::listener_destroyLayerSurface, layerSurface, "layerSurface");
layerSurface->hyprListener_mapLayerSurface.initCallback(&WLRLAYERSURFACE->surface->events.map, &Events::listener_mapLayerSurface, layerSurface, "layerSurface");
layerSurface->hyprListener_unmapLayerSurface.initCallback(&WLRLAYERSURFACE->surface->events.unmap, &Events::listener_unmapLayerSurface, layerSurface, "layerSurface");
layerSurface->layerSurface = WLRLAYERSURFACE;
layerSurface->layer = WLRLAYERSURFACE->current.layer;
WLRLAYERSURFACE->data = layerSurface;
layerSurface->monitorID = PMONITOR->ID;
layerSurface->popupHead = std::make_unique<CPopup>(layerSurface);
layerSurface->forceBlur = g_pConfigManager->shouldBlurLS(layerSurface->szNamespace);
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)layerSurface->layerSurface, layerSurface->layerSurface->_namespace,
(int)layerSurface->layer, PMONITOR->szName);
}
void Events::listener_destroyLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layersurface->layerSurface);
const auto PMONITOR = g_pCompositor->getMonitorFromID(layersurface->monitorID);
layersurface->popupHead.reset();
if (!g_pCompositor->getMonitorFromID(layersurface->monitorID))
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
if (!layersurface->fadingOut) {
if (layersurface->mapped) {
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
listener_unmapLayerSurface(layersurface, nullptr);
} else {
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
layersurface->alpha.setValueAndWarp(0.f);
layersurface->fadingOut = true;
g_pCompositor->addToFadingOutSafe(layersurface);
}
}
layersurface->noProcess = true;
layersurface->hyprListener_commitLayerSurface.removeCallback();
layersurface->hyprListener_destroyLayerSurface.removeCallback();
layersurface->hyprListener_mapLayerSurface.removeCallback();
layersurface->hyprListener_unmapLayerSurface.removeCallback();
// rearrange to fix the reserved areas
if (PMONITOR) {
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
PMONITOR->scheduledRecalc = true;
// and damage
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
}
layersurface->readyToDelete = true;
layersurface->layerSurface = nullptr;
}
void Events::listener_mapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layersurface->layerSurface);
layersurface->mapped = true;
layersurface->keyboardExclusive = layersurface->layerSurface->current.keyboard_interactive;
layersurface->surface = layersurface->layerSurface->surface;
// fix if it changed its mon
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layersurface->layerSurface->output);
if (!PMONITOR)
return;
layersurface->applyRules();
if ((uint64_t)layersurface->monitorID != PMONITOR->ID) {
const auto POLDMON = g_pCompositor->getMonitorFromID(layersurface->monitorID);
for (auto it = POLDMON->m_aLayerSurfaceLayers[layersurface->layer].begin(); it != POLDMON->m_aLayerSurfaceLayers[layersurface->layer].end(); it++) {
if (it->get() == layersurface) {
PMONITOR->m_aLayerSurfaceLayers[layersurface->layer].emplace_back(std::move(*it));
POLDMON->m_aLayerSurfaceLayers[layersurface->layer].erase(it);
break;
}
}
layersurface->monitorID = PMONITOR->ID;
PMONITOR->scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(POLDMON->ID);
}
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output);
if (layersurface->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(layersurface);
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained());
if (GRABSFOCUS) {
g_pInputManager->releaseAllMouseButtons();
g_pCompositor->focusSurface(layersurface->layerSurface->surface);
const auto LOCAL =
g_pInputManager->getMouseCoordsInternal() - Vector2D(layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layersurface->layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pInputManager->m_bEmptyFocusCursorSet = false;
}
layersurface->position = Vector2D(layersurface->geometry.x, layersurface->geometry.y);
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
layersurface->startAnimation(!(layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
layersurface->readyToDelete = false;
layersurface->fadingOut = false;
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("openLayer", layersurface);
g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
}
void Events::listener_unmapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layersurface->layerSurface);
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", layersurface);
std::erase(g_pInputManager->m_dExclusiveLSes, layersurface);
if (!g_pInputManager->m_dExclusiveLSes.empty())
g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface);
if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
g_pCompositor->addToFadingOutSafe(layersurface);
layersurface->mapped = false;
layersurface->startAnimation(false);
return;
}
// make a snapshot and start fade
g_pHyprOpenGL->makeLayerSnapshot(layersurface);
layersurface->startAnimation(false);
layersurface->mapped = false;
g_pCompositor->addToFadingOutSafe(layersurface);
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layersurface->layerSurface->output);
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == layersurface->layerSurface->surface;
layersurface->surface = nullptr;
if (!PMONITOR)
return;
// refocus if needed
if (WASLASTFOCUS) {
g_pInputManager->releaseAllMouseButtons();
Vector2D surfaceCoords;
SLayerSurface* pFoundLayerSurface = nullptr;
wlr_surface* foundSurface = nullptr;
g_pCompositor->m_pLastFocus = nullptr;
// find LS-es to focus
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&surfaceCoords, &pFoundLayerSurface);
if (!foundSurface && g_pCompositor->m_pLastWindow && g_pCompositor->isWorkspaceVisible(g_pCompositor->m_pLastWindow->m_iWorkspaceID)) {
// if there isn't any, focus the last window
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow;
g_pCompositor->focusWindow(nullptr);
g_pCompositor->focusWindow(PLASTWINDOW);
} else {
// otherwise, full refocus
g_pInputManager->refocus();
}
}
CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
geomFixed = {layersurface->geometry.x + (int)PMONITOR->vecPosition.x, layersurface->geometry.y + (int)PMONITOR->vecPosition.y,
(int)layersurface->layerSurface->surface->current.width, (int)layersurface->layerSurface->surface->current.height};
g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused();
}
void Events::listener_commitLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
if (!layersurface->layerSurface || !layersurface->layerSurface->output)
return;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layersurface->layerSurface->output);
if (!PMONITOR)
return;
if (layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
CBox geomFixed = {layersurface->geometry.x, layersurface->geometry.y, layersurface->geometry.width, layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
// fix if it changed its mon
if ((uint64_t)layersurface->monitorID != PMONITOR->ID) {
const auto POLDMON = g_pCompositor->getMonitorFromID(layersurface->monitorID);
for (auto it = POLDMON->m_aLayerSurfaceLayers[layersurface->layer].begin(); it != POLDMON->m_aLayerSurfaceLayers[layersurface->layer].end(); it++) {
if (it->get() == layersurface) {
PMONITOR->m_aLayerSurfaceLayers[layersurface->layer].emplace_back(std::move(*it));
POLDMON->m_aLayerSurfaceLayers[layersurface->layer].erase(it);
break;
}
}
layersurface->monitorID = PMONITOR->ID;
PMONITOR->scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(POLDMON->ID);
}
if (layersurface->layerSurface->current.committed != 0) {
if (layersurface->layer != layersurface->layerSurface->current.layer) {
for (auto it = PMONITOR->m_aLayerSurfaceLayers[layersurface->layer].begin(); it != PMONITOR->m_aLayerSurfaceLayers[layersurface->layer].end(); it++) {
if (it->get() == layersurface) {
PMONITOR->m_aLayerSurfaceLayers[layersurface->layerSurface->current.layer].emplace_back(std::move(*it));
PMONITOR->m_aLayerSurfaceLayers[layersurface->layer].erase(it);
break;
}
}
layersurface->layer = layersurface->layerSurface->current.layer;
if (layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
}
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
PMONITOR->scheduledRecalc = true;
} else {
layersurface->position = Vector2D(layersurface->geometry.x, layersurface->geometry.y);
// update geom if it changed
if (layersurface->layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layersurface->layerSurface->surface->current.viewport.has_dst) {
// fractional scaling. Dirty hack.
layersurface->geometry = {layersurface->geometry.x, layersurface->geometry.y, (int)(layersurface->layerSurface->surface->current.viewport.dst_width),
(int)(layersurface->layerSurface->surface->current.viewport.dst_height)};
} else {
// this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly.
layersurface->geometry = {layersurface->geometry.x, layersurface->geometry.y, (int)layersurface->layerSurface->surface->current.width,
(int)layersurface->layerSurface->surface->current.height};
}
}
if (layersurface->realPosition.goal() != layersurface->geometry.pos()) {
if (layersurface->realPosition.isBeingAnimated())
layersurface->realPosition = layersurface->geometry.pos();
else
layersurface->realPosition.setValueAndWarp(layersurface->geometry.pos());
}
if (layersurface->realSize.goal() != layersurface->geometry.size()) {
if (layersurface->realSize.isBeingAnimated())
layersurface->realSize = layersurface->geometry.size();
else
layersurface->realSize.setValueAndWarp(layersurface->geometry.size());
}
if (layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !layersurface->keyboardExclusive && layersurface->mapped) {
g_pCompositor->focusSurface(layersurface->layerSurface->surface);
const auto LOCAL =
g_pInputManager->getMouseCoordsInternal() - Vector2D(layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layersurface->layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pInputManager->m_bEmptyFocusCursorSet = false;
} else if (!layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) &&
layersurface->keyboardExclusive) {
g_pInputManager->refocus();
}
layersurface->keyboardExclusive = layersurface->layerSurface->current.keyboard_interactive;
g_pHyprRenderer->damageSurface(layersurface->layerSurface->surface, layersurface->position.x, layersurface->position.y);
g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
}

View File

@@ -16,16 +16,6 @@
// //
// ------------------------------ //
void Events::listener_outputMgrApply(wl_listener* listener, void* data) {
const auto CONFIG = (wlr_output_configuration_v1*)data;
g_pHyprRenderer->outputMgrApplyTest(CONFIG, false);
}
void Events::listener_outputMgrTest(wl_listener* listener, void* data) {
const auto CONFIG = (wlr_output_configuration_v1*)data;
g_pHyprRenderer->outputMgrApplyTest(CONFIG, true);
}
void Events::listener_leaseRequest(wl_listener* listener, void* data) {
const auto REQUEST = (wlr_drm_lease_request_v1*)data;
struct wlr_drm_lease_v1* lease = wlr_drm_lease_request_v1_grant(REQUEST);
@@ -141,7 +131,8 @@ void Events::listener_destroyDrag(void* owner, void* data) {
g_pInputManager->m_sDrag.dragIcon = nullptr;
g_pInputManager->m_sDrag.hyprListener_destroy.removeCallback();
g_pCompositor->focusWindow(g_pCompositor->m_pLastWindow, g_pCompositor->m_pLastWindow ? g_pXWaylandManager->getWindowSurface(g_pCompositor->m_pLastWindow) : nullptr);
g_pCompositor->focusWindow(g_pCompositor->m_pLastWindow.lock(),
g_pCompositor->m_pLastWindow.lock() ? g_pXWaylandManager->getWindowSurface(g_pCompositor->m_pLastWindow.lock()) : nullptr);
}
void Events::listener_mapDragIcon(void* owner, void* data) {
@@ -186,109 +177,3 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
g_pConfigManager->m_bWantsMonitorReload = true;
}
void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) {
Debug::log(LOG, "PowerMgr set mode!");
const auto EVENT = (wlr_output_power_v1_set_mode_event*)data;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(EVENT->output);
if (!PMONITOR) {
Debug::log(ERR, "Invalid powerMgrSetMode output");
return;
}
wlr_output_state_set_enabled(PMONITOR->state.wlr(), EVENT->mode == 1);
if (!PMONITOR->state.commit())
Debug::log(ERR, "Couldn't set power mode");
}
void Events::listener_newIME(wl_listener* listener, void* data) {
Debug::log(LOG, "New IME added!");
g_pInputManager->m_sIMERelay.onNewIME((wlr_input_method_v2*)data);
}
void Events::listener_newTextInput(wl_listener* listener, void* data) {
Debug::log(LOG, "New TextInput added!");
g_pInputManager->m_sIMERelay.onNewTextInput((wlr_text_input_v3*)data);
}
void Events::listener_newSessionLock(wl_listener* listener, void* data) {
Debug::log(LOG, "New session lock!");
g_pSessionLockManager->onNewSessionLock((wlr_session_lock_v1*)data);
}
void Events::listener_setGamma(wl_listener* listener, void* data) {
Debug::log(LOG, "New Gamma event at {:x}", (uintptr_t)data);
const auto E = (wlr_gamma_control_manager_v1_set_gamma_event*)data;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(E->output);
if (!PMONITOR) {
Debug::log(ERR, "Gamma event object references non-existent output {:x} ?", (uintptr_t)E->output);
return;
}
PMONITOR->gammaChanged = true;
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
}
void Events::listener_setCursorShape(wl_listener* listener, void* data) {
const auto E = (wlr_cursor_shape_manager_v1_request_set_shape_event*)data;
g_pInputManager->processMouseRequest(E);
}
void Events::listener_newTearingHint(wl_listener* listener, void* data) {
Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data);
const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get();
NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data;
NEWCTRL->hyprListener_destroy.initCallback(
&NEWCTRL->pWlrHint->events.destroy,
[&](void* owner, void* data) {
Debug::log(LOG, "Destroyed {:x} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint);
std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; });
},
NEWCTRL, "TearingController");
NEWCTRL->hyprListener_set.initCallback(
&NEWCTRL->pWlrHint->events.set_hint,
[&](void* owner, void* data) {
const auto TEARINGHINT = (STearingController*)owner;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface);
if (PWINDOW) {
PWINDOW->m_bTearingHint = (bool)TEARINGHINT->pWlrHint->current;
Debug::log(LOG, "Hint {:x} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current);
}
},
NEWCTRL, "TearingController");
}
void Events::listener_newShortcutInhibitor(wl_listener* listener, void* data) {
const auto INHIBITOR = (wlr_keyboard_shortcuts_inhibitor_v1*)data;
const auto PINH = &g_pKeybindManager->m_lShortcutInhibitors.emplace_back();
PINH->hyprListener_destroy.initCallback(
&INHIBITOR->events.destroy,
[](void* owner, void* data) {
const auto OWNER = (SShortcutInhibitor*)owner;
g_pKeybindManager->m_lShortcutInhibitors.remove(*OWNER);
},
PINH, "ShortcutInhibitor");
PINH->pWlrInhibitor = INHIBITOR;
Debug::log(LOG, "New shortcut inhibitor for surface {:x}", (uintptr_t)INHIBITOR->surface);
}

View File

@@ -16,57 +16,45 @@
// //
// --------------------------------------------------------- //
void Events::listener_change(wl_listener* listener, void* data) {
// layout got changed, let's update monitors.
const auto CONFIG = wlr_output_configuration_v1_create();
static void checkDefaultCursorWarp(std::shared_ptr<CMonitor>* PNEWMONITORWRAP, std::string monitorName) {
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
if (!CONFIG)
static auto PCURSORMONITOR = CConfigValue<std::string>("general:default_cursor_monitor");
static auto firstMonitorAdded = std::chrono::system_clock::now();
static bool cursorDefaultDone = false;
static bool firstLaunch = true;
const auto POS = PNEWMONITOR->middle();
// by default, cursor should be set to first monitor detected
// this is needed as a default if the monitor given in config above doesn't exist
if (firstLaunch) {
firstLaunch = false;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
if (cursorDefaultDone || *PCURSORMONITOR == STRVAL_EMPTY)
return;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output)
continue;
if (g_pCompositor->m_pUnsafeOutput == m.get())
continue;
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
CBox BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, BOX.pWlr());
BOX.applyFromWlr();
//m->vecSize.x = BOX.width;
// m->vecSize.y = BOX.height;
m->vecPosition.x = BOX.x;
m->vecPosition.y = BOX.y;
CONFIGHEAD->state.enabled = m->output->enabled;
CONFIGHEAD->state.mode = m->output->current_mode;
if (!m->output->current_mode) {
CONFIGHEAD->state.custom_mode = {
m->output->width,
m->output->height,
m->output->refresh,
};
}
CONFIGHEAD->state.x = m->vecPosition.x;
CONFIGHEAD->state.y = m->vecPosition.y;
CONFIGHEAD->state.transform = m->transform;
CONFIGHEAD->state.scale = m->scale;
CONFIGHEAD->state.adaptive_sync_enabled = m->vrrActive;
// after 10s, don't set cursor to default monitor
auto timePassedSec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - firstMonitorAdded);
if (timePassedSec.count() > 10) {
cursorDefaultDone = true;
return;
}
wlr_output_manager_v1_set_configuration(g_pCompositor->m_sWLROutputMgr, CONFIG);
if (*PCURSORMONITOR == monitorName) {
cursorDefaultDone = true;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
}
void Events::listener_newOutput(wl_listener* listener, void* data) {
// new monitor added, let's accommodate for that.
const auto OUTPUT = (wlr_output*)data;
// for warping the cursor on launch
static bool firstLaunch = true;
if (!OUTPUT->name) {
Debug::log(ERR, "New monitor has no name?? Ignoring");
return;
@@ -101,12 +89,8 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
g_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR);
if (firstLaunch) {
firstLaunch = false;
const auto POS = PNEWMONITOR->middle();
if (g_pCompositor->m_sSeat.mouse)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y);
} else {
checkDefaultCursorWarp(PNEWMONITORWRAP, OUTPUT->name);
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
@@ -114,7 +98,6 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
}
}
}
}
void Events::listener_monitorFrame(void* owner, void* data) {
if (g_pCompositor->m_bExitTriggered) {
@@ -146,7 +129,7 @@ void Events::listener_monitorFrame(void* owner, void* data) {
PMONITOR->tearingState.busy = false;
if (PMONITOR->tearingState.activelyTearing && PMONITOR->solitaryClient /* can be invalidated by a recheck */) {
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?

View File

@@ -3,6 +3,7 @@
#include "../Compositor.hpp"
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/TokenManager.hpp"
#include "../render/Renderer.hpp"
#include "../config/ConfigValue.hpp"
@@ -42,7 +43,7 @@ void setAnimToMove(void* data) {
}
void Events::listener_mapWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
@@ -51,21 +52,59 @@ void Events::listener_mapWindow(void* owner, void* data) {
static auto PSWALLOWREGEX = CConfigValue<std::string>("misc:swallow_regex");
static auto PSWALLOWEXREGEX = CConfigValue<std::string>("misc:swallow_exception_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;
if (!g_pCompositor->m_pLastMonitor) {
g_pCompositor->setActiveMonitor(g_pCompositor->getMonitorFromVector({}));
PMONITOR = g_pCompositor->m_pLastMonitor;
}
auto PWORKSPACE = PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
auto PWORKSPACE = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace;
PWINDOW->m_iMonitorID = PMONITOR->ID;
PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
PWINDOW->m_pWorkspace = PWORKSPACE;
PWINDOW->m_bIsMapped = true;
PWINDOW->m_bReadyToDelete = false;
PWINDOW->m_bFadingOut = false;
PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW);
PWINDOW->m_iX11Type = PWINDOW->m_bIsX11 ? (PWINDOW->m_uSurface.xwayland->override_redirect ? 2 : 1) : 1;
PWINDOW->m_bFirstMap = true;
PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle;
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
// check for token
std::string requestedWorkspace = "";
bool workspaceSilent = false;
if (*PINITIALWSTRACKING) {
const auto WINDOWENV = PWINDOW->getEnv();
if (WINDOWENV.contains("HL_INITIAL_WORKSPACE_TOKEN")) {
const auto SZTOKEN = WINDOWENV.at("HL_INITIAL_WORKSPACE_TOKEN");
Debug::log(LOG, "New window contains HL_INITIAL_WORKSPACE_TOKEN: {}", SZTOKEN);
const auto TOKEN = g_pTokenManager->getToken(SZTOKEN);
if (TOKEN) {
// find workspace and use it
SInitialWorkspaceToken WS = std::any_cast<SInitialWorkspaceToken>(TOKEN->data);
Debug::log(LOG, "HL_INITIAL_WORKSPACE_TOKEN {} -> {}", SZTOKEN, WS.workspace);
if (g_pCompositor->getWorkspaceByString(WS.workspace) != PWINDOW->m_pWorkspace) {
requestedWorkspace = WS.workspace;
workspaceSilent = true;
}
if (*PINITIALWSTRACKING == 1) // one-shot token
g_pTokenManager->removeToken(TOKEN);
else if (*PINITIALWSTRACKING == 2) { // persistent
if (WS.primaryOwner.expired()) {
WS.primaryOwner = PWINDOW;
TOKEN->data = WS;
}
PWINDOW->m_szInitialWorkspaceToken = SZTOKEN;
}
}
}
}
if (g_pInputManager->m_bLastFocusOnLS) // waybar fix
g_pInputManager->releaseAllMouseButtons();
@@ -73,9 +112,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
// Set all windows tiled regardless of anything
g_pXWaylandManager->setWindowStyleTiled(PWINDOW, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM);
// Foreign Toplevel
PWINDOW->createToplevelHandle();
// checks if the window wants borders and sets the appropriate flag
g_pXWaylandManager->checkBorders(PWINDOW);
@@ -108,9 +144,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
// window rules
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(PWINDOW, false);
std::string requestedWorkspace = "";
bool workspaceSilent = false;
PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen ||
(!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && PWINDOW->m_uSurface.xdg->toplevel->requested.fullscreen) ||
(PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen);
@@ -119,10 +153,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
bool overridingNoFullscreen = false;
bool overridingNoMaximize = false;
PWINDOW->m_szInitialTitle = g_pXWaylandManager->getTitle(PWINDOW);
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
for (auto& r : WINDOWRULES) {
for (auto& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) {
try {
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' ')));
@@ -153,7 +184,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->m_iMonitorID));
PMONITOR = PMONITORFROMID;
}
PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
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()); }
@@ -287,16 +318,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWORKSPACE = pWorkspace;
PWINDOW->m_iWorkspaceID = pWorkspace->m_iID;
PWINDOW->m_pWorkspace = pWorkspace;
PWINDOW->m_iMonitorID = pWorkspace->m_iMonitorID;
if (g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->specialWorkspaceID && !pWorkspace->m_bIsSpecialWorkspace)
if (g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeSpecialWorkspace && !pWorkspace->m_bIsSpecialWorkspace)
workspaceSilent = true;
if (!workspaceSilent) {
if (pWorkspace->m_bIsSpecialWorkspace)
g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID)->setSpecialWorkspace(pWorkspace);
else if (PMONITOR->activeWorkspace != REQUESTEDWORKSPACEID)
else if (PMONITOR->activeWorkspaceID() != REQUESTEDWORKSPACEID)
g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspaceName);
PMONITOR = g_pCompositor->m_pLastMonitor;
@@ -312,7 +343,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bCreatedOverFullscreen = true;
// size and move rules
for (auto& r : WINDOWRULES) {
for (auto& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("size")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
@@ -356,10 +387,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
int posY = 0;
if (POSXSTR.starts_with("100%-")) {
const auto POSXRAW = POSXSTR.substr(5);
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) {
@@ -375,10 +410,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (POSYSTR.starts_with("100%-")) {
const auto POSYRAW = POSYSTR.substr(5);
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 (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
@@ -431,7 +470,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal() - Vector2D(10, 10);
}
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow;
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow.lock();
if (PWINDOW->m_sAdditionalConfigData.forceAllowsInput) {
PWINDOW->m_sAdditionalConfigData.noFocus = false;
@@ -468,28 +507,30 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW,
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XDG Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_minimize, &Events::listener_requestMinimize, PWINDOW,
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_requestMove.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_move, &Events::listener_requestMove, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
PWINDOW->hyprListener_requestMove.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_move, &Events::listener_requestMove, PWINDOW.get(), "XDG Window Late");
PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW.get(), "XDG Window Late");
} else {
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(),
"XWayland Window Late");
PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW, "XWayland Window Late");
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XWayland Window Late");
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_minimize, &Events::listener_requestMinimize, PWINDOW,
PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW.get(),
"XWayland Window Late");
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XWayland Window Late");
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(),
"Xwayland Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_maximize, &Events::listener_requestMaximize, PWINDOW,
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(),
"Xwayland Window Late");
if (PWINDOW->m_iX11Type == 2)
PWINDOW->hyprListener_setGeometryX11U.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_geometry, &Events::listener_unmanagedSetGeometry, PWINDOW,
PWINDOW->hyprListener_setGeometryX11U.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_geometry, &Events::listener_unmanagedSetGeometry, PWINDOW.get(),
"XWayland Window Late");
}
@@ -519,7 +560,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->updateToplevel();
if (workspaceSilent) {
if (g_pCompositor->windowValidMapped(PFOCUSEDWINDOWPREV)) {
if (validMapped(PFOCUSEDWINDOWPREV)) {
g_pCompositor->focusWindow(PFOCUSEDWINDOWPREV);
PFOCUSEDWINDOWPREV->updateWindowDecos(); // need to for some reason i cba to find out why
} else if (!PFOCUSEDWINDOWPREV)
@@ -548,14 +589,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (ppid) {
// get window by pid
std::vector<CWindow*> found;
CWindow* finalFound = nullptr;
std::vector<PHLWINDOW> found;
PHLWINDOW finalFound;
for (auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->isHidden())
continue;
if (w->getPID() == ppid) {
found.push_back(w.get());
found.push_back(w);
}
}
@@ -626,20 +667,20 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform);
if (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained())
if (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained())
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize;
g_pCompositor->updateWorkspaceWindows(PWINDOW->m_iWorkspaceID);
g_pCompositor->updateWorkspaceWindows(PWINDOW->workspaceID());
if (PMONITOR && PWINDOW->m_iX11Type == 2)
PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale;
}
void Events::listener_unmapWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
Debug::log(LOG, "{:c} unmapped", PWINDOW);
@@ -687,24 +728,24 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
// swallowing
if (PWINDOW->m_pSwallowed && g_pCompositor->windowExists(PWINDOW->m_pSwallowed)) {
PWINDOW->m_pSwallowed->setHidden(false);
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed);
PWINDOW->m_pSwallowed = nullptr;
if (valid(PWINDOW->m_pSwallowed)) {
PWINDOW->m_pSwallowed.lock()->setHidden(false);
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed.lock());
PWINDOW->m_pSwallowed.reset();
}
bool wasLastWindow = false;
if (PWINDOW == g_pCompositor->m_pLastWindow) {
if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) {
wasLastWindow = true;
g_pCompositor->m_pLastWindow = nullptr;
g_pCompositor->m_pLastWindow.reset();
g_pCompositor->m_pLastFocus = nullptr;
g_pInputManager->releaseAllMouseButtons();
}
// remove the fullscreen window status from workspace if we closed it
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->m_bIsFullscreen)
PWORKSPACE->m_bHasFullscreenWindow = false;
@@ -720,19 +761,19 @@ void Events::listener_unmapWindow(void* owner, void* data) {
Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE);
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow && PWINDOWCANDIDATE)
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow.lock() && PWINDOWCANDIDATE)
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
if (!PWINDOWCANDIDATE && g_pCompositor->getWindowsOnWorkspace(PWINDOW->m_iWorkspaceID) == 0)
if (!PWINDOWCANDIDATE && g_pCompositor->getWindowsOnWorkspace(PWINDOW->workspaceID()) == 0)
g_pInputManager->refocus();
g_pInputManager->sendMotionEventsToFocused();
// CWindow::onUnmap will remove this window's active status, but we can't really do it above.
if (PWINDOW == g_pCompositor->m_pLastWindow || !g_pCompositor->m_pLastWindow) {
if (PWINDOW == g_pCompositor->m_pLastWindow.lock() || !g_pCompositor->m_pLastWindow.lock()) {
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
EMIT_HOOK_EVENT("activeWindow", (CWindow*)nullptr);
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
EMIT_HOOK_EVENT("activeWindow", (PHLWINDOW) nullptr);
}
} else {
Debug::log(LOG, "Unmapped was not focused, ignoring a refocus.");
@@ -751,21 +792,18 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, true);
PWINDOW->m_fAlpha = 0.f;
// Destroy Foreign Toplevel
PWINDOW->destroyToplevelHandle();
// recheck idle inhibitors
g_pInputManager->recheckIdleInhibitorStatus();
// force report all sizes (QT sometimes has an issue with this)
g_pCompositor->forceReportSizesToWindowsOnWorkspace(PWINDOW->m_iWorkspaceID);
g_pCompositor->forceReportSizesToWindowsOnWorkspace(PWINDOW->workspaceID());
// update lastwindow after focus
PWINDOW->onUnmap();
}
void Events::listener_ackConfigure(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
const auto E = (wlr_xdg_surface_configure*)data;
// find last matching serial
@@ -779,7 +817,7 @@ void Events::listener_ackConfigure(void* owner, void* data) {
}
void Events::listener_commitWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->initial_commit) {
Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(PWINDOW);
@@ -800,39 +838,11 @@ void Events::listener_commitWindow(void* owner, void* data) {
PWINDOW->m_pPendingSizeAck.reset();
}
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
if (!PWINDOW->m_bIsX11) {
PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces();
PWINDOW->m_pPopupHead->recheckTree();
}
// 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 == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear &&
PWINDOW->m_pWLSurface.wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
CRegion damageBox{&PWINDOW->m_pWLSurface.wlr()->buffer_damage};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {
PMONITOR->tearingState.frameScheduledWhileBusy = true;
} else {
PMONITOR->tearingState.nextRenderTorn = true;
g_pHyprRenderer->renderMonitor(PMONITOR);
}
}
}
if (PWINDOW->m_bIsX11 || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
return;
if (!PWINDOW->m_bIsX11 && !PWINDOW->m_bIsFullscreen && PWINDOW->m_bIsFloating) {
const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height};
const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
if (MAXSIZE < Vector2D{1, 1})
return;
if (MAXSIZE > Vector2D{1, 1}) {
const auto REALSIZE = PWINDOW->m_vRealSize.goal();
Vector2D newSize = REALSIZE;
@@ -852,17 +862,46 @@ void Events::listener_commitWindow(void* owner, void* data) {
g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true);
g_pHyprRenderer->damageWindow(PWINDOW);
}
}
if (!PWINDOW->m_pWorkspace->m_bVisible)
return;
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
if (!PWINDOW->m_bIsX11) {
PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces();
PWINDOW->m_pPopupHead->recheckTree();
}
// 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.wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
CRegion damageBox{&PWINDOW->m_pWLSurface.wlr()->buffer_damage};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {
PMONITOR->tearingState.frameScheduledWhileBusy = true;
} else {
PMONITOR->tearingState.nextRenderTorn = true;
g_pHyprRenderer->renderMonitor(PMONITOR);
}
}
}
}
void Events::listener_destroyWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
Debug::log(LOG, "{:c} destroyed, queueing.", PWINDOW);
if (PWINDOW->m_bIsX11)
Debug::log(LOG, "XWayland class raw: {}", PWINDOW->m_uSurface.xwayland->_class ? PWINDOW->m_uSurface.xwayland->_class : "null");
if (PWINDOW == g_pCompositor->m_pLastWindow) {
g_pCompositor->m_pLastWindow = nullptr;
if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) {
g_pCompositor->m_pLastWindow.reset();
g_pCompositor->m_pLastFocus = nullptr;
}
@@ -890,9 +929,9 @@ void Events::listener_destroyWindow(void* owner, void* data) {
}
void Events::listener_setTitleWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!g_pCompositor->windowValidMapped(PWINDOW))
if (!validMapped(PWINDOW))
return;
const auto NEWTITLE = g_pXWaylandManager->getTitle(PWINDOW);
@@ -901,12 +940,12 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
return;
PWINDOW->m_szTitle = NEWTITLE;
g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW.get())});
EMIT_HOOK_EVENT("windowTitle", PWINDOW);
if (PWINDOW == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others
if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { // if it's the active, let's post an event to update others
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(PWINDOW) + "," + PWINDOW->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW.get())});
EMIT_HOOK_EVENT("activeWindow", PWINDOW);
}
@@ -918,7 +957,7 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
}
void Events::listener_fullscreenWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!PWINDOW->m_bIsMapped) {
PWINDOW->m_bWantsInitialFullscreen = true;
@@ -934,7 +973,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
const auto REQUESTED = &PWINDOW->m_uSurface.xdg->toplevel->requested;
if (REQUESTED->fullscreen && PWINDOW->m_bIsFullscreen) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
if (PWORKSPACE->m_efFullscreenMode != FULLSCREEN_FULL) {
// Store that we were maximized
PWINDOW->m_bWasMaximized = true;
@@ -976,40 +1015,8 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
Debug::log(LOG, "{} fullscreen to {}", PWINDOW, PWINDOW->m_bIsFullscreen);
}
void Events::listener_activateXDG(wl_listener* listener, void* data) {
const auto E = (wlr_xdg_activation_v1_request_activate_event*)data;
static auto PFOCUSONACTIVATE = CConfigValue<Hyprlang::INT>("misc:focus_on_activate");
Debug::log(LOG, "Activate request for surface at {:x}", (uintptr_t)E->surface);
if (!wlr_xdg_surface_try_from_wlr_surface(E->surface))
return;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(E->surface);
if (!PWINDOW || PWINDOW == g_pCompositor->m_pLastWindow || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE))
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
PWINDOW->m_bIsUrgent = true;
if (!*PFOCUSONACTIVATE || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY))
return;
if (PWINDOW->m_bIsFloating)
g_pCompositor->changeWindowZOrder(PWINDOW, true);
g_pCompositor->focusWindow(PWINDOW);
g_pCompositor->warpCursorTo(PWINDOW->middle());
}
void Events::listener_activateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
static auto PFOCUSONACTIVATE = CConfigValue<Hyprlang::INT>("misc:focus_on_activate");
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
Debug::log(LOG, "X11 Activate request for window {}", PWINDOW);
@@ -1017,7 +1024,7 @@ void Events::listener_activateX11(void* owner, void* data) {
Debug::log(LOG, "Unmanaged X11 {} requests activate", PWINDOW);
if (g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID())
if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock()->getPID() != PWINDOW->getPID())
return;
if (!wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))
@@ -1027,24 +1034,14 @@ void Events::listener_activateX11(void* owner, void* data) {
return;
}
if (PWINDOW == g_pCompositor->m_pLastWindow || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE))
if (PWINDOW == g_pCompositor->m_pLastWindow.lock() || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE))
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
if (!*PFOCUSONACTIVATE || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY))
return;
if (PWINDOW->m_bIsFloating)
g_pCompositor->changeWindowZOrder(PWINDOW, true);
g_pCompositor->focusWindow(PWINDOW);
g_pCompositor->warpCursorTo(PWINDOW->middle());
PWINDOW->activate();
}
void Events::listener_configureX11(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
const auto E = (wlr_xwayland_surface_configure_event*)data;
@@ -1059,7 +1056,7 @@ void Events::listener_configureX11(void* owner, void* data) {
g_pHyprRenderer->damageWindow(PWINDOW);
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow == PWINDOW) {
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow.lock() == PWINDOW) {
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true);
g_pInputManager->refocus();
g_pHyprRenderer->damageWindow(PWINDOW);
@@ -1094,10 +1091,10 @@ void Events::listener_configureX11(void* owner, void* data) {
PWINDOW->updateWindowDecos();
if (!g_pCompositor->isWorkspaceVisible(PWINDOW->m_iWorkspaceID))
if (!g_pCompositor->isWorkspaceVisible(PWINDOW->m_pWorkspace))
return; // further things are only for visible windows
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace;
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace;
g_pCompositor->changeWindowZOrder(PWINDOW, true);
@@ -1110,7 +1107,7 @@ void Events::listener_configureX11(void* owner, void* data) {
}
void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!PWINDOW->m_bIsMapped)
return;
@@ -1155,7 +1152,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.goal();
PWINDOW->m_vSize = PWINDOW->m_vRealSize.goal();
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace;
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace;
g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->updateWindowDecos();
@@ -1175,16 +1172,16 @@ void Events::listener_setOverrideRedirect(void* owner, void* data) {
}
void Events::listener_associateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW, "XWayland Window");
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XWayland Window");
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW.get(), "XWayland Window");
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW.get(), "XWayland Window");
PWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PWINDOW), PWINDOW);
}
void Events::listener_dissociateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
PWINDOW->m_pWLSurface.unassign();
@@ -1199,7 +1196,7 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
if (XWSURFACE->parent)
Debug::log(LOG, "Window parent data: {} at {:x}", XWSURFACE->parent->_class ? XWSURFACE->parent->_class : "null", (uintptr_t)XWSURFACE->parent);
const auto PNEWWINDOW = (CWindow*)g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create());
PNEWWINDOW->m_uSurface.xwayland = XWSURFACE;
PNEWWINDOW->m_iX11Type = XWSURFACE->override_redirect ? 2 : 1;
@@ -1207,11 +1204,11 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->m_pX11Parent = g_pCompositor->getX11Parent(PNEWWINDOW);
PNEWWINDOW->hyprListener_associateX11.initCallback(&XWSURFACE->events.associate, &Events::listener_associateX11, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_dissociateX11.initCallback(&XWSURFACE->events.dissociate, &Events::listener_dissociateX11, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_setOverrideRedirect.initCallback(&XWSURFACE->events.set_override_redirect, &Events::listener_setOverrideRedirect, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_associateX11.initCallback(&XWSURFACE->events.associate, &Events::listener_associateX11, PNEWWINDOW.get(), "XWayland Window");
PNEWWINDOW->hyprListener_dissociateX11.initCallback(&XWSURFACE->events.dissociate, &Events::listener_dissociateX11, PNEWWINDOW.get(), "XWayland Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XWayland Window");
PNEWWINDOW->hyprListener_setOverrideRedirect.initCallback(&XWSURFACE->events.set_override_redirect, &Events::listener_setOverrideRedirect, PNEWWINDOW.get(), "XWayland Window");
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW.get(), "XWayland Window");
}
void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
@@ -1221,32 +1218,26 @@ void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create());
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PNEWWINDOW), PNEWWINDOW);
}
void Events::listener_NewXDGDeco(wl_listener* listener, void* data) {
const auto WLRDECO = (wlr_xdg_toplevel_decoration_v1*)data;
wlr_xdg_toplevel_decoration_v1_set_mode(WLRDECO, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
void Events::listener_requestMaximize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE)
return;
Debug::log(LOG, "Maximize request for {}", PWINDOW);
if (!PWINDOW->m_bIsX11) {
const auto EV = (wlr_foreign_toplevel_handle_v1_maximized_event*)data;
g_pCompositor->setWindowFullscreen(PWINDOW, EV ? EV->maximized : !PWINDOW->m_bIsFullscreen,
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen,
FULLSCREEN_MAXIMIZED); // this will be rejected if there already is a fullscreen window
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
@@ -1259,7 +1250,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
}
void Events::listener_requestMinimize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
Debug::log(LOG, "Minimize request for {}", PWINDOW);
@@ -1269,26 +1260,25 @@ void Events::listener_requestMinimize(void* owner, void* data) {
const auto E = (wlr_xwayland_minimize_event*)data;
g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW, (int)E->minimize)});
EMIT_HOOK_EVENT("minimize", (std::vector<void*>{PWINDOW, (void*)E->minimize}));
g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW.get(), (int)E->minimize)});
EMIT_HOOK_EVENT("minimize", (std::vector<std::any>{PWINDOW, E->minimize}));
wlr_xwayland_surface_set_minimized(PWINDOW->m_uSurface.xwayland, E->minimize && g_pCompositor->m_pLastWindow != PWINDOW); // fucking DXVK
wlr_xwayland_surface_set_minimized(PWINDOW->m_uSurface.xwayland, E->minimize && g_pCompositor->m_pLastWindow.lock() != PWINDOW); // fucking DXVK
} else {
const auto E = (wlr_foreign_toplevel_handle_v1_minimized_event*)data;
g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW, E ? (int)E->minimized : 1)});
EMIT_HOOK_EVENT("minimize", (std::vector<void*>{PWINDOW, (void*)(E ? (uint64_t)E->minimized : 1)}));
g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW.get(), 1)});
EMIT_HOOK_EVENT("minimize", (std::vector<std::any>{PWINDOW, (int64_t)(1)}));
}
}
void Events::listener_requestMove(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
// ignore
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
}
void Events::listener_requestResize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
// ignore
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);

View File

@@ -6,7 +6,7 @@ CBaseAnimatedVariable::CBaseAnimatedVariable(ANIMATEDVARTYPE type) : m_Type(type
; // dummy var
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, CWindow* pWindow, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWindow = pWindow;
@@ -14,7 +14,7 @@ void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, CWindo
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, SLayerSurface* pLayer, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pLayer = pLayer;
@@ -22,7 +22,7 @@ void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, SLayer
m_bDummy = false;
}
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, CWorkspace* pWorkspace, AVARDAMAGEPOLICY policy) {
void CBaseAnimatedVariable::create(SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
m_eDamagePolicy = policy;
m_pConfig = pAnimConfig;
m_pWorkspace = pWorkspace;

View File

@@ -6,8 +6,9 @@
#include <type_traits>
#include "Vector2D.hpp"
#include "Color.hpp"
#include "../macros.hpp"
#include "../defines.hpp"
#include "../debug/Log.hpp"
#include "../desktop/DesktopTypes.hpp"
enum ANIMATEDVARTYPE {
AVARTYPE_INVALID = -1,
@@ -48,11 +49,11 @@ enum AVARDAMAGEPOLICY {
};
class CAnimationManager;
class CWorkspace;
struct SLayerSurface;
struct SAnimationPropertyConfig;
class CHyprRenderer;
class CWindow;
class CWorkspace;
class CLayerSurface;
// Utility to define a concept as a list of possible type
template <class T, class... U>
@@ -67,9 +68,9 @@ concept Animable = OneOf<T, Vector2D, float, CColor>;
class CBaseAnimatedVariable {
public:
CBaseAnimatedVariable(ANIMATEDVARTYPE type);
void create(SAnimationPropertyConfig* pAnimConfig, CWindow* pWindow, AVARDAMAGEPOLICY policy);
void create(SAnimationPropertyConfig* pAnimConfig, SLayerSurface* pLayer, AVARDAMAGEPOLICY policy);
void create(SAnimationPropertyConfig* pAnimConfig, CWorkspace* pWorkspace, AVARDAMAGEPOLICY policy);
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(const CBaseAnimatedVariable&) = delete;
@@ -139,14 +140,14 @@ class CBaseAnimatedVariable {
m_bRemoveEndAfterRan = false;
}
CWindow* getWindow() {
return (CWindow*)m_pWindow;
PHLWINDOW getWindow() {
return m_pWindow.lock();
}
protected:
void* m_pWindow = nullptr;
void* m_pWorkspace = nullptr;
void* m_pLayer = nullptr;
PHLWINDOWREF m_pWindow;
std::weak_ptr<CWorkspace> m_pWorkspace;
PHLLSREF m_pLayer;
SAnimationPropertyConfig* m_pConfig = nullptr;
@@ -198,7 +199,7 @@ class CBaseAnimatedVariable {
friend class CAnimationManager;
friend class CWorkspace;
friend struct SLayerSurface;
friend class CLayerSurface;
friend class CHyprRenderer;
};
@@ -207,17 +208,17 @@ class CAnimatedVariable : public CBaseAnimatedVariable {
public:
CAnimatedVariable() : CBaseAnimatedVariable(typeToANIMATEDVARTYPE<VarType>) {} // dummy var
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, CWindow* pWindow, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWINDOW pWindow, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pWindow, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, SLayerSurface* pLayer, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLLS pLayer, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pLayer, policy);
m_Value = value;
m_Goal = value;
}
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, CWorkspace* pWorkspace, AVARDAMAGEPOLICY policy) {
void create(const VarType& value, SAnimationPropertyConfig* pAnimConfig, PHLWORKSPACE pWorkspace, AVARDAMAGEPOLICY policy) {
create(pAnimConfig, pWorkspace, policy);
m_Value = value;
m_Goal = value;
@@ -303,6 +304,6 @@ class CAnimatedVariable : public CBaseAnimatedVariable {
friend class CAnimationManager;
friend class CWorkspace;
friend struct SLayerSurface;
friend class CLayerSurface;
friend class CHyprRenderer;
};

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