Compare commits

..

539 Commits

Author SHA1 Message Date
vaxerski
1b48642fd1 props: bump version to 0.30.0 2023-09-23 00:07:52 +01:00
vaxerski
3b1e09e5a1 renderer: fix missing dontRound check in renderSurface
fixes 3296
2023-09-23 00:06:48 +01:00
vaxerski
9f68aa33ea renderer: damage monitor on special workspace change
fixes #3396
2023-09-22 23:36:28 +01:00
vaxerski
ea45bfb63c renderer: cleanup drm format selection 2023-09-22 20:15:12 +01:00
memchr
ea5d9584da refactor: manually resets singletons (#3395) 2023-09-22 11:06:09 +01:00
memchr
1357b66091 windowrules: add rule group to map windows grouped (#3279)
* windows: add rule group to map windows grouped

* group rule: use `invade` to force open a window in a locked group
2023-09-22 00:42:00 +01:00
vaxerski
2e1842b5ff screencopy: log renderer_begin_with_buffer failures 2023-09-22 00:21:07 +01:00
vaxerski
b662215fad input: simplify fullscreen workspace input calculations
Fixes an issue with special workspaces not receiving focus above fullscreen windows, and cleans up the code
2023-09-21 23:58:58 +01:00
alaricljs
e4ddfcfa0c keybinds: implement pushactivetobottom dispacher (#3217)
Co-authored-by: Leeman <lstrout@enlj.com>
2023-09-21 22:18:26 +01:00
vaxerski
d41a91e050 [gha] Nix: update wlroots 2023-09-21 10:20:18 +00:00
vaxerski
47f38dbc8f deps: update wlroots 2023-09-21 11:19:25 +01:00
Mihai Fufezan
1925e64c21 Vector2D: fix typo in include 2023-09-20 22:26:08 +03:00
vaxerski
62efc045d7 input: lock focus to last surface on buttons held
fixes #3385
2023-09-20 19:50:46 +01:00
Vaxry
ef94375882 doc: Add a CoC (#3366) 2023-09-20 19:38:44 +01:00
Vaxry
0dbd997003 input: Various constraint handling fixes (#3381)
Fixes #3204
2023-09-20 16:47:05 +01:00
memchr
3785defaf1 logging: implement std::formatter for some types (#3380) 2023-09-20 16:25:03 +01:00
memchr
6594b50e57 logging/format: use std::format_string to catch formatting string errors at compile time (#3377)
* fix(log): use constexpr format string

* deprecate getFormat
2023-09-20 08:26:20 +01:00
Dickby
d8d0cd75c2 internal: Make part of CCompositor::focusWindow a bit easier to read. (#3356) 2023-09-19 11:37:30 +01:00
memchr
c50072b108 varlist: cleanup unintuitive string splitting (#3369)
* refactor(varlist): replace unintuitive string splitting

* refactor(varlist): remove test asserts
2023-09-19 09:44:54 +01:00
vaxerski
60f10e6037 hyprctl: print monitor IDs as int64 2023-09-19 00:41:56 +01:00
vaxerski
e4d6695375 keybinds: focus last window on workspace change to another mon 2023-09-18 11:33:19 +01:00
Dickby
b0a82c04df bezier: Remove Todo about N-Point bezier curves. (#3352) 2023-09-17 18:48:11 +01:00
vaxerski
af15b15b4b input: guard constraint in unconstrainMouse 2023-09-17 16:46:04 +01:00
Mihai Fufezan
f72e04d63b misc: add disable_hypr_chan option (#3343) 2023-09-17 15:26:24 +01:00
vaxerski
c8cc811e85 nix: update patches for meson changes 2023-09-17 00:08:19 +01:00
vaxerski
c0082519ae wlroots: disable wlr vulkan support
Hyprland will never use vulkan so this is completely redundant
2023-09-17 00:02:11 +01:00
vaxerski
2f01a18989 window: recalculate layout on swallow in map
fixes #2848
2023-09-16 18:40:57 +01:00
Dickby
a53ec98b82 bezier: Optimize CBezierCurve::getYForPoint (#3321) 2023-09-16 18:32:33 +01:00
fufexan
d126d2c092 [gha] Nix: update inputs 2023-09-16 00:03:39 +00:00
Jeremy Huang
cc630c90b5 master: partially revert 0e64dd2e and fix the animation (#3327) 2023-09-15 22:13:23 +01:00
vaxerski
b9b38424b0 input: fix hints not being used if committed at the beginning 2023-09-15 21:01:54 +01:00
memchr
56adec7c1a socket2: emit lockgroups event (#3317) 2023-09-15 12:06:59 +01:00
MightyPlaza
824290c791 windowrules: fix workspace exec rule with special (#3316)
modified:   src/events/Windows.cpp
2023-09-15 12:05:21 +01:00
Jeremy Huang
d3cbec2d1a master: Add mfact dispatcher (#3298)
* add mfact dispatcher

* limit mfact to between 0.05 and 0.95

* add try catch block for stof

* change log to err
2023-09-15 12:03:05 +01:00
Mihai Fufezan
f8008e4b3b Nix CI: use DetSys' install & cache actions 2023-09-14 17:55:53 +03:00
vaxerski
2536630049 dwindle: fix moving windows on special 2023-09-14 15:37:45 +01:00
memchr
e6651334f2 refactor: flatten nesting ifs in layout window creation method (#3294) 2023-09-14 12:27:16 +01:00
Jeremy Huang
0e64dd2ea5 master: respect the resizing animation option in master layout (#3300) 2023-09-14 12:14:10 +01:00
memchr
db2b72adee config: fix relative path resolution (#3308) 2023-09-14 12:07:31 +01:00
memchr
0dc8289b02 dwindle: fix: don't merge groups when moving window (#3302) 2023-09-14 11:56:29 +01:00
Mihai Fufezan
f6473aa3ad flake.lock: update nixpkgs and xdph
- nix/module: update xdph override
- nix/overlays: remove hyprland-share-picker (now built directly inside xdph)
2023-09-14 12:03:48 +03:00
memchr
84f8f4d77d keybinds: warp cursor to correct window in moveWindowOutOfGroup (#3290) 2023-09-13 15:02:21 +01:00
Jeremy Huang
2ad429dfe0 master: Fix the wrong resize speed in master layout center orientation (#3289)
* rebase

* change isStackVertical variable to make it more readable

* fix some code getting unnecessarily repeating in a loop
2023-09-13 11:48:19 +01:00
memchr
4f88897fc0 keybinds: avoid onWindowRemoved & onWindowCreated when group has one member (#3286)
Make moveWindowOutOfGroup behave like toggleGroup on a group with single
member, this prevents the window from moving around rabidly.
2023-09-13 11:44:02 +01:00
vaxerski
41e5f401c5 keybinds: simulate movement on empty focus after workspace change 2023-09-13 11:36:41 +01:00
vaxerski
b884544ee6 input: don't refocus on switched workspace on the same monitor 2023-09-13 11:33:36 +01:00
memchr
6b1ac659e0 layout: add direction parameter to onWindowCreated and friends (#3269)
* feat(layout): add direction parameter to onWindowCreated and friends

In addition:

- Implement directional moveWindowOutOfGroup for `movewindoworgroup`
  when using dwindle layout. (augmentation of #3006)
- Replace `DWindleLayout::OneTimeFocus` with `IHyprLayout::eDirection`.
- Slight formatting change (clang-format).

* fix: nullptr dereference in dwindle window creation

* refactor: generalized eDirection

* refactor: eliminate DIRECTION_NONE

* Update IHyprLayout.hpp
2023-09-13 11:13:29 +01:00
outfoxxed
b0d5e4008b layout: Allow the layout to control how windows are raised in groups (#3275)
* Allow the layout to control how windows are raised in groups

Previously windows could only be focused if they weren't hidden or
were part of a group. This shifts the logic for picking the window out
of a group to the layout allowing for alternate group implementations
to function normally.

* Fix doc comment consistency

* Fix tabs in comments
2023-09-12 22:37:08 +01:00
vaxerski
be19773aaa windowrules: fix workspace rule separation
fixes #3265
2023-09-12 16:50:10 +01:00
vaxerski
2e34548aea varlist: move to a separate header, add join 2023-09-12 16:50:10 +01:00
memchr
5cc53c14d9 config: add wildcard handling in source= (#3276) 2023-09-12 12:54:05 +01:00
alaricljs
9192b20b96 windowrules: add on-screen constraint to wrv2 'move' (#3247)
* add on-screen constraint to wrv2 'move'

* review changes

* std::clamp

* more parens

---------

Co-authored-by: Leeman <lstrout@enlj.com>
2023-09-12 09:56:20 +01:00
Mihai Fufezan
b6191cbc76 CI: don't run wlroots update in PRs 2023-09-12 11:48:30 +03:00
Mihai Fufezan
ed51fe7bac CI: add reusable actions 2023-09-12 01:40:57 +03:00
memchr
bc41d7ec85 fix: inconsistent behaviour where last workspace was not saved (#3261) 2023-09-11 23:39:24 +01:00
memchr
df51c45d7f keybinds: unify changeworkspace dispatcher (#3250)
* refactor: changeworkspace dispatcher

* refactor: remove redundant focusWindow calls

* refactor(changeworkspace): warp cursor to middle of last focused window

* refactor: use rememberPrevWorkspace

* Fix: `CWorkspace::rememberPreWorkspace` condition is illogical
2023-09-11 15:14:43 +01:00
vaxerski
5a6d0e9963 refactor: fix missed middle() call in CKeybindManager 2023-09-11 12:19:21 +01:00
alaricljs
99fac59938 keybinds: movewindow to screen edge for floating windows (#3245)
* movewindow to screen edge for floating windows

* review changes

* constify vars

---------

Co-authored-by: Leeman <lstrout@enlj.com>
Co-authored-by: vaxerski <vaxry@vaxry.net>
2023-09-11 10:23:57 +01:00
memchr
e96e0dc02d refactor: utilize 'middle()' method for window/montior center (#3253) 2023-09-11 10:09:34 +01:00
vaxerski
3859607b6c input: do not process mouse in unsafe state 2023-09-10 23:44:47 +01:00
memchr
ac2f1a9c30 feat: add a new movewindoworgroup dispatcher (#3006) 2023-09-10 23:29:10 +01:00
alaricljs
81661b49aa windowrules: add % to resizeparams (#3246)
Co-authored-by: Leeman <lstrout@enlj.com>
2023-09-10 23:26:14 +01:00
memchr
79862c957c layout: add missing groupbar decoration to the window (#3235)
* fix: add missing groupbar decoration to the window

Add groupbar decoration to the operand window of `CWindow::insertWindowToGroup` if it does not exist, to prevent segmentation faults when mouse events are triggered after moving the window to a group, where `getDecorationByType(DECORATION_GROUPBAR)` unexpectedly returns nullptr.

Also fixed a bug where the group bar disappeared when the moveIntoGroup dispatcher's operand window was in a group.

* Update Window.cpp
2023-09-10 22:59:10 +01:00
Jeremy Huang
19bbdeed47 master: Add smart resizing option for master layout (#3210)
* add smart resizing for master layout

* fix smart resizing workspace check

* master layout fix smart resize when at max size

* change resizing for center orientation so it doesnt use all nodes

* master layout resizing, simplify code for calculating total height and weight

* remove the redundant smart resizing check
2023-09-10 22:58:06 +01:00
vaxerski
0d53401217 xwayland: adjust coord calculation
coord exactly at the corner would be calc'd wrong
2023-09-10 20:19:09 +01:00
Mihai Fufezan
1e60802968 [gha] Nix: bump wlroots 2023-09-10 20:32:54 +03:00
vaxerski
d28725c678 keybinds: ignore conditions on special release
while the previous commit ignores mods, we also need to ignore other conditions to avoid dangling presses
2023-09-10 16:54:14 +01:00
vaxerski
dcb909df04 keybinds: track pressed special binds
Ignore mods only if we're processing a special bind, and only if it's pressed. Otherwise we might shadow normal key releases via ignoring mods. Fixes #3240
2023-09-10 16:27:14 +01:00
vaxerski
807fc20525 deps: update wlroots 2023-09-10 13:18:22 +01:00
Philip Damianik
6c855dd6e4 internal: Fix wlr output management (#3234) 2023-09-10 13:14:27 +01:00
vaxerski
d490f198a4 keybinds: ignore mods on release of special binds
fixes #3208
2023-09-10 13:13:15 +01:00
memchr
a781c152ff fix(layout): remove focusWindow calls from onWindowCreatedTiling (#3233) 2023-09-10 12:55:18 +01:00
memchr
f7f70c9e72 refactor: replace lambdas with CKeybindManager::switchToWindow (#3229)
moves `switchToWindow` lambdas to a single private method
`CKeybindManager::switchToWindow()`.

fixes #3227, as a by-product of the mouse motion simulation at the end
of `CKeybindManager::switchToWindow()`.
2023-09-10 12:51:27 +01:00
vaxerski
f4e99a36a9 input: don't warp region constraints 2023-09-10 12:49:53 +01:00
Philip Damianik
1a6f961de2 hyprctl: Make device configs queryable (#3226)
* Make device configs queryable

Signed-off-by: pdamianik <39028343+pdamianik@users.noreply.github.com>

* Add set property to getoption output

---------

Signed-off-by: pdamianik <39028343+pdamianik@users.noreply.github.com>
2023-09-09 12:25:17 +01:00
shadowmax31
c061946a94 keybinds: Do not suppress pass and mouse release (#3219) 2023-09-09 10:15:24 +01:00
memchr
6648274735 fix: focusWindow on hidden workspace triggers another focusWindow. (#3216)
This commit address an issue where focusing a window on a hidden
workspace inadvertently triggered a second `focusWindow` call due to
simulated mouse movement. This behaviour led to the incorrect focus on
the window under the cursor instead of target window of method
`focusWindow()`, disrupting `focusurgentorlast` and `focuscurrentorlast`
dispatchers. Introduced a flag to the `CMonitor::changeWorkspace()`
method to prevent simulated mouse movements. This flag is set to false
by default. Changed the `focusWindow()` method accordingly to set this
flag to true when the target window is in a hidden workspace.
2023-09-08 19:17:04 +02:00
vaxerski
398e861b55 internal: fix warnings 2023-09-07 16:43:01 +02:00
Mihai Fufezan
0be6b03ee9 Nix: use gcc13Stdenv 2023-09-07 11:20:08 +03:00
vaxerski
cc5852faa2 config: disable blur special by default
very expensive
2023-09-07 00:35:44 +02:00
vaxerski
b2516010b7 hyprctl: fix missing comma in getopt -j
Fixes #3200
2023-09-06 23:52:25 +02:00
Mihai Fufezan
0d5a6f3168 Nix: fix xdph build 2023-09-07 00:52:19 +03:00
Mihai Fufezan
1581666171 flake.lock: update xdph and nixpkgs 2023-09-07 00:32:38 +03:00
Philip Damianik
8c83852704 internal: Remove all .c_str() calls when using std::vformat (#3198)
Signed-off-by: pdamianik <39028343+pdamianik@users.noreply.github.com>
2023-09-06 21:45:37 +02:00
Philip Damianik
60c01dab01 config: Integrate HASCONFIG into CConfigManager::getConfigValueSafeDevice (#3195) 2023-09-06 19:16:46 +02:00
Philip Damianik
a15e3e1f38 config/input: Improve fallback behavior for unset device config values (#3184)
* Fix fallback behavior for unset device config values

* Replace overload with default argument for fallback config key

* Remove default value for fallback argument in source

* Fix typos
2023-09-06 16:14:18 +02:00
vaxerski
a1cc99a986 pch: add format 2023-09-06 14:59:34 +02:00
unrealhoang
f90a009e93 input/config: add button scroll lock (#3189) 2023-09-06 14:54:48 +02:00
vaxerski
8b9cc9a8db animationmgr: guard monitor ptr in animationSlide 2023-09-06 13:05:37 +02:00
vaxerski
37e2311a3e config: loosen restrictions around animation keywords
Fixes #3185, makes hyprland ignore further args if an animation is disabled
2023-09-06 12:58:01 +02:00
Vaxry
61a71c65ac internal: Formatter rework (#3186) 2023-09-06 12:51:36 +02:00
vaxerski
c3a83daa1e monitor: minor adjustments 2023-09-05 22:16:14 +02:00
Dickby
fa3de9b70e renderer: Fix blur for passes set to 0. (#3181) 2023-09-05 16:33:40 +02:00
vaxerski
2d100bf57e socket2: add renameWorkspace event 2023-09-05 15:55:24 +02:00
Dickby
28f1f035b1 animationmgr: Optimize CAnimationManager::scheduleTick a bit. (#3172)
* Optimize CAnimationManager::scheduleTick a bit.

* Use integer in wl_event_source_timer_update call.
2023-09-05 15:06:39 +02:00
vaxerski
db48f973fd xdgoutput: do not destroy resources on monitorRemoved 2023-09-05 13:33:08 +02:00
Mihai Fufezan
4ddcda93f5 Nix: remove hidpi patches 2023-09-04 21:12:27 +03:00
memchr
8e9f010ee0 build: use PCH to reduce compile time (#3095) 2023-09-04 19:56:02 +02:00
vaxerski
4eecb8bffc config: don't enable vrr 2 for maximized workspaces
fixes #3162
2023-09-04 18:24:18 +02:00
vaxerski
d9937fcdba input: minor constraint fixes
fixes the confined region, warping issues, etc. Closes #3158
2023-09-04 17:34:19 +02:00
vaxerski
e3c83ab2e0 xwaylandmgr: find closest output in xwayland coord translation
partially off-screen windows would be snapped to 0,0 otherwise
2023-09-04 17:30:48 +02:00
shadowmax31
b4c832a1f2 master: Add orientationcycle command on MasterLayout (#3128) 2023-09-04 16:45:58 +02:00
vaxerski
9f3a64481e dwindle: add proper movement for window move binds
ditches the "movewindow = swapwindow" mechanism. Fixes #2804
2023-09-04 15:34:13 +02:00
memchr
69439871e6 refactor: replace manual iteration of CWindow with existing methods (#3147) 2023-09-04 15:13:39 +02:00
MightyPlaza
6a0e2bbff3 socket2: added "activespecial" IPC event (#3163) 2023-09-04 15:11:51 +02:00
vaxerski
32f75ebb70 renderer: separate workspace window render logic
Makes the logic used to render stuff over the windows (special, popups, ime, lockscreen) unified by yeeting the workspace window logic (which is separate cuz fullscreen windows) into their own funcs. Fixes #2053
2023-09-04 15:07:56 +02:00
vaxerski
35df4693ea animatedvariable: don't reset timers on duplicate setters
Fixes the annoying jump when a recalc happens while a window is being animated
2023-09-04 13:11:56 +02:00
MightyPlaza
8fefb180b1 windowrules: Monitor rule fix (#3157)
* fix monitor rules
modified:   src/events/Windows.cpp

* exec on workspace as silent when special is open
modified:   src/events/Windows.cpp
2023-09-03 17:23:13 +02:00
vaxerski
5126bfab72 monitor: ignore no-op workspace changes 2023-09-03 17:21:55 +02:00
vaxerski
96d555e8e7 props: bump ver to 0.29.1 2023-09-03 13:32:57 +02:00
vaxerski
c6c820d16d layout: always center floating xdg windows 2023-09-03 13:07:40 +02:00
vaxerski
e6ca4b6eee window: check for special id matching in close_special_on_empty 2023-09-03 13:02:05 +02:00
vaxerski
5e0cf7d6a5 special: add misc:close_special_on_empty
fixes #3156
2023-09-03 13:00:06 +02:00
staz
01c6c5ae22 internal: Revert rounding of window size (#3153)
* Revert "renderer: round coords and size in renderWindow"

This reverts commit fc0c1896e0.

* Revert "xwaylandmgr: round reported sizes"

This reverts commit 2985e20e6a.
2023-09-03 12:51:48 +02:00
vaxerski
423b129b24 renderer: don't set enabled in applyMonitorRule 2023-09-03 12:43:57 +02:00
MightyPlaza
00bee91bbc internal: workspace rule handling refactor (#3116)
modified:   src/events/Windows.cpp
2023-09-02 20:41:02 +02:00
vaxerski
d4ec54d048 renderer: render IME popups in fullscreen
fixes #1353
2023-09-02 20:32:15 +02:00
memchr
69ce11a063 fix: use-after-free of CWindow* in CHyprGroupBarDecoration::draw (#3146) 2023-09-02 19:58:01 +02:00
vaxerski
204a580544 keybinds: fixup global conditions 2023-09-02 19:36:12 +02:00
vaxerski
1ecfb5e852 globalshortcuts: allow empty appid 2023-09-02 19:32:05 +02:00
vaxerski
f69c5469d7 readme: refactor and update a bit 2023-09-02 00:21:57 +02:00
vaxerski
2985e20e6a xwaylandmgr: round reported sizes
Since we round the sizes in the renderer, it's also a good idea to round the sizes reported to the apps. Fixes #3138
2023-09-02 00:04:55 +02:00
vaxerski
d2a785dfe3 renderer: fixup occlusion conditions
fixes #3123 I fucking hate yall
2023-09-01 23:01:59 +02:00
vaxerski
563fe83db2 keybinds: set special monitor id before recalc
set special workspace's monitor ID before we call recalculateMonitor in toggleSpecialWorkspace. Fixes #1151
2023-09-01 22:57:23 +02:00
vaxerski
f242f9447b keybinds: suppress up event if down was consumed
Fixes #3000
2023-09-01 22:14:10 +02:00
Vaxry
d9292800a2 internal: Unsafe state reworks (#3114) 2023-09-01 22:03:56 +02:00
vaxerski
774a5bedf8 waylandResource: remove user data in markDefunct and not ~dtor 2023-09-01 20:03:58 +02:00
vaxerski
8314341ffe readme: remove wlr_ext workspace proto support mention 2023-09-01 17:22:46 +02:00
vaxerski
b48f810a12 meson/cmake: remove refs to ext-workspace-unstable-v1 2023-09-01 17:14:56 +02:00
vaxerski
bb0933437f wlr-ext-workspace: remove protocol impl
Various reasons: 1st, memory issues. 2nd, MR got closed (see https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/merge_requests/35) 3rd, not needed anymore (waybar has its own hyprland/workspaces module)
2023-09-01 17:10:03 +02:00
q234rty
5035f5fc68 text-input-v1: Fix preedit styling in chromium (#3131) 2023-09-01 16:39:15 +02:00
memchr
1a13d44d5d refactor: raise SIGABRT instead of write to null address (#3124) 2023-08-31 22:52:02 +02:00
vaxerski
fc0c1896e0 renderer: round coords and size in renderWindow 2023-08-31 09:55:38 +02:00
vaxerski
9c4f776757 keybinds: fixup release conditions
honestly I already forgot why the m_vHeldBack vec existed. Seems useless and annoying. Fixes #3113
2023-08-30 23:23:35 +02:00
MightyPlaza
b10cae3010 decos: groupbar mouse interaction (#3102)
* allow groupbar clicking
	modified:   src/Window.cpp
	modified:   src/Window.hpp
	modified:   src/managers/input/InputManager.cpp
	modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
	modified:   src/render/decorations/CHyprGroupBarDecoration.hpp

* remove setting pos inside insertWindowToGroup()

modified:   src/Window.cpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/managers/KeybindManager.cpp

* add group window by index and group size functions
modified:   src/Window.cpp
modified:   src/Window.hpp
modified:   src/managers/input/InputManager.cpp

* allow dragging into groupbar
modified:   src/Window.cpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp

* allow dragging from groupbar
modified:   src/managers/KeybindManager.cpp

* try groupbar clicking before border resize
modified:   src/managers/input/InputManager.cpp

* block grabbing groupbar on floating (crash)

remove later when crashing is fixed

modified:   src/managers/KeybindManager.cpp

* remove redundant { }
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp

* implement getWindowDecorationBox()
modified:   src/Window.cpp
modified:   src/Window.hpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/managers/KeybindManager.cpp
modified:   src/managers/input/InputManager.cpp
modified:   src/render/decorations/CHyprDropShadowDecoration.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
modified:   src/render/decorations/IHyprWindowDecoration.cpp
modified:   src/render/decorations/IHyprWindowDecoration.hpp

* fix crash when moveoutofgroup in floating windows

also removes dragging from floating windows limitation

modified:   src/layout/IHyprLayout.cpp
modified:   src/managers/KeybindManager.cpp

* use CRegion in getWindowDecorationBox()
modified:   src/helpers/Region.cpp
modified:   src/helpers/Region.hpp
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/managers/KeybindManager.cpp
modified:   src/managers/input/InputManager.cpp
modified:   src/render/decorations/IHyprWindowDecoration.cpp
modified:   src/render/decorations/IHyprWindowDecoration.hpp

* add groupbar scrolling
modified:   src/config/ConfigManager.cpp
modified:   src/managers/input/InputManager.cpp

* change name to getWindowDecorationRegion()
modified:   src/layout/DwindleLayout.cpp
modified:   src/layout/MasterLayout.cpp
modified:   src/managers/KeybindManager.cpp
modified:   src/managers/input/InputManager.cpp
modified:   src/render/decorations/IHyprWindowDecoration.cpp
modified:   src/render/decorations/IHyprWindowDecoration.hpp

* make dragging from group less hacky for floating
modified:   src/managers/KeybindManager.cpp
2023-08-30 17:39:22 +02:00
vaxerski
c98a00678c input: fix xwayland constraint calcs
some minor adjustments, fixes #3028
2023-08-30 17:23:35 +02:00
vaxerski
b4f123d1f2 compositor: remove wlr_scene refs
super old leftovers
2023-08-30 17:22:55 +02:00
vaxerski
28a90d6055 input: warp in unconstrainMouse
XWayland coord translation would fuck up if we did this in destroyConstraint
2023-08-30 13:42:17 +02:00
vaxerski
32f4059b37 screencopy: fix region sharing
translate before scaling if we are not dividing the coords
2023-08-30 13:17:26 +02:00
vaxerski
84c4a14dad opengl: cleanup framebuffer style 2023-08-29 23:24:35 +02:00
NotAShelf
c5084f36c6 nix: bump nixpkgs
adresses recent mesa version bump in nixpkgs
2023-08-29 17:34:40 +01:00
vaxerski
0a78f6031c props: bump ver to 0.29.0 2023-08-28 23:03:35 +02:00
vaxerski
981386d2ae layout: allow changing float status of fullscreen windows 2023-08-28 22:55:52 +02:00
vaxerski
b8f38dcbd3 layout: allow drag on fullscreen windows 2023-08-28 22:54:23 +02:00
vaxerski
df691859fb renderer: make contrast and brightness adjustments before rendering 2023-08-28 18:29:44 +02:00
pastalian
aed1f66bec makefile: fix build failure with some PREFIX (#3099)
Most systems does not have xdg-desktop-portal directory other than
/usr/share.
2023-08-28 17:21:01 +02:00
Vaxry
4a41d013a2 internal: Fix XDP multi-portal issues (#3077)
* add a hyprland portals conf

* remove portal checks

* typo

* Nix: remove portal patch

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-08-26 17:12:21 +02:00
vaxerski
299d201e56 renderer: don't blur special on disabled blur 2023-08-26 11:48:04 +02:00
Mihai Fufezan
d63a42e93f Nix & Meson: bump wlroots 2023-08-26 12:07:11 +03:00
vaxerski
ae69b9a2fa deps: update wlroots 2023-08-25 20:42:11 +02:00
vaxerski
116b9a8056 xdgoutput: mark resources defunct when monitor is unplugged 2023-08-25 18:22:25 +02:00
vaxerski
9dae8ece71 layout: use full box for visibility check of floating xdg
Fixes #3069, if an xdg dialog pops up and is partially off bounds, just move it to the center.
2023-08-25 18:10:12 +02:00
vaxerski
870471dd96 internal: allow opening empty special workspaces
Fixes point 2 of #2596
2023-08-25 18:05:08 +02:00
vaxerski
23e17700a7 renderer: add decoration:blur:special
will blur behind the special ws
2023-08-25 17:44:17 +02:00
memchr
f0da0b0be4 input: improved path handling; null check return value of fopen. (#3061) 2023-08-25 17:21:55 +02:00
vaxerski
3a1f30519b compositor: fix offset in auto arrangeMonitors
Fixes #3043, I am an idiot
2023-08-25 13:11:32 +02:00
vaxerski
6a5a5ed11e input: add transparent binds
adds a new flag for binds to be transparent (non-shadowable)
fixes #3058
2023-08-25 12:35:24 +02:00
vaxerski
ad085666c1 input: sanitize newlines in device names
fixes #3066 by replacing newlines with dashes in device names
2023-08-25 12:17:48 +02:00
Jeremy Huang
90c03e5bd2 master: layout resize makes the wrong size changes (#3064) 2023-08-24 11:57:30 +02:00
end-4
17ea7db23a gestures: Swipe direction lock (#3052) 2023-08-23 22:40:19 +02:00
Tristan Daniël Maat
4d14edd8a5 nix module: Fix attempted workaround for option name change
`system.stateVersion` is about *system state*, i.e., stuff that was
created by old software versions on users' machines, like SQL
databases that have moved to a different directory or such.

It will not help figure out whether an option has been renamed.
2023-08-23 13:32:06 +03:00
Mihai Fufezan
3576ee61f1 Nix: update flake.lock 2023-08-23 13:12:25 +03:00
Mihai Fufezan
6692fb12ab CI: set myself as author and committer 2023-08-23 13:08:02 +03:00
vaxerski
9d094f655e renderer: track fade alpha for blur if ignore opacity is true
with ignore_opacity, blur would stay at 1 opacity during a fadeOut animation on layers, making it look wrong. Fixes #3051
2023-08-23 12:03:27 +02:00
Mihai Fufezan
5e7183daf5 CI/Nix: remove waybar build 2023-08-22 20:59:15 +03:00
vaxerski
ba31518ed8 compositor: fix log types 2023-08-22 16:44:33 +02:00
MightyPlaza
41d9b6f0d7 dwindle: use smart on display borders (#2897) 2023-08-21 20:57:55 +02:00
memchr
9ad4a96d18 renderer: update alpha of layers only if active ws (#2994)
update `alpha` of ZWLR_LAYER_SHELL_V1_LAYER_TOP only when Workspaces is active on the monitor.
2023-08-21 20:55:57 +02:00
memchr
cb59763d32 fix: improved focus behaviour on workspace switching for follow_mouse!=1 (#3041)
* fix: improved focus behaviour on workspace switching for follow_mouse!=1

When the 'input:follow_mouse' is not set to 1 and the target workspace
for switching does not currently have the previously focused window,
Hyprland will prioritize focusing on the top-left-most window.

* fix: #2451 call simulateMouseMovement.

* unify logic

* multimon fix

---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-08-21 20:54:02 +02:00
vaxerski
9977a8bfd4 compositor: log more in arrangeMonitors 2023-08-21 19:52:34 +02:00
Vaxry
37128bfd43 internal: Wayland Protocol impl improvements (#2944) 2023-08-21 19:36:09 +02:00
Mihai Fufezan
17d8e4750b Nix: remove waybar completely
Get it from Nixpkgs instead.
2023-08-21 15:34:38 +03:00
vaxerski
63b2189ce8 xdgshell: damage old popup coords after a reposition
fixes #3038. I updated the shell ver without properly supporting .reposition
2023-08-21 01:15:00 +02:00
vaxerski
025c023e4b xwayland: use logical pos in unmanaged geom requests
using raw xwayland coords is wrong cuz we use separate coord spaces
2023-08-21 00:39:39 +02:00
memchr
09cc96c0d5 feat(debug): add debug:suppress_errors to suppress errors. (#3032) 2023-08-20 18:17:49 +02:00
vaxerski
b79dfcceb4 compositor: fix missed raw throws 2023-08-20 13:58:46 +02:00
vaxerski
7713daa86a compositor: log thrown runtime exceptions 2023-08-20 11:47:06 +02:00
vaxerski
fff118fa76 xdgoutput: don't send all details in .get 2023-08-19 21:14:12 +02:00
vaxerski
ebc5fed9b2 compositor: minor cleanup 2023-08-19 19:24:48 +02:00
vaxerski
942ee943f5 input: unify constraint hint logic
Unifies the constraint hint logic. Previously, ignoring the stupid conditions, unscaled hints were mistaken with scaled coordinates.
2023-08-18 22:40:23 +02:00
vaxerski
14f20a7372 xwayland: fix incorrect VECINRECT usage
Fixes #3002. VECINRECT uses x2 and y2 and not w / h.
2023-08-18 22:20:05 +02:00
vaxerski
3d9545d2e0 shaders: minor premultiplication fixes 2023-08-18 22:07:28 +02:00
end-4
37a211a2ae animations: add slidefade and slidefadevert styles for workspaces (#3008)
* add slidefade and slidefadevert animations

* fix swiping for slidefadevert

* rename minPerc to movePerc for slidefade anim styles

* change default slidefade percentage to 100%

* remove useless comments

* findlastof + 1

* debug logging for slidefade/slidefadevert percentage
2023-08-17 22:30:20 +02:00
MightyPlaza
7155b4c266 renderer: Border improvements (#2986)
* simplify getting border size
	modified:   src/Window.cpp
	modified:   src/Window.hpp
	modified:   src/layout/DwindleLayout.cpp
	modified:   src/layout/MasterLayout.cpp
	modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* fix border damage and input box
	modified:   src/Window.cpp
	modified:   src/managers/AnimationManager.cpp

* add border size in resize_on_border box
	modified:   src/managers/input/InputManager.cpp

* simplify border (fixes possible typo)
	modified:   src/Window.cpp

* use updateSpecialRenderData()
	modified:   Window.cpp
	modified:   Window.hpp
	modified:   events/Windows.cpp
	modified:   layout/DwindleLayout.cpp
	modified:   layout/IHyprLayout.cpp
	modified:   layout/MasterLayout.cpp
	modified:   render/Renderer.cpp

* update window workspace rule data on reload
	modified:   src/config/ConfigManager.cpp

* use rounding()
	modified:   src/Window.cpp
2023-08-17 10:13:19 +02:00
vaxerski
7e8a212027 internal: allow negative monitor offsets 2023-08-16 18:16:39 +02:00
Mihai Fufezan
19f3e927d9 Nix: deprecate waybar-hyprland
Now merged in Nixpkgs, no reason to keep it here.
2023-08-16 18:46:57 +03:00
Mihai Fufezan
78fa8adadc Nix/module: fix system when config.allowAlias is false 2023-08-16 13:39:20 +03:00
Dashie
19c4855afc layout: Use refreshrate tick on all manual animations (#2988) 2023-08-16 10:56:48 +02:00
memchr
63b266cf65 internal: ioctl use /dev/tty instead of fd 0 for VT_GETSTATE (#2989)
* fix: ioctl use /dev/tty instead of fd 0 for VT_GETSTATE

ioctl VT_GETSTATE on stdin fails if it is not console, such as when
using GDM.  `/dev/tty` should be used instead.

- `/dev/tty` is a synonym for the controlling terminal of a process, if
there is one.

---------

Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
2023-08-16 10:51:08 +02:00
vaxerski
4986d74ef2 xwayland: fix use of xwayland coords in native spaces 2023-08-15 20:10:43 +02:00
vaxerski
91e28bbe9d xwayland: use a completely separate coordinate system
Use a completely separate coordinate system for XWayland. This fixes all issues with force_zero_scaling overlaps.

Fixes #2916
2023-08-15 19:15:37 +02:00
kbtz
2b4537606f master: fix always_center_master (#2961)
* master: fix always_center_master

* master: allow resizing single centered window

---------

Co-authored-by: kbtz <github@kbtz.dev>
2023-08-15 18:13:45 +02:00
vaxerski
347a1eb662 xdgoutput: separate logic for zero scaling positions
if a scaled monitor was directly to the left of another monitor, we'd change the size to have zero scaling without pushing other monitors out of the way, creating overlaps in xwayland.
2023-08-15 17:55:29 +02:00
vaxerski
7c4daee29a compositor: fix skipping iterators in arrangeMonitors 2023-08-14 19:27:33 +02:00
vaxerski
b9a783229b compositor: don't apply offsets in renderer 2023-08-14 18:41:52 +02:00
vaxerski
2110dc1f03 compositor: fix auto positioning offset calcs 2023-08-14 17:31:36 +02:00
Mihai Fufezan
e5fb9b1b02 CI: update auto commit author & committer 2023-08-14 15:34:44 +03:00
vaxerski
13886a264f internal: add a new monitor auto system 2023-08-14 14:22:10 +02:00
Mihai Fufezan
a0cf890292 Nix/module: final fix 2023-08-14 11:53:58 +03:00
Mihai Fufezan
9ba6eab8db ugh 2023-08-14 11:37:08 +03:00
Mihai Fufezan
9180fb08e2 Nix/module: fix package option 2023-08-14 11:12:57 +03:00
Mihai Fufezan
314f88de53 Nix: disable nixpkgs module in ours 2023-08-14 11:03:01 +03:00
Mihai Fufezan
aff4a1e237 Nix: don't warn against xdpw and xdph being present
It seems that, on NixOS at least, having both the Hyprland and the wlr
desktop portals does not result in unexpected behaviour. xdph will be
started and wlr ignored.
2023-08-14 10:39:57 +03:00
Mihai Fufezan
2650224c1f Nix: remove hidpi from modules 2023-08-14 10:39:57 +03:00
Mihai Fufezan
e510c6a7fc Nix: add deprecation messages for removed/renamed flags 2023-08-14 10:39:57 +03:00
Mihai Fufezan
c1bcbdb3dd Nix: remove HiDPI patches
- remove `hidpiXWayland` option
- rename `nvidiaPatches` -> `enableNvidiaPatches` for consistency
2023-08-14 10:39:57 +03:00
vaxerski
0314a727eb layers: set proper alpha to top grabbing surfaces 2023-08-13 21:52:43 +02:00
vaxerski
3fc4ac07e0 hyprctl: fix hyprpaper 2023-08-13 21:42:16 +02:00
Mihai Fufezan
38814e8a95 flake.lock: update 2023-08-13 20:47:44 +03:00
vaxerski
0220e4c1ea input: notify idle about activity on mouse move regardless of focus 2023-08-13 17:46:20 +02:00
MightyPlaza
4b568ae5f6 remove shadow no_gaps_when_only master (#2958)
* remove shadow no_gaps_when_only
	modified:   src/Window.hpp
	modified:   src/config/ConfigManager.cpp
	modified:   src/config/ConfigManager.hpp
	modified:   src/layout/DwindleLayout.cpp
	modified:   src/render/decorations/CHyprDropShadowDecoration.cpp

* add master
	modified:   src/layout/MasterLayout.cpp

* fix wrong
	modified:   src/layout/MasterLayout.cpp
2023-08-13 10:18:48 +02:00
MightyPlaza
739598717b remove shadow no_gaps_when_only (#2956)
modified:   src/Window.hpp
	modified:   src/config/ConfigManager.cpp
	modified:   src/config/ConfigManager.hpp
	modified:   src/layout/DwindleLayout.cpp
	modified:   src/render/decorations/CHyprDropShadowDecoration.cpp
2023-08-12 23:37:55 +02:00
Jacob Birkett
d20837bef8 Nix: corrections for overlays, overrideable systems (#2929)
* nix: overlays: move waybar-hyprland to own overlay

* flake: use legacyPackages for formatter

Run `nix fmt` for all files.

* flake: move default overlay to nix/overlays.nix

* nix: lib: remove lib

* nix: overlays: extras: explicitly include xdph overlays

* nix: use interpolation for versions

* nix: overlays: include deps with hyprland-packages

* flake: make systems overrideable

* flake: packages: inherit from overlaid pkgsFor
2023-08-12 15:22:37 +03:00
vaxerski
3f7f4207a6 config: add vrr per-display 2023-08-11 17:37:57 +02:00
UserSv4
8a7ce59ad4 internal: Fix grouped windows not being properly focused on activation (#2925)
* Fix grouped windows not being properly focused on activation

This bug could happen if you:
1. Open two Chromium windows in a single group
2. Open some tabs in both of the windows
3. Using tabs search (Ctrl+Shift+A) try to switch to a tab in the second window from the first one.
When this happens any window focus would be lost (hyprctl activewindow starts to output "Invalid") and also the mouse cursor would become centered relatively to the second window.

* Update Compositor.cpp
2023-08-11 17:27:09 +02:00
vaxerski
60b548296d internal: move backtrace to specific func 2023-08-11 17:22:19 +02:00
MightyPlaza
901236a535 rules: center window improvements (#2935)
* center window improvements
	modified:   src/config/ConfigManager.cpp
	modified:   src/events/Windows.cpp
	modified:   src/managers/KeybindManager.cpp

* remove redundant line
	modified:   src/events/Windows.cpp

* simplify code
	modified:   src/events/Windows.cpp
	modified:   src/managers/KeybindManager.cpp
2023-08-11 16:54:16 +02:00
vaxerski
e4e8ae8f88 dwindle: disallow togglesplit on fullscreen 2023-08-10 22:01:34 +02:00
MightyPlaza
c4c3b590e5 toplevel: patches group toplevel-activated (#2931)
* patches group toplevel-activated

	modified:   src/Compositor.cpp
	modified:   src/Window.cpp

* remove redundant call

	modified:   src/Compositor.cpp

* fix style
	modified:   src/Compositor.cpp
2023-08-10 20:06:39 +02:00
vaxerski
126792584f renderer: don't add workspace offset to pinned 2023-08-09 22:15:37 +02:00
vaxerski
cbb899740c rules: add layer and window xray rules 2023-08-09 22:03:31 +02:00
rewine
fe9453c643 Nix: enable systemd only when it's available on hostPlatform 2023-08-09 20:25:28 +03:00
vaxerski
d7209b90bb renderer: remember extents before removing a window and use them for dt
fixes #2351
2023-08-09 14:28:04 +02:00
vaxerski
47430411d6 hyprctl: remove log 2023-08-09 14:10:54 +02:00
Mihai Fufezan
ec269622fc flake.lock: update xdph 2023-08-08 21:50:28 +03:00
Gonçalo Duarte
12cb109137 Nix: fix nvidia patch for dual gpu system
* this fix it most important for laptops with dgpu
 * remove post patch line wlroots.nix

Signed-off-by: Gonçalo Duarte <gonegrier.duarte@gmail.com>
2023-08-08 21:27:05 +03:00
vaxerski
8e04a80e60 toplevelexport: minor fixes to dmabuf 2023-08-08 19:10:53 +02:00
MightyPlaza
6295cbe9cb keybinds: Keep aspect ratio (#2907) 2023-08-08 18:52:20 +02:00
vaxerski
b68292340c hyprctl: fix old request methods 2023-08-08 18:41:00 +02:00
vaxerski
ab73183cb2 config: adjust default background color 2023-08-08 18:04:46 +02:00
vaxerski
36052abd33 hyprctl: add --instance 2023-08-08 18:04:24 +02:00
0x455241
b3393c429f background: add background color option (#2915)
Co-authored-by: 0x455241 <>
2023-08-08 18:00:11 +02:00
vaxerski
c748f36939 internal: add lock files and hyprctl instances 2023-08-08 16:16:40 +02:00
Michael
ad3b8dddf9 keybinds: Only call fclose() when we have a valid file. (#2908)
Signed-off-by: Michael Pacheco <git@michaelpacheco.org>
2023-08-08 16:07:15 +02:00
Michael
cebab759d5 Close keymap files. Fixes #2904 (#2905)
Signed-off-by: Michael Pacheco <git@michaelpacheco.org>
2023-08-07 19:34:15 +02:00
vaxerski
deeeb33c5f macros: fix missing include 2023-08-07 18:48:08 +02:00
vaxerski
261c3307f7 internal: cleanup headers in helpers/ 2023-08-07 13:36:14 +02:00
vaxerski
6e53c47e68 renderer: fix missing premultiplication for border multisampling 2023-08-06 20:46:00 +02:00
vaxerski
6c10c38481 renderer: fully switch to premultiplied alpha 2023-08-06 20:33:36 +02:00
vaxerski
a077b7a92e animationmgr: avoid redundant ticks 2023-08-05 23:29:33 +02:00
MightyPlaza
b925f1b497 fix workspace change focus (#2891) 2023-08-05 20:53:13 +02:00
Mihai Fufezan
46cf4eb837 Nix: bump wlroots 2023-08-04 14:58:16 +00:00
Mihai Fufezan
79ce387cb8 CI: update auto commit author 2023-08-04 17:41:42 +03:00
vaxerski
ef0d97153a xdgoutput: manually destroy manager resource 2023-08-04 14:07:41 +02:00
vaxerski
2e6693fbb6 compositor: update window rules on workspace move 2023-08-03 21:59:12 +02:00
romanstingler
7d98181ade xwayland: drop wlr_xwayland_surface.events.set_pid (#2884)
Co-authored-by: Roman Stingler <roman.stingler@waterdrop.com>
2023-08-03 21:40:56 +02:00
vaxerski
7a2027d1fd input: fix and unify client checking in mouseRequests 2023-08-03 18:44:07 +02:00
vaxerski
9654749244 props: bump version to 0.28.0 2023-08-03 18:00:10 +02:00
vaxerski
da46e01b97 config: update default config for new blur changes 2023-08-03 15:16:37 +02:00
Vaxry
d96f8ff0fe renderer: Various Blur Improvements (#2877)
* move blur to its own category

* blur improvements, contrast, brightness, noise
2023-08-03 15:11:10 +02:00
vaxerski
5c50fac907 windowrules: add workspace param 2023-08-02 13:21:38 +02:00
vaxerski
51cda87fe4 hyprctl: rerender on cursor_zoom_factor dynamic calls 2023-08-01 18:08:53 +02:00
vaxerski
da0c74cdf0 config: schedule frame for mons on reload 2023-08-01 15:47:56 +02:00
vaxerski
08651736ad keybinds: add toggle to dpms 2023-07-30 16:46:37 +02:00
Mihai Fufezan
bf0d8ab4a3 NixOS module: check system version for fonts 2023-07-29 23:05:35 +03:00
rewine
a805905a49 Nix: move wayland-scanner to nativeBuildInputs (#2816) 2023-07-29 16:31:01 +03:00
vaxerski
f61a714320 renderer: cleanup old redundancies in CFramebuffer 2023-07-29 13:27:40 +02:00
Po-Yeh Chen
77818e3457 xwayland: Add temporary fix for #2808 by adding nullptr check. (#2829)
* Add temporary fix for #2808 by adding nullptr check.

* Remove redundency.
2023-07-28 17:12:24 +02:00
Alfred Wingate
2ea7d10d04 assets/meson.build: install new wallpapers
* Previous changes forgot to update meson with the new wallpapers.

70dae78c1b
9bad2a8180

Signed-off-by: Alfred Wingate <parona@protonmail.com>
2023-07-27 13:25:40 +03:00
Gioele Pannetto
ef26f711c9 Added git tag in json version command (#2821) 2023-07-27 10:51:04 +02:00
Okirshen
c36c30c17b Renamed deprecated value in nix config
See 83793ca898 for more info.
2023-07-27 11:36:35 +03:00
vaxerski
a9b8e2159c renderer: reset scissor after renderTexturePrimitive 2023-07-25 13:33:08 +02:00
vaxerski
4173d2ccf6 input: fix styling 2023-07-25 11:49:36 +02:00
Mihai Fufezan
427321c5ab Nix: bump nixpkgs
Nix: bump wlroots
2023-07-25 10:27:41 +03:00
vaxerski
f5913135c6 input: add support for cursor-shape-v1 2023-07-24 18:50:17 +02:00
vaxerski
76d4a50af3 deps: update wlroots 2023-07-24 18:34:25 +02:00
vaxerski
603de16f9a renderer: adjust distribution of backgrounds 2023-07-24 18:30:00 +02:00
ferrreo
f6b340cc19 init: Fix for issue #2797 (#2799)
* Fix for issue #2797

* Fix for issue #2797

* Fix for issue #2797
2023-07-24 18:26:24 +02:00
memchr
76c6e09e39 keybinds: Make moveintogroup locking check configurable (#2796)
* groups: revert to the old moveintogroup behaviour, ignore m_sGroupData.locked

* groups: Make moveintogroup locking check configurable
2023-07-24 18:25:10 +02:00
vaxerski
9bad2a8180 backgrounds: add hypr chan var 2 2023-07-24 18:22:39 +02:00
MightyPlaza
6db3c4ef5e layout: no_border_when_only-improvements (#2791) 2023-07-24 12:13:40 +02:00
psentee
2bbe3aa122 nix: use final.hyprland instead of prev.hyprland in waybar-hyprland
Currently, waybar-hyprland package adds `prev.hyprland` to `$PATH`. This is nixpkgs' default hyprland, not hyprland injected by this overlay. I'd expect we want waybar-hyprland to depend on what's in `pkgs.hyprland` after this (and possibly user's other overlays) are applied – which is `final.hyprland`.
2023-07-23 23:19:57 +03:00
Mily
9fc5f4c48b init: Request SCHED_RR using CAP_SYS_NICE and add Python to nix dev shell (#2690)
* nix: add python3 to devShell

* init: request SCHED_RR scheduling policy

* init: checks if host supports reseting scheduler on fork

* init: make gainRealTime more compatible with other OSes

* init: remove linux-only code
2023-07-23 20:51:00 +02:00
MightyPlaza
50e6f368ff windowrules: implements nomaximizerequest (#2785)
* Window.hpp

* ConfigManager.cpp

* Windows.cpp

* fix maximize event
2023-07-23 19:47:41 +02:00
vaxerski
975c4175b2 hyprctl: unify and sanitize ver commit msg better 2023-07-23 19:43:15 +02:00
vaxerski
af395a8f55 windowrules: fix opacity override 2023-07-23 16:03:59 +02:00
vaxerski
90f69782ee window: fix segfault in boundingbox 2023-07-23 16:02:35 +02:00
vaxerski
5a64c73e05 render: use primitive end() only when no screen shader is present 2023-07-23 15:54:27 +02:00
end-4
9845f99b60 add config option to enable/disable new intuitive resizing (#2774)
dwindle:smart_resizing = 1 by default
== 1 then use new resizing
else use old resizing
2023-07-23 15:50:09 +02:00
MightyPlaza
d3bba2489d render: Border fixes (#2781)
* Window.cpp

* layout

* CHyprGroupBarDecoration.cpp
2023-07-23 15:49:49 +02:00
Rachel Knight
b21644b611 input: Fix #2376 mouse movement bug in XWayland (#2776) 2023-07-22 19:31:36 +02:00
vaxerski
b70553cf46 opengl: fix warn 2023-07-22 19:26:56 +02:00
vaxerski
1a7fb1572a renderer: disable init anim on disabled animations 2023-07-22 19:26:44 +02:00
outfoxxed
375e8385ee Fix background occlusion ignoring monitor position (#2771) 2023-07-22 10:19:51 +02:00
vaxerski
27dd07f1b8 windowrules: improve opacity 2023-07-21 19:05:00 +02:00
vaxerski
263b9c6e39 socket1: add a timeout for requests 2023-07-21 17:20:23 +02:00
vaxerski
d7e9eb65e2 renderer: use primitive rendering for copying buffers 2023-07-21 17:11:54 +02:00
Mihai Fufezan
0af97636fa Nix/HM module: solve conflict with upstream HM module 2023-07-21 15:55:56 +03:00
vaxerski
1ec0b7b59a forms: bring back auto labels 2023-07-20 21:54:57 +02:00
vaxerski
f864b15427 background: add option to force hypr chan 2023-07-20 21:01:07 +02:00
vaxerski
61dc0909ae layout: use warp() instead of setValue() 2023-07-20 20:03:27 +02:00
MightyPlaza
ca54ceff6f groups: add use_current_group_pos (#2759) 2023-07-20 19:48:32 +02:00
vaxerski
6c1f4faff2 animationmgr: avoid looping over all avars in favor of only active ones 2023-07-20 19:26:10 +02:00
vaxerski
7940f779e9 cmakelists: move tracy to debug section 2023-07-20 18:29:37 +02:00
vaxerski
a3f6a72a51 region: allow ctor from pixman_box32_t 2023-07-20 18:27:28 +02:00
vaxerski
d2a8b8c2de renderer: cleanup back buffer skip checks 2023-07-20 18:27:14 +02:00
vaxerski
833d73df09 shaders: short-circuit rounding if radius <= 0 2023-07-20 18:18:53 +02:00
vaxerski
23eda1411b renderer: scale box for occlusion 2023-07-20 18:12:29 +02:00
vaxerski
ff598b0827 renderer: don't occlude when pre-blur is queued 2023-07-20 18:03:47 +02:00
vaxerski
e5dd133808 cmake: fix compile 2023-07-20 17:57:02 +02:00
vaxerski
a3e1e5e8ba tracy: add more opengl zones 2023-07-20 17:51:38 +02:00
vaxerski
a921c5b89e Debug: add tracy 2023-07-20 17:47:49 +02:00
vaxerski
948855a984 renderer: add canSkipBackBufferClear 2023-07-20 14:11:05 +02:00
vaxerski
547305c7ed opengl: adjust blend and reduce the usage of clear 2023-07-20 13:49:28 +02:00
vaxerski
b65adf8d4a toplevelexport: support dmabuf + various fixes 2023-07-20 12:42:25 +02:00
fufexan
3a1496b4eb Nix: bump wlroots 2023-07-20 11:20:22 +03:00
Mihai Fufezan
a58b70ca07 CI: update scripts 2023-07-20 11:20:22 +03:00
Mihai Fufezan
91e3c654d3 Nix: move patches to subdir 2023-07-20 11:20:22 +03:00
vaxerski
7091d4e597 animationmanager: optimize avar state 2023-07-19 22:40:03 +02:00
vaxerski
80cd2ef3d7 renderer: remove unused var 2023-07-19 21:10:42 +02:00
vaxerski
2c2314faa0 renderer: take workspace offset into account for occlusion 2023-07-19 21:08:49 +02:00
vaxerski
88c2a02773 render: avoid allocating mirrorfb for no reason 2023-07-19 21:04:45 +02:00
Vaxry
89b87158db internal: Wrap regions (#2750) 2023-07-19 20:09:49 +02:00
vaxerski
ce9896204a renderer: fix rounding in renderSurface 2023-07-19 20:06:19 +02:00
vaxerski
f4f0f35c5b renderer: add occlusion for back layers 2023-07-19 16:13:55 +02:00
vaxerski
b08b72358a props: bump ver to 0.27.2 2023-07-19 13:31:35 +02:00
vaxerski
aac75ddcbf screencopy: guard region buffer values in frameDamage 2023-07-19 13:10:41 +02:00
outfoxxed
5cd5631fb2 Add bringWindowToTop function to IHyprLayout (#2747)
* Add bringWindowToTop function to IHyprLayout

* Rename `bringWindowToTop` to `requestFocusForWindow`

* Fix doc
2023-07-19 12:39:45 +02:00
vaxerski
b8a7b09092 screencopy: use wlr_buffer api for shm copies 2023-07-19 12:24:41 +02:00
vaxerski
81f4a4f471 screencopy: improve shm handling 2023-07-19 00:51:38 +02:00
vaxerski
2623364dbd no_xwayland: fix redef 2023-07-19 00:33:47 +02:00
MightyPlaza
3b03597784 keybinds: movegroupwindow-improvement (#2740)
* movegroupwindow-improvement

* use std::swap
2023-07-19 00:30:10 +02:00
vaxerski
ce9c5fd722 render: set refresh to 0 for both wl and x11 backends 2023-07-19 00:28:15 +02:00
vaxerski
f2999e84b9 render: set refresh to 0 for wayland backend outputs 2023-07-19 00:02:57 +02:00
vaxerski
2fed1badbf props: update ver to 0.27.1 2023-07-18 21:44:49 +02:00
Aaron Blasko
7c1dacea09 only return 0 when using -h (#2738) 2023-07-18 21:00:08 +02:00
vaxerski
08310b4af9 issues: add form templates 2023-07-18 16:51:14 +02:00
vaxerski
16fd9084ea screencopy: nullcheck for empty buffer 2023-07-18 15:52:53 +02:00
vaxerski
0ba28a46fd monitor: unplug all callbacks in ~dtor 2023-07-18 15:36:27 +02:00
Vaxry
8370a7fcc4 internal: Protocol C++ Wraps + XDGOutput impl (#2733)
move to our own xdgoutput impl instead of wlr's
2023-07-18 15:30:28 +02:00
vaxerski
629e61c7a5 monitor: disconnect bind on disconnect 2023-07-18 15:22:49 +02:00
vaxerski
2e323a5671 renderer: use correct wlr sample func 2023-07-18 12:25:48 +02:00
Jan Beich
8c9e2e1ff1 deps: update wlroots (#2734) 2023-07-18 12:13:59 +02:00
István Donkó
5c8a20be77 fix: handle window change directions in fullscreen (#2728) 2023-07-18 12:12:50 +02:00
Tuur Vanhoutte
d2eb4fee76 Avoid connected monitor reusing unavailable ID (#2731) 2023-07-18 12:12:05 +02:00
vaxerski
4537860079 layout: recalculate monitor instead of window in updateDynamicRules 2023-07-18 11:49:57 +02:00
vaxerski
7f47655f60 layout: recalc window on dynamic rule update 2023-07-18 00:11:43 +02:00
vaxerski
2c7b2ad6ca windowrules: add border size rule 2023-07-18 00:11:29 +02:00
MightyPlaza
ddb8c89776 Allow empty args in hyprctl dispatch (#2724) 2023-07-16 21:01:06 +02:00
MightyPlaza
cacdb424a9 massive-fix (#2725) 2023-07-16 21:00:38 +02:00
vaxerski
b156a9654f build: add asan enable status flag 2023-07-16 17:06:05 +02:00
vaxerski
3229862dd4 xwayland: guard monitor validity in xwayland scale overriding 2023-07-15 23:10:05 +02:00
vaxerski
06563d7034 popups: update parent pos on commit 2023-07-15 18:27:21 +02:00
vaxerski
459afcc47f idle: fix reverse flag for new idle protocol 2023-07-14 20:02:31 +02:00
Lennard Hofmann
06f5910365 Make bind modmask case-insensitive (#2714) 2023-07-14 18:39:53 +02:00
MightyPlaza
b159634ef9 move/resize window (#2706) 2023-07-13 20:20:40 +02:00
MightyPlaza
db2367bf33 update groub decos (#2705) 2023-07-13 20:17:14 +02:00
vaxerski
f8def68e7e idle: implement new protocol 2023-07-13 18:05:34 +02:00
vaxerski
9f7382bca4 keybinds: add movegroupwindow 2023-07-13 17:55:25 +02:00
Daniel Adolfsson
d3a644d81c Dwindle: Make resize more intuitive (#2681)
* improved resize

* clang-format

* rewrite

* almost legacy behavior when using CORNER_NONE
2023-07-13 16:52:11 +02:00
vaxerski
70dae78c1b background: add mascot versions 2023-07-13 14:32:30 +02:00
vaxerski
5e577acf51 props: bump ver to 0.27.0 2023-07-12 13:11:11 +02:00
zakk4223
21f64b6660 Keep new mapped layer's alpha zero if the workspace has a fullscreen window (#2686)
Co-authored-by: Zakk <zakk@rsdio.com>
2023-07-12 12:58:45 +02:00
vaxerski
7a7e3ee6d9 screencopy: don't send frames on commits w/o a buffer 2023-07-12 00:30:42 +02:00
vaxerski
9c9f56743e groupbar: fix vram leak 2023-07-11 20:57:38 +02:00
Daniel Adolfsson
64e7d5345d Add support for smart splitting (#2676)
* Add support for smart splitting

* clang-format

* smart_split default to 0, and make smart_split behave like preserve_split
2023-07-11 13:37:25 +02:00
MightyPlaza
29d017f54b monitor desc default workspace but working now (#2678)
* fix desc check

* fixes crashes when nulptr
2023-07-11 11:29:19 +02:00
vaxerski
26579fa962 texture: fix styling 2023-07-11 00:27:13 +02:00
vaxerski
0c61a1530f plugins: fix config value usage in init 2023-07-10 14:13:23 +02:00
vaxerski
da7ea2b33d pluginapi: add configReloaded event 2023-07-10 13:54:06 +02:00
vaxerski
382af06406 render: resize subsurfaces with size resizes 2023-07-10 13:32:57 +02:00
vaxerski
515a363ecd render: move lastFrameDamage to CMonitor 2023-07-10 13:21:00 +02:00
vaxerski
fe54dcb4eb screencopy: send original damage, avoid extents 2023-07-10 13:17:21 +02:00
vaxerski
42f46aeac5 config: minor path handling fixes 2023-07-10 13:10:34 +02:00
MightyPlaza
4cc0e6de90 monitor desc default workspace (#2673) 2023-07-10 07:53:03 +02:00
MightyPlaza
d9f7f039e1 monitor desc support (#2670)
* monitor desc

* monitor desc
2023-07-09 23:10:35 +02:00
Tuur Vanhoutte
b99ac063ea Reuse same ID when reconnecting monitor, otherwise use minimum available ID (#2666)
Fixes #2601
2023-07-09 23:08:40 +02:00
vaxerski
b33d82734f input: schedule frame on mouse move 2023-07-09 00:44:32 +02:00
Ed Younis
f49af187bc Xdg config home support (#2047)
* config: Add support for XDG_CONFIG_HOME

Contributes-to: #1040
Co-authored-by: Björn Bidar <bjorn.bidar@thaodan.de>
Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>

* config: Log used config file

* config: Add GetConfigDir and minor fixes

* config: fixed minor nitpicks

---------

Signed-off-by: Björn Bidar <bjorn.bidar@thaodan.de>
Co-authored-by: Björn Bidar <bjorn.bidar@thaodan.de>
2023-07-07 19:19:35 +02:00
vaxerski
e632bf176b config: fix reading touchpad values to non-touchpad per-device cfgs 2023-07-06 16:26:38 +02:00
Mykola Perehudov
41358c6fb5 Cleanup compositor deadcode (#2657)
* Remove dead code as it is a part of CCompositor::cleanup

* Unify sd_notify logic with Compositor lifetime
2023-07-06 15:24:49 +02:00
Mykola Perehudov
bbedb065e1 eventmanager: drop obsoleted ignore events flag (#2660)
This flag became obsoleted in commit
287e6c4ede
2023-07-06 15:23:11 +02:00
Mihai Fufezan
bc34713b29 Nix CI: fix typo 2023-07-06 13:02:56 +03:00
Mykola Perehudov
0c974b7236 Avoid passing control unix socket descriptors to children (#2656)
Add SOCK_CLOEXEC flags to server side connection FDs to make them
closed during execve(2).
2023-07-06 11:39:02 +02:00
nexec
8407a9af0a Close socket2 client descriptor on hangup (#2654) 2023-07-06 00:18:44 +02:00
Mihai Fufezan
c4f288582b flake.lock: update nixpkgs 2023-07-06 01:14:09 +03:00
Mihai Fufezan
981c71e60a Nix CI: split inputs and wlroots updating
Now there are separate update scripts for wlroots and for all other
inputs.
2023-07-05 16:46:02 +03:00
vaxerski
86e487e003 input: remove old redundant code 2023-07-05 13:20:40 +02:00
MightyPlaza
34d845da13 typo (#2643) 2023-07-04 22:03:19 +02:00
vaxerski
07d7962c7f swipe: fix mixup of r/m 2023-07-04 12:34:22 +02:00
vaxerski
18f9fb5e0f rules: add stayfocused 2023-07-04 12:05:25 +02:00
vaxerski
6f91997f06 layout: improve time restraints in window drag 2023-07-04 11:49:24 +02:00
MightyPlaza
283a8e77aa screenshot fix (#2640) 2023-07-04 11:40:28 +02:00
MightyPlaza
50755d26d4 lockgroups fix (#2636) 2023-07-03 15:53:04 +02:00
MightyPlaza
05047f60f4 groupbar fixes (#2630)
Fixes multiple groupbar decoration issues:

  -  togglegroup removes fullscreen to avoid to avoid weird state
 -   fixes issue where a group had multiple windows with head = true
 -   fixes issue where merging 2 groups would cause a window to have 2 groupbar decorations
  -  fixes issue where merging a group with more than 1 window into another group would make windows have no groupbar decoration
  -  fixes issue where ungrouping windows could just move them into another group on the same workspace


---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-07-03 12:49:56 +02:00
vaxerski
738ec900f4 config: fix floating rules with floating-by-rule windows 2023-07-02 13:02:48 +02:00
vaxerski
86ca283352 gestures: add workspace_swipe_use_r 2023-07-01 16:30:36 +02:00
vaxerski
6c28388420 layout: minor style fixes 2023-07-01 16:28:17 +02:00
vaxerski
e96fcb31f0 layout: set pseudo to float size in new window 2023-06-30 11:29:06 +02:00
Mihai Fufezan
64fc19cc81 Nix/portals.patch: conditionally check portals 2023-06-30 10:13:39 +03:00
vaxerski
1012e2735a shader: remove useless comment 2023-06-29 23:20:10 +02:00
MightyPlaza
990ad854bd Reloads animated decoration values set on window rules (#2594)
* Reloads dynamic window rules

* Update ConfigManager.cpp

* Update ConfigManager.cpp

* Reloads animated decoration values set on window rules

Reloads animated decoration values like border color and opacity set on window rule on config reload.
2023-06-28 11:40:16 +02:00
vaxerski
d83296c7a9 renderer: damage decos on damageWindow 2023-06-27 13:23:58 +02:00
Mihai Fufezan
62c75883d1 Nix: patch portals search dir 2023-06-27 00:51:04 +03:00
MightyPlaza
7ed66abe57 Reloads dynamic window rules (#2585)
* Reloads dynamic window rules

* Update ConfigManager.cpp

* Update ConfigManager.cpp
2023-06-26 13:03:51 +02:00
vaxerski
4294456cdc xwayland: remove spaces from output names 2023-06-25 13:52:24 +02:00
Mihai Fufezan
a82559f185 flake.lock: update nixpkgs 2023-06-24 16:38:23 +03:00
vaxerski
01f85a09a9 xwayland: send zero scaling to xwayland if enabled 2023-06-23 21:42:44 +02:00
vaxerski
69fae18e63 damage: account for popups in getFullWindowBoundingBox 2023-06-23 13:54:01 +02:00
vaxerski
c241da5ea5 crashreporter: log tag 2023-06-23 13:22:38 +02:00
vaxerski
0283c498d6 xwayland: fix minor force_zero_scaling bugs 2023-06-22 21:43:31 +02:00
vaxerski
fc59cef1ee keybinds: remove old todo 2023-06-21 21:20:26 +02:00
vaxerski
2f875aec79 includes: move workspace protocol header to includes 2023-06-21 21:17:05 +02:00
vaxerski
cfa4086b0b configmgr: fix idiotic comparisons in device configs 2023-06-21 20:58:35 +02:00
vaxerski
cbe9bf0e69 compositor: move group members properly in moveWindowToWorkspaceSafe 2023-06-21 20:51:18 +02:00
vaxerski
83ad6b9af8 groupbar: fix damage calcs 2023-06-21 13:16:10 +02:00
vaxerski
cabdf38ce4 internal: minor style fixes 2023-06-20 21:35:54 +02:00
vaxerski
2295bbdd80 xwaylandmanager: fixup style 2023-06-18 18:14:52 +02:00
Shemig
74ca81cc79 Partial revert of Commit 302ec13: (#2539)
Fix crash when screen size is 0x0 (#2523)

Reason: The disable of a monitor with 0x0 size
is causing issuses with some users.

https://github.com/hyprwm/Hyprland/issues/2537

Left the defensive code to resolve the crash.
Will continue to investigate and find a solution for the
dell xps disabled monitor

Co-authored-by: giladsx <gilad@spectalix.com>
2023-06-17 18:49:37 +02:00
vaxerski
5ac625d7bd format: fix hooksystem 2023-06-16 18:45:14 +02:00
solopasha
1d902a4621 fix updating revision in wlroots.wrap
revision gets updated to the previous one, not current.
2023-06-15 23:32:52 +03:00
Shemig
302ec1372c Fix crash when screen size is 0x0 (#2523)
(When booting into laptop clam mode in dell XPS)
and also ignore any screen with size 0x0 in the first place

Co-authored-by: giladsx <gilad@spectalix.com>
2023-06-15 22:07:58 +02:00
vaxerski
d768226de9 [gha] bump flake inputs 2023-06-15 08:53:30 +00:00
vaxerski
c55c28ec7f deps: update wlroots 2023-06-15 10:45:20 +02:00
vaxerski
c4dec4f796 internal: damage window on change group 2023-06-14 19:44:51 +02:00
vaxerski
0f1911a8d4 opengl: fix invalid tex references to tex-less shader 2023-06-14 13:47:57 +02:00
vaxerski
e43f7fc98d shader: init uniforms to -1 2023-06-14 13:29:12 +02:00
vaxerski
fbabb105c3 gamma: use wlr's new gamma manager event 2023-06-14 13:26:47 +02:00
outfoxxed
f0e4f6622e Implement pass binds (#2503)
* Implement pass binds

Pass binds run the associated dispatcher but do not prevent windows
from receiving the bind.

* Fix pass binds not working properly with release binds

* Rename `pass` to `nonConsuming`
2023-06-14 13:08:56 +02:00
JManch
9a88c19f1a screenshader: fix uniform variable checks (#2513) 2023-06-14 13:03:20 +02:00
vaxerski
7762ac0173 toplevelexport: ignore defunct windows 2023-06-13 20:13:21 +02:00
vaxerski
e8c6d0f51e compositor: don't set dim percent on disabled dim 2023-06-13 18:51:49 +02:00
vaxerski
807b52b019 animationmgr: warp on equal start and goal 2023-06-13 18:50:45 +02:00
vaxerski
0e31eaa157 shadow: drop useless damageEntire() 2023-06-13 18:50:45 +02:00
memchr
24ed9b061f feat: add variable to customize locked group border color (#2507)
added variables:
- general:col.group_border_locked
- general:col.group_border_locked_active
2023-06-13 12:04:54 +02:00
vaxerski
528cfc2889 [gha] bump flake inputs 2023-06-12 16:47:13 +00:00
vaxerski
91fbee24da deps: update wlroots 2023-06-12 18:35:33 +02:00
vaxerski
6beb79f27b xwayland: add force_zero_scaling 2023-06-11 21:52:13 +02:00
vaxerski
64ce06a353 xwayland: add nearest neighbor filtering as an opt 2023-06-11 21:33:59 +02:00
end-4
e1edfde539 Allow setting alpha value for ignorezero layer rule (#2477)
* rename ignorezero to ignorealpha

* allow setting ignorealpha value

This commit allows setting a float value (0-1) for the ignorealpha layer rule.
Does not yet have error handling; invalid ignorealpha layer rule will crash Hyprland.

* add brackets i forgot to add

* prevent crash with invalid ignorealpha value

prevents hyprland from immediately crashing with invalid ignorealpha layer rule
does not log

* don't try to set ignoreAlphaValue if alpha value not specified

* add catch to try, reintroduce ignorezero

- added catch after try cuz i was an idiot
- re-add ignorezero as an alternative to ignorealpha to not introduce a breaking change

* add logging for failed ignorealpha layer rule

* fix get ignorealpha's get VALUE

* check npos and use empty()

* rename VALUE cuz no longer const

* format Shader.hpp
2023-06-11 19:30:31 +02:00
Nicola Guerrera
10fd75c833 zoom: multiply by scale only on mouseZoomUseMouse (#2495) 2023-06-10 23:00:41 +02:00
vaxerski
7932e42507 screenshader: add output uniform 2023-06-10 16:10:26 +02:00
Nicola Guerrera
003993337a calculate zoom_center based on monitor scale (#2482) 2023-06-10 12:28:00 +02:00
memchr
bca3068db2 feat: add lockactivegroup dispatcher (#2478)
* feat: add lockactivegroup dispatcher

The `lockactivewindow` dispatcher takes `lock`, `toggle` or `unlock` as arguments. When a group is locked, no window or group can be added to it, nor can it be added to another group, but the `moveintogroup` and `moveoutofgroup` dispatches are not affected.

Implementation details:

the lock is implement via `SGroupData.locked` flag (defaults to false).

The flag is only relevant to the group head, and upon the group head's succession, the flag will be passed down to the new head. Meanwhile, the old head's flag will be set to false.

The flag is set to false when a group is dismissed.

New condition checks have been added to the dwindle and master layout to check if target group is unlocked (and if the source is also a group and unlocked) before adding windows to the target group.

* refactor: `lockactivegroup dispatcher code ordering
2023-06-09 23:44:18 +02:00
vaxerski
cf37922d42 input: update surface input on changeworkspace 2023-06-09 12:20:40 +02:00
eriedaberrie
d123835ef5 main: fix segfault when -c is given with no other arguments (#2470) 2023-06-09 12:15:18 +02:00
Michał
7f753cab9a chore: fix typos (#2463)
Some typos when spelling Hyprland
2023-06-07 20:18:39 +02:00
Franz Berger
4afeedbd56 Fix fractional scale updates in some cases (#2447)
* fix fractional scale update in moveWorkspaceToMonitor

* fix fractional scale update in moveToWorkspace

* Revert "fix fractional scale update in moveWorkspaceToMonitor"

This reverts commit 6612197a38.

* Revert "fix fractional scale update in moveToWorkspace"

This reverts commit 75d9795a06.

* move fractional scale code to updateSurfaceOutputs

* remove duplicate check

* remove superfluous setPreferredScale()
2023-06-06 09:48:07 +02:00
vaxerski
10db5a4fdb xwayland: disconnect events on destroy
thanks Kirill Primak
2023-06-05 20:51:47 +02:00
vaxerski
1a4e6e6a4b window: recalc on deco remove 2023-06-05 09:49:21 +02:00
staz
e4e6ddb075 Make movetoworkspace register previous workspace (#2436)
* Make movetoworkspace register previous workspace

* style: no braces + format with clang-format
2023-06-05 09:44:13 +02:00
vaxerski
4ef684f615 hyprctl: recalc layout on setprop 2023-06-04 21:35:23 +02:00
vaxerski
2629cfeeab layouts: make aware of borderSize prop 2023-06-04 21:35:23 +02:00
vaxerski
d83e5b8409 internal: make borderSize prop overridable 2023-06-04 21:35:23 +02:00
vaxerski
df98db5092 [gha] bump flake inputs 2023-06-03 16:07:31 +00:00
vaxerski
d87010f300 deps: update wlroots 2023-06-03 17:59:31 +02:00
vaxerski
c5a7202cd9 noxwl: add missing stubs 2023-06-03 13:01:32 +02:00
vaxerski
70e4162dcc [gha] bump flake inputs 2023-06-03 10:28:25 +00:00
vaxerski
147e962370 deps: update wlroots 2023-06-03 12:20:28 +02:00
Cyril Levis
d7db7040d4 feat: add ipc set title window event (#2419)
* feat: add ipc set title window event

* chore: add EMIT_HOOK_EVENT and change event name
2023-06-03 11:18:49 +02:00
vaxerski
67be8d89b5 [gha] build man pages 2023-06-03 09:11:37 +00:00
asdfer
18956144d5 Update ISSUE_GUIDELINES.md faq link to the wiki (#2424)
Co-authored-by: local <>
2023-06-03 11:11:13 +02:00
vaxerski
871ab24c6e internal: properly set monitor props on special windows 2023-06-02 20:14:34 +02:00
vaxerski
ce0f248d20 events: remove old comment 2023-06-02 19:50:02 +02:00
vaxerski
dd0bf87c01 [gha] bump flake inputs 2023-06-02 16:53:02 +00:00
vaxerski
6ba8310c13 deps: update wlroots 2023-06-02 18:44:05 +02:00
Sinkerine
ca3791fed8 [hyprctl] Expose the special workspace id and name of the monitor (#2392)
* [hyprctl] Expose the special workspace id of the monitor

So that we know if a special workspace is shown on a monitor

* [hyprctl] Add special workspace name to the output
2023-06-02 13:25:33 +02:00
vaxerski
9cf72a30fc debug: add WITH_ASAN flag in cmake 2023-06-01 17:08:11 +02:00
vaxerski
e76bd43f53 rules: add nodim 2023-05-31 21:11:20 +02:00
vaxerski
baf81cdc5d input: force focus on movefocus 2023-05-31 20:59:38 +02:00
vaxerski
9f72d508ae groupbars: fix damage calcs 2023-05-31 19:53:58 +02:00
vaxerski
1844e8adad [gha] bump flake inputs 2023-05-31 14:18:39 +00:00
vaxerski
b540d28849 deps: update wlroots 2023-05-31 16:09:25 +02:00
vaxerski
fd73a7f795 gradient: fix warn 2023-05-31 15:55:54 +02:00
vaxerski
51a930f802 version: bump to 0.26.0 2023-05-29 19:17:38 +02:00
vaxerski
a7cfbdb854 keybinds: fix tryMoveFocusToMonitor with special 2023-05-29 18:11:37 +02:00
vaxerski
1e3571eb5b layout: don't reset to floating size on drag tiled 2023-05-29 18:05:41 +02:00
vaxerski
5484411232 compositor: disallow sending pinned to special 2023-05-29 17:46:12 +02:00
Jan Beich
f1ad270ff8 vector: restore cmath include after 438d063ec6 (#2394)
src/helpers/Vector2D.cpp:27:26: error: no member named 'floor' in namespace 'std'
    return Vector2D(std::floor(x), std::floor(y));
                    ~~~~~^
src/helpers/Vector2D.cpp:27:41: error: no member named 'floor' in namespace 'std'
    return Vector2D(std::floor(x), std::floor(y));
                                   ~~~~~^
src/helpers/Vector2D.cpp:37:17: error: no member named 'sqrt' in namespace 'std'
    return std::sqrt(dx * dx + dy * dy);
           ~~~~~^
2023-05-29 15:12:00 +02:00
Jeremy Huang
b3a86952cf focus: fix #1675 window not scrollable after movefocus (#2390) 2023-05-29 09:52:36 +02:00
outfoxxed
409ff027f8 Fix UAF in animation end callback if callback deletes the animation (#2389)
Removes use after free when the end callback deletes the animation as
long as `m_bRemoveEndAfterRan` is false.
2023-05-29 09:51:58 +02:00
vaxerski
438d063ec6 vector: use c++ stdlib math functions instead of cmath 2023-05-28 22:50:13 +02:00
vaxerski
078ba6daa8 groupbars: add text color opt 2023-05-27 17:46:02 +02:00
Ruby Iris Juric
74b49de883 Add hyprland to waybar-hyprland path as fallback for hyprctl 2023-05-27 15:35:25 +03:00
Jeremy Huang
8afc2f45c7 focus: make cursor follow movewindow (#2374) 2023-05-27 12:16:50 +02:00
vaxerski
5f4659afef xdgshell: check and conform to rigid float sizes on commit 2023-05-26 14:49:03 +02:00
vaxerski
0887e2ee6e window: reveal current from group on toplevel activate 2023-05-26 13:44:59 +02:00
vaxerski
62e3953f5b master: guard monitor in recalc 2023-05-26 13:41:52 +02:00
vaxerski
9c9b74179c pluginapi: add separate window render events 2023-05-26 13:07:45 +02:00
vaxerski
a2bb95fc60 touch: fix double offset in local 2023-05-24 22:19:22 +02:00
vaxerski
12227d7b6a input: only configure newly added touch devices 2023-05-24 22:17:33 +02:00
vaxerski
a4c120d608 layouts: ignore direction forces on non-map 2023-05-24 19:23:10 +02:00
eriedaberrie
53285a75ad hyprctl: fix cut-off json outputs (#2352)
* hyprctl: fix cut-off json outputs

* Address comments
2023-05-24 17:46:56 +02:00
vaxerski
f877d68f4f launchanim: fix #2291 2023-05-24 15:49:31 +02:00
Xianhao Yu
5bfd5a9240 examples: fix incorrect link in per-device config comments (#2353) 2023-05-24 11:11:49 +02:00
vaxerski
eb1f832fce decos: recalc on add 2023-05-23 14:26:38 +02:00
vaxerski
799add8659 groupbars: fix reserved area on titles 2023-05-23 14:18:26 +02:00
vaxerski
90cb5fb672 groupbars: make gradients toggleable 2023-05-22 22:07:32 +02:00
vaxerski
d1ec314a03 groupbars: conserve VRAM by staticizing textures 2023-05-22 22:06:40 +02:00
vaxerski
7bcc01efb7 groupbars: fix minor alignment issues 2023-05-22 21:43:37 +02:00
vaxerski
206ac000b9 groupbars: add title and gradient rendering 2023-05-22 21:40:32 +02:00
vaxerski
1eb6cfd45c groupbar: make exclusive 2023-05-22 20:52:41 +02:00
vaxerski
088b4a68e6 moveActiveToWorkspace: update last window of old ws 2023-05-22 19:44:10 +02:00
vaxerski
086f724951 wlr_ext_workspaces: honor activate from client + format 2023-05-22 19:36:47 +02:00
Russell Greene
ad244190e0 1483: fix crash on last display disconnect (#2344) 2023-05-22 12:18:07 +02:00
maqrrr
9f8c5cb63c Fix broken pipe crash when event listener terminates (#2339) 2023-05-21 14:38:18 +02:00
vaxerski
5627b70981 input: reset cursor hide timer on tablet 2023-05-20 21:15:21 +02:00
vaxerski
79b8576df9 examples: update plugin makefile 2023-05-17 15:01:17 +01:00
vaxerski
ba714b3b71 examples: update plugin headers 2023-05-17 14:59:52 +01:00
Anthony Ruhier
d7935356da input: don't move monitor focus on wp change (#2320) 2023-05-17 13:32:59 +01:00
Anthony Ruhier
9ef7225532 don't swap workspaces if monitors are the same (#2322)
Return swapActiveWorkspaces early if MON1 and MON2 are equals, to avoid
buggy behavior.
2023-05-17 13:31:03 +01:00
outfoxxed
642030f959 Fix not finding function symbols for hooking (#2292)
Fixes no useful feedback about failing subcommand.
Fixes function hooks breaking when running under a path containing
spaces.

Replaced old usages with this function where possible.
Complex shell usages now use `execAndGetShell` which is equal to the
old function.
2023-05-16 23:39:14 +03:00
Mihai Fufezan
78826c6d18 meson & nix: install wlroots headers (#2287) 2023-05-16 20:50:18 +03:00
Eric_Luo
b5b9af508a bug fix (#2314)
fix float check logic

Co-authored-by: hnboy <hnywolf@gmail.ccom>
2023-05-16 17:43:04 +01:00
vaxerski
d68f8ea668 internal: make CAnimatedVariable non-move non-copy 2023-05-16 12:49:59 +01:00
vaxerski
5b84b0fb44 animationmgr: allow empty avars 2023-05-15 17:11:51 +01:00
Vaxry
621eac32d3 readme: update previews 2023-05-15 15:33:58 +01:00
vaxerski
824813fc6a internal: remove check for negative exact vector args 2023-05-15 15:16:06 +01:00
vaxerski
7c207243e4 input: check for matrix availability in touch config 2023-05-14 13:54:55 +01:00
vaxerski
b748b0734f input: don't refocus on closed window 2023-05-14 13:47:43 +01:00
vaxerski
896a78aaa0 lock: add allow_session_lock_restore 2023-05-13 12:36:36 +01:00
vaxerski
cc01550aff hyprctl: recalc layout on dynamic workspace 2023-05-12 01:16:27 +01:00
vaxerski
413a36a914 workspacerules: overwrite on existing 2023-05-12 01:15:32 +01:00
vaxerski
86ef85efae layershell: don't enter on unmapped ls 2023-05-12 01:07:46 +01:00
vaxerski
a483376591 pluginenv: copy built wlr headers 2023-05-11 20:17:26 +01:00
vaxerski
f2725a374a render: minor fixes to fullscreen rendering 2023-05-10 18:36:13 +01:00
vaxerski
7fde80f38e layers: fix wonky focus on multimon 2023-05-09 17:01:18 +01:00
vaxerski
cc4ccfdbfd internal: use i64 for workspaces in outofbounds 2023-05-09 14:08:05 +01:00
Andrei Alexeyev
2f87e4c2f3 renderer,config: add custom DRM modeline support (#2254)
This allows specifying custom display resolutions for the DRM backend.
This is useful for display overclocking, working around broken EDIDs,
etc. To use this feature, specify a modeline instead of a resolution
in the config, for example:

    monitor = DP-1, modeline 1071.101 3840 3848 3880 3920 2160 2263 2271 2277 +hsync -vsync, 0x0, 1

This example is a custom 3840x2160@120Hz mode with tightened timings.
I use it because the standard timings don't work with my monitor and GPU
combination (M28U with RX580).

The syntax is compatible with Sway and Xorg.
2023-05-09 14:01:45 +01:00
vaxerski
e7c2ea9724 layershell: focus if changed keyboard mode 2023-05-08 19:12:01 +01:00
scorpion-26
826dc61e5c Add "next on monitor or empty" workspace parameter (#2198)
* Add "next on monitor or empty" workspace parameter

Implements the following workspace parameter:
r+x/r-x (i.e. r+1): Behaves similar to the "m" parameter, but can also
select empty workspaces and it doesn't wrap around

* Improve code comments

* Implementation V2 for 'r' workspace param

* Rebase to upstream

Should fix CI

* Always set outName

* Include named workspaces

Currently only considers open/active named workspaces
2023-05-08 14:37:59 +01:00
vaxerski
a31dceb2c6 includes: remove redundant from screencopy 2023-05-07 00:11:33 +01:00
vaxerski
1ba7a09bf6 includes: use libdrm prefix for include 2023-05-07 00:11:01 +01:00
vaxerski
afe8d8dfec monitors: fix some bugs with re-plug 2023-05-06 16:50:15 +01:00
dtop129
7e5ba5e824 Fixed wrong focus changes when moving background workspaces (#2258) 2023-05-06 16:13:26 +01:00
vaxerski
83cd5e2ebd workspaces: minor fixes for multi-special 2023-05-06 16:10:51 +01:00
vaxerski
7f0738bcb3 varlist: allow using s for std::isspace 2023-05-06 01:16:20 +01:00
vaxerski
583b8842e7 configmanager: fix substr offset in default ws rule 2023-05-06 01:10:05 +01:00
Dashie
a8541d5f64 Add split preselection (#2240) 2023-05-06 01:02:18 +01:00
vaxerski
4ad03af544 compositor: don't focus pointer in focusWindow 2023-05-06 00:59:25 +01:00
vaxerski
0859944c9a input: add custom accel profiles 2023-05-05 16:06:13 +01:00
vaxerski
c0be1e2fd8 configmanager: remove useless log from handleWorkspaceRule 2023-05-05 15:42:02 +01:00
vaxerski
7b73a332ea configmanager: deprecate bindws 2023-05-05 15:41:25 +01:00
vaxerski
a5d63a0324 configmanager: store workspace rules as a deque 2023-05-05 15:33:31 +01:00
vaxerski
8435d6fc12 monitors: toggle special on changeworkspace with special 2023-05-05 15:01:31 +01:00
Mihai Fufezan
d1d4683c91 Issue template: request users to ping me for Nix 2023-05-05 11:46:20 +03:00
vaxerski
bf04c83e3d config: clear layer rules on reload 2023-05-04 18:28:45 +01:00
John Rinehart
0432804b18 Nix: expose/use flake's own hyprland-protocols in overlays.default (#2235) 2023-05-04 17:47:15 +03:00
solopasha
da093a8aec quote hash for the GIT_COMMIT_HASH macro (#2227)
otherwise compilation fails with smth like
```
../src/debug/../plugins/../defines.hpp:1:25:
error: ‘f27873a6f06dc2f87600edb890f3c38298bfb55f’ was not declared in this scope
```
2023-05-03 20:50:33 +01:00
177 changed files with 8409 additions and 6183 deletions

56
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Bug Report
description: Something is not working right
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Before opening a new issue, take a moment to search through the current open ones.
---
- type: input
id: ver
attributes:
label: Hyprland Version
description: "Paste here the output of `hyprctl version`."
placeholder: Hyprland, built from branch main at commit...
validations:
required: true
- type: dropdown
id: type
attributes:
label: Bug or Regression?
description: Is this a bug or a regression?
options:
- Bug
- Regression
validations:
required: true
- type: textarea
id: desc
attributes:
label: Description
description: "What went wrong?"
validations:
required: true
- type: textarea
id: repro
attributes:
label: How to reproduce
description: "How can someone else reproduce the issue?"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Crash reports, logs, images, videos
description: |
Anything that can help. Please always ATTACH and not paste them.
Logs can be found in /tmp/hypr
Crash reports are stored in ~/.hyprland or $XDG_CACHE_HOME/hyprland

View File

@@ -1,12 +0,0 @@
---
name: Bug report
about: Found a bug? Report it here!
title: ''
labels: bug
assignees: ''
---
Please consult the issue guidelines at
https://github.com/vaxerski/Hyprland/blob/main/docs/ISSUE_GUIDELINES.md
BEFORE submitting.

19
.github/ISSUE_TEMPLATE/feature.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Feature Request
description: I'd like to request additional functionality
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Before opening a new issue, take a moment to search through the current open ones.
---
- type: textarea
id: desc
attributes:
label: Description
description: "Describe your idea"
validations:
required: true

View File

@@ -1,12 +0,0 @@
---
name: Feature request
about: Suggest a feature/change/idea
title: ''
labels: enhancement
assignees: ''
---
Please consult the issue guidelines at
https://github.com/vaxerski/Hyprland/blob/main/docs/ISSUE_GUIDELINES.md
BEFORE submitting.

View File

@@ -15,13 +15,15 @@ jobs:
steps:
- name: Install deps
run: sudo apt install pandoc
- name: Clone repository
uses: actions/checkout@v3
# Not needed
# with:
# submodules: recursive
with:
token: ${{ secrets.PAT }}
- name: Build man pages
run: make man
- uses: stefanzweifel/git-auto-commit-action@v4
name: Commit
with:

View File

@@ -1,26 +0,0 @@
name: Build Hyprland (Nix)
on: [push, pull_request, workflow_dispatch]
jobs:
nix:
name: "Build Hyprland (Nix)"
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install nix
uses: cachix/install-nix-action@v20
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
auto-optimise-store = true
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v12
with:
name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build packages
run: nix flake check --print-build-logs --accept-flake-config

30
.github/workflows/nix-build.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
jobs:
build:
strategy:
matrix:
package:
- hyprland
- hyprland-nvidia
- xdg-desktop-portal-hyprland
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: cachix/cachix-action@v12
with:
name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix build -L ${{ matrix.command }}

15
.github/workflows/nix-ci.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Nix
on: [push, pull_request, workflow_dispatch]
jobs:
wlroots:
if: github.event_name != 'pull_request'
uses: ./.github/workflows/nix-update-wlroots.yml
secrets: inherit
build:
if: always() && !cancelled() && !contains(needs.*.result, 'failure')
needs: wlroots
uses: ./.github/workflows/nix-build.yml
secrets: inherit

29
.github/workflows/nix-update-inputs.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Nix
on:
schedule:
- cron: '0 0 * * *' # check daily
jobs:
update:
name: inputs
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
token: ${{ secrets.PAT }}
- uses: DeterminateSystems/nix-installer-action@main
- name: Update inputs
run: nix/update-inputs.sh
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "[gha] Nix: update inputs"
update-build:
needs: update
uses: ./.github/workflows/nix-build.yml
secrets: inherit

View File

@@ -0,0 +1,26 @@
name: Nix
on:
workflow_call:
secrets:
PAT:
required: true
jobs:
update:
name: wlroots
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
token: ${{ secrets.PAT }}
- uses: DeterminateSystems/nix-installer-action@main
- name: Update lockfile
run: nix/update-wlroots.sh
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "[gha] Nix: update wlroots"

View File

@@ -1,29 +0,0 @@
name: "Nix: update lockfile"
on: [push, workflow_dispatch]
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Install nix
uses: cachix/install-nix-action@v20
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
auto-optimise-store = true
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
experimental-features = nix-command flakes
- name: Update lockfile
run: nix/update-inputs.sh
- uses: cachix/cachix-action@v12
with:
name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build Waybar-Hyprland
run: nix build .#waybar-hyprland --print-build-logs
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "[gha] bump flake inputs"

View File

@@ -18,7 +18,7 @@ jobs:
- name: Create tarball with submodules
id: tar
run: |
sed -i "1s/^/#define GIT_COMMIT_HASH $(git rev-parse HEAD)\n#define GIT_TAG \"$(git describe --tags)\"\n/" ./src/defines.hpp
sed -i "1s/^/#define GIT_COMMIT_HASH \"$(git rev-parse HEAD)\"\n#define GIT_TAG \"$(git describe --tags)\"\n/" ./src/macros.hpp
mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz *
- id: whatrelease

3
.gitmodules vendored
View File

@@ -7,3 +7,6 @@
[submodule "subprojects/udis86"]
path = subprojects/udis86
url = https://github.com/canihavesomecoffee/udis86
[submodule "subprojects/tracy"]
path = subprojects/tracy
url = https://github.com/wolfpld/tracy

View File

@@ -71,6 +71,7 @@ endif()
include_directories(
.
"src/"
"subprojects/wlroots/include/"
"subprojects/wlroots/build/include/"
"subprojects/udis86/"
@@ -96,9 +97,29 @@ add_executable(Hyprland ${SRCFILES})
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
target_link_libraries(Hyprland asan)
if (WITH_ASAN)
message(STATUS "Enabling ASan")
add_compile_options(-pg -no-pie -fno-builtin -fsanitize=address)
target_link_libraries(Hyprland asan)
add_compile_options(-fsanitize=address)
endif()
if(USE_TRACY)
message(STATUS "Tracy is turned on")
option( TRACY_ENABLE "" ON)
option( TRACY_ON_DEMAND "" ON)
add_subdirectory (subprojects/tracy)
target_link_libraries(Hyprland Tracy::TracyClient)
if(USE_TRACY_GPU)
message(STATUS "Tracy GPU Profiling is turned on")
add_compile_definitions(USE_TRACY_GPU)
endif()
endif()
add_compile_options(-pg -no-pie -fno-builtin)
add_link_options(-pg -no-pie -fno-builtin)
endif()
@@ -144,14 +165,18 @@ target_compile_definitions(Hyprland
"GIT_DIRTY=\"${GIT_DIRTY}\""
"GIT_TAG=\"${GIT_TAG}\"")
target_link_libraries(Hyprland rt)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
message(STATUS "Setting precompiled headers")
target_precompile_headers(Hyprland PRIVATE $<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
message(STATUS "Setting link libraries")
target_link_libraries(Hyprland rt PkgConfig::deps)
function(protocol protoPath protoName external)
if (external)
execute_process(
@@ -172,8 +197,6 @@ function(protocol protoPath protoName external)
endif()
endfunction()
target_link_libraries(Hyprland PkgConfig::deps)
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032 # wlroots is provided by us
OpenGL::EGL
@@ -182,7 +205,6 @@ target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a
)
protocol("protocols/ext-workspace-unstable-v1.xml" "ext-workspace-unstable-v1" true)
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)
@@ -194,5 +216,7 @@ protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.
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("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)

96
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,96 @@
## Goal
Our goal is to provide a space where it is safe for everyone to contribute to,
and get support for, open-source software in a respectful and cooperative
manner.
We value all contributions and want to make this organization and its
surrounding community a place for everyone.
As members, contributors, and everyone else who may participate in the
development, we strive to keep the entire experience civil.
## Standards
Our community standards exist in order to make sure everyone feels comfortable
contributing to the project(s) together.
Our standards are:
- Do not harass, attack, or in any other way discriminate against anyone, including
for their protected traits, including, but not limited to, sex, religion, race,
appearance, gender, identity, nationality, sexuality, etc.
- Do not go off-topic, do not post spam.
- Treat everyone with respect.
Examples of breaking each rule respectively include:
- Harassment, bullying or inappropriate jokes about another person.
- Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand.
- Treating someone as worse because of their lack of understanding of an issue.
## Enforcement
Enforcement of this CoC is done by the members of the hyprwm organization.
We, as the organization, will strive our best to keep this community civil and
following the standards outlined above.
### Reporting incidents
If you believe an incident of breaking our standards has occurred, but nobody has
taken appropriate action, you can privately contact the people responsible for dealing
with such incidents in multiple ways:
***E-Mail***
- `vaxry[at]vaxry.net`
- `mihai[at]fufexan.net`
***Discord***
- `@vaxry`
- `@fufexan`
***Matrix***
- `@vaxry:matrix.vaxry.net`
- `@fufexan:matrix.org`
We, as members, guarantee your privacy and will not share those reports with anyone.
## Enforcement Strategy
Depending on the severity of the infraction, any action from the list below may be applied.
Please keep in mind cases are reviewed on a per-case basis and members are the ultimate
deciding factor in the type of punishment.
If the matter would benefit from an outside opinion, a member might reach for more opinions
from people unrelated to the organization, however, the final decision regarding the action
to be taken is still up to the member.
For example, if the matter at hand regards a representative of a marginalized group or minority,
the member might ask for a first-hand opinion from another representative of such group.
### Correction/Edit
If your message is found to be misleading or poorly worded, a member might
edit your message.
### Warning/Deletion
If your message is found inappropriate, a member might give you a public or private warning,
and/or delete your message.
### Mute
If your message is disruptive, or you have been repeatedly violating the standards,
a member might mute (or temporarily ban) you.
### Ban
If your message is hateful, very disruptive, or other, less serious infractions are repeated
ignoring previous punishments, a member might ban you permanently.
## Scope
This CoC shall apply to all projects ran under the `hyprwm` organization and all _official_ communities
outside of GitHub.
However, it is worth noting that official communities outside of GitHub might have their own,
additional sets of rules.

View File

@@ -44,9 +44,7 @@ install:
cp -f ./hyprctl/hyprctl ${PREFIX}/bin
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_2K.png ${PREFIX}/share/hyprland
cp ./assets/wall_4K.png ${PREFIX}/share/hyprland
cp ./assets/wall_8K.png ${PREFIX}/share/hyprland
cp ./assets/wall_* ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/man/man1
install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
@@ -55,11 +53,14 @@ install:
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/share/pkgconfig
mkdir -p ${PREFIX}/share/xdg-desktop-portal
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cd subprojects/wlroots/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal/
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
cleaninstall:
@@ -83,7 +84,7 @@ fixwlr:
config:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false -Drenderers="gles2"
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
@@ -107,6 +108,7 @@ pluginenv:
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cd subprojects/wlroots/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
@@ -114,7 +116,7 @@ pluginenv:
configdebug:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=debug -Dwerror=false -Dexamples=false -Db_sanitize=address
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=debug -Dwerror=false -Dexamples=false -Drenderers="gles2" -Db_sanitize=address
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install

View File

@@ -16,7 +16,8 @@
Hyprland is a dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks.
It supports multiple layouts, fancy effects, has a very flexible IPC model allowing for a lot of customization, a powerful plugin system and more.
It provides the latest Wayland features, is highly customizable, has all the eyecandy, the most powerful plugins,
easy IPC, much more QoL stuff than other wlr-based compositors and more...
<br>
<br>
@@ -32,40 +33,28 @@ It supports multiple layouts, fancy effects, has a very flexible IPC model allow
<br>
</div>
# Notice
Hyprland is still in pretty early development compared to some other Wayland compositors.
Although Hyprland is pretty stable, it may have some bugs.
# Features
- All of the eyecandy: gradient borders, blur, animations, shadows and much more
- A lot of customization
- Much more QoL stuff than other wlr-based compositors
- Custom bezier curves for the best animations
- Powerful plugin support
- Easily expandable and readable codebase
- Plugin support
- Fast and active development
- Not scared to provide bleeding-edge features
- Config reloaded instantly upon saving
- Custom bezier curve based animations
- Dual Kawase blur
- Drop shadows
- Rounded corners
- Gradient borders
- Fully dynamic workspaces
- Two built-in layouts and more available as plugins
- Closely follows `wlroots-git`
- Global keybinds passed to your apps of choice
- A lot of customization
- Bundled wlroots
- Window/layer fade in/out
- Tiling/pseudotiling/floating/fullscreen windows
- Switching workspaces between window modes on the fly
- Special workspaces (scratchpads)
- Window/monitor rules
- Window groups (tabbed mode)
- Powerful window/monitor/layer rules
- Socket-based IPC
- `wlr_ext` workspaces protocol support
- Event system for bash scripts
- Full damage tracking
- Docks support
- Drawing tablet support
- Native IME + Input panels support
- Native IME and Input Panels Support
- and much more...
<br>
@@ -90,15 +79,6 @@ Although Hyprland is pretty stable, it may have some bugs.
<br>
<br>
# Stars Over Time
<br>
[![Stars Preview]][Stars]
<br>
<br>
</div>
# Special Thanks
@@ -147,9 +127,9 @@ Although Hyprland is pretty stable, it may have some bugs.
<!----------------------------------{ Images }--------------------------------->
[Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg
[Preview A]: https://i.ibb.co/SX7GbYR/winter-rice.png
[Preview B]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png
[Preview C]: https://i.imgur.com/pC6YF1Y.png
[Preview A]: https://cdn.discordapp.com/attachments/1091569872535814185/1107675866101723277/screenshot-summer.png
[Preview B]: https://i.ibb.co/SX7GbYR/winter-rice.png
[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png
<!----------------------------------{ Badges }--------------------------------->

View File

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

View File

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

BIN
assets/wall_anime2_2K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 KiB

BIN
assets/wall_anime2_4K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
assets/wall_anime2_8K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
assets/wall_anime_2K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

BIN
assets/wall_anime_4K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
assets/wall_anime_8K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc 2.9.2.1
.\"
.TH "Hyprland" "1" "03 Apr 2023" "" "Hyprland User Manual"
.TH "Hyprland" "1" "" "" "Hyprland User Manual"
.hy
.SH NAME
.PP

View File

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

View File

@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc 2.9.2.1
.\"
.TH "hyprctl" "1" "03 Apr 2023" "" "hyprctl User Manual"
.TH "hyprctl" "1" "" "" "hyprctl User Manual"
.hy
.SH NAME
.PP

View File

@@ -3,6 +3,6 @@
# and that you have ran `make protocols` in the hl dir.
all:
g++ -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/include" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/include" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/build/include" -std=c++23
$(CXX) -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b
clean:
rm ./examplePlugin.so

View File

@@ -1,6 +1,6 @@
#include "customDecoration.hpp"
#include "../../src/Window.hpp"
#include "../../src/Compositor.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
CCustomDecoration::CCustomDecoration(CWindow* pWindow) {

View File

@@ -2,7 +2,7 @@
#define WLR_USE_UNSTABLE
#include "../../src/render/decorations/IHyprWindowDecoration.hpp"
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
class CCustomDecoration : public IHyprWindowDecoration {
public:

View File

@@ -1,5 +1,5 @@
#include "customLayout.hpp"
#include "../../src/Compositor.hpp"
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {

View File

@@ -2,7 +2,7 @@
#define WLR_USE_UNSTABLE
#include "../../src/layout/IHyprLayout.hpp"
#include <hyprland/src/layout/IHyprLayout.hpp>
struct SWindowData {
CWindow* pWindow = nullptr;

View File

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

View File

@@ -2,8 +2,8 @@
#include "globals.hpp"
#include <src/Window.hpp>
#include <src/Compositor.hpp>
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "customLayout.hpp"
#include "customDecoration.hpp"
@@ -77,8 +77,12 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "processMouseDownNormal");
g_pMouseDownHook = HyprlandAPI::createFunctionHook(PHANDLE, METHODS[0].address, (void*)&hkProcessMouseDownNormal);
static auto* const PBORDERCOLOR = HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color");
// fancy notifications
HyprlandAPI::addNotificationV2(PHANDLE, {{"text", "Example hint"}, {"time", (uint64_t)10000}, {"color", CColor(0.2, 0.2, 0.9, 1.0)}, {"icon", ICON_HINT}});
HyprlandAPI::addNotificationV2(
PHANDLE,
{{"text", "Example hint, color " + std::to_string(PBORDERCOLOR->intValue)}, {"time", (uint64_t)10000}, {"color", CColor{PBORDERCOLOR->intValue}}, {"icon", ICON_HINT}});
// Enable our hooks
g_pFocusHook->hook();

View File

@@ -55,10 +55,12 @@ decoration {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
rounding = 10
blur = true
blur_size = 3
blur_passes = 1
blur_new_optimizations = true
blur {
enabled = true
size = 3
passes = 1
}
drop_shadow = true
shadow_range = 4
@@ -98,7 +100,7 @@ gestures {
}
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device:epic-mouse-v1 {
sensitivity = -0.5
}

47
flake.lock generated
View File

@@ -4,14 +4,17 @@
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1681065697,
"narHash": "sha256-QPzwwlGKX95tl6ZEshboZbEwwAXww6lNLdVYd6T9Mrc=",
"lastModified": 1691753796,
"narHash": "sha256-zOEwiWoXk3j3+EoF3ySUJmberFewWlagvewDRuWYAso=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "4d29e48433270a2af06b8bc711ca1fe5109746cd",
"rev": "0c2ce70625cb30aef199cb388f99e19a61a6ce03",
"type": "github"
},
"original": {
@@ -22,11 +25,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1683014792,
"narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
"lastModified": 1694767346,
"narHash": "sha256-5uH27SiVFUwsTsqC5rs3kS7pBoNhtoy9QfTP9BmknGk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
"rev": "ace5093e36ab1e95cb9463863491bee90d5a4183",
"type": "github"
},
"original": {
@@ -40,25 +43,42 @@
"inputs": {
"hyprland-protocols": "hyprland-protocols",
"nixpkgs": "nixpkgs",
"systems": "systems",
"wlroots": "wlroots",
"xdph": "xdph"
}
},
"systems": {
"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": 1682436395,
"narHash": "sha256-GGEjkQO9m7YLYIXIXM76HWdhjg4Ye+oafOtyaFAYKI4=",
"lastModified": 1695277534,
"narHash": "sha256-LEIUGXvKR5DYFQUTavC3yifcObvG4XZUUHfxXmu8nEM=",
"owner": "wlroots",
"repo": "wlroots",
"rev": "6830bfc17fd94709e2cdd4da0af989f102a26e59",
"rev": "98a745d926d8048bc30aef11b421df207a01c279",
"type": "gitlab"
},
"original": {
"host": "gitlab.freedesktop.org",
"owner": "wlroots",
"repo": "wlroots",
"rev": "98a745d926d8048bc30aef11b421df207a01c279",
"type": "gitlab"
}
},
@@ -69,14 +89,17 @@
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1682439384,
"narHash": "sha256-zHDa8LCZs05TZHQSIZ3ucwyMPglBGHcqTBzfkLjYXTM=",
"lastModified": 1694628480,
"narHash": "sha256-Qg9hstRw0pvjGu5hStkr2UX1D73RYcQ9Ns/KnZMIm9w=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "c0e233955568fbea4e859336f6d3d14d51294d7c",
"rev": "8f45a6435069b9e24ebd3160eda736d7a391cbf2",
"type": "github"
},
"original": {

View File

@@ -4,19 +4,28 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# <https://github.com/nix-systems/nix-systems>
systems.url = "github:nix-systems/default-linux";
wlroots = {
url = "gitlab:wlroots/wlroots?host=gitlab.freedesktop.org";
type = "gitlab";
host = "gitlab.freedesktop.org";
owner = "wlroots";
repo = "wlroots";
rev = "98a745d926d8048bc30aef11b421df207a01c279";
flake = false;
};
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
xdph = {
url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprland-protocols.follows = "hyprland-protocols";
};
};
@@ -24,38 +33,23 @@
outputs = inputs @ {
self,
nixpkgs,
systems,
...
}: let
lib = nixpkgs.lib.extend (import ./nix/lib.nix);
genSystems = lib.genAttrs [
# Add more systems if they are supported
"aarch64-linux"
"x86_64-linux"
];
pkgsFor = genSystems (system:
inherit (nixpkgs) lib;
eachSystem = lib.genAttrs (import systems);
pkgsFor = eachSystem (system:
import nixpkgs {
inherit system;
overlays = [
self.overlays.hyprland-packages
self.overlays.wlroots-hyprland
inputs.hyprland-protocols.overlays.default
localSystem = system;
overlays = with self.overlays; [
hyprland-packages
hyprland-extras
];
});
in {
overlays =
(import ./nix/overlays.nix {inherit self lib inputs;})
// {
default =
lib.mkJoinedOverlays
(with self.overlays; [
hyprland-packages
hyprland-extras
wlroots-hyprland
]);
};
overlays = import ./nix/overlays.nix {inherit self lib inputs;};
checks = genSystems (system:
checks = eachSystem (system:
(lib.filterAttrs
(n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n))
self.packages.${system})
@@ -63,16 +57,30 @@
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
});
packages = genSystems (system:
(self.overlays.default pkgsFor.${system} pkgsFor.${system})
// {
default = self.packages.${system}.hyprland;
});
packages = eachSystem (system: {
default = self.packages.${system}.hyprland;
inherit
(pkgsFor.${system})
# hyprland-packages
hyprland
hyprland-unwrapped
hyprland-debug
hyprland-nvidia
# hyprland-extras
xdg-desktop-portal-hyprland
# dependencies
hyprland-protocols
wlroots-hyprland
udis86
;
});
devShells = genSystems (system: {
default = pkgsFor.${system}.mkShell {
devShells = eachSystem (system: {
default = pkgsFor.${system}.mkShell.override {
stdenv = pkgsFor.${system}.gcc13Stdenv;
} {
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [cmake];
nativeBuildInputs = with pkgsFor.${system}; [cmake python3];
buildInputs = [self.packages.${system}.wlroots-hyprland];
inputsFrom = [
self.packages.${system}.wlroots-hyprland
@@ -81,7 +89,7 @@
};
});
formatter = genSystems (system: pkgsFor.${system}.alejandra);
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
nixosModules.default = import ./nix/module.nix inputs;
homeManagerModules.default = import ./nix/hm-module.nix self;

View File

@@ -11,12 +11,17 @@
#include <unistd.h>
#include <ranges>
#include <algorithm>
#include <signal.h>
#include <format>
#include <iostream>
#include <string>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <filesystem>
#include <stdarg.h>
const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args]
@@ -45,14 +50,63 @@ commands:
plugin
notify
globalshortcuts
instances
flags:
-j -> output in JSON
--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)
)#";
#define PAD
std::string instanceSignature;
struct SInstanceData {
std::string id;
uint64_t time;
uint64_t pid;
std::string wlSocket;
bool valid = true;
};
std::vector<SInstanceData> instances() {
std::vector<SInstanceData> result;
for (const auto& el : std::filesystem::directory_iterator("/tmp/hypr")) {
if (el.is_directory())
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->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1));
// read file
std::ifstream ifs(el.path().string());
int i = 0;
for (std::string line; std::getline(ifs, line); ++i) {
if (i == 0) {
data->pid = std::stoull(line);
} else if (i == 1) {
data->wlSocket = line;
} else
break;
}
ifs.close();
}
std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; });
return result;
}
void request(std::string arg, int minArgs = 0) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -68,20 +122,15 @@ void request(std::string arg, int minArgs = 0) {
return;
}
// get the instance signature
auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!instanceSig) {
if (instanceSignature.empty()) {
std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)";
return;
}
std::string instanceSigStr = std::string(instanceSig);
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
std::string socketPath = "/tmp/hypr/" + instanceSignature + "/.socket.sock";
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
@@ -131,20 +180,15 @@ void requestHyprpaper(std::string arg) {
return;
}
// get the instance signature
auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!instanceSig) {
if (instanceSignature.empty()) {
std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)";
return;
}
std::string instanceSigStr = std::string(instanceSig);
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.hyprpaper.sock";
std::string socketPath = "/tmp/hypr/" + instanceSignature + "/.hyprpaper.sock";
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
@@ -153,6 +197,9 @@ void requestHyprpaper(std::string arg) {
return;
}
arg = arg.substr(arg.find_first_of('/') + 1); // strip flags
arg = arg.substr(arg.find_first_of(' ') + 1); // strip "hyprpaper"
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
if (sizeWritten < 0) {
@@ -174,92 +221,42 @@ void requestHyprpaper(std::string arg) {
std::cout << std::string(buffer);
}
int dispatchRequest(int argc, char** argv) {
if (argc < 3) {
std::cout << "Usage: hyprctl dispatch <dispatcher> <arg>\n\
Execute a hyprland keybind dispatcher with the given argument";
return 1;
}
std::string rq = "/dispatch";
for (int i = 2; i < argc; i++) {
if (!strcmp(argv[i], "--"))
continue;
rq += " " + std::string(argv[i]);
}
request(rq);
return 0;
}
int keywordRequest(int argc, char** argv) {
if (argc < 4) {
std::cout << "Usage: hyprctl keyword <keyword> <arg>\n\
Execute a hyprland keyword with the given argument";
return 1;
}
std::string rq = "/keyword";
for (int i = 2; i < argc; i++)
rq += " " + std::string(argv[i]);
request(rq);
return 0;
}
int hyprpaperRequest(int argc, char** argv) {
if (argc < 4) {
std::cout << "Usage: hyprctl hyprpaper <command> <arg>\n\
Execute a hyprpaper command with the given argument";
return 1;
}
std::string rq = std::string(argv[2]) + " " + std::string(argv[3]);
requestHyprpaper(rq);
return 0;
}
int setcursorRequest(int argc, char** argv) {
if (argc < 4) {
std::cout << "Usage: hyprctl setcursor <theme> <size>\n\
Sets the cursor theme for everything except GTK and reloads the cursor";
return 1;
}
std::string rq = "setcursor ";
for (size_t i = 2; i < argc; ++i)
rq += std::string(argv[i]) + " ";
rq.pop_back();
request(rq);
return 0;
}
int outputRequest(int argc, char** argv) {
if (argc < 4) {
std::cout << "Usage: hyprctl output <mode> <name>\n\
creates / destroys a fake output\n\
with create, name is the backend name to use (available: auto, x11, wayland, headless)\n\
with destroy, name is the output name to destroy";
return 1;
}
std::string rq = "output " + std::string(argv[2]) + " " + std::string(argv[3]);
request(rq);
return 0;
}
void batchRequest(std::string arg) {
std::string rq = "[[BATCH]]" + arg.substr(arg.find_first_of(" ") + 1);
request(rq);
}
void instancesRequest(bool json) {
std::string result = "";
// gather instance data
std::vector<SInstanceData> inst = instances();
if (!json) {
for (auto& el : inst) {
result += std::format("instance {}:\n\ttime: {}\n\tpid: {}\n\twl socket: {}\n\n", el.id, el.time, el.pid, el.wlSocket);
}
} else {
result += '[';
for (auto& el : inst) {
result += std::format(R"#(
{{
"instance": "{}",
"time": {},
"pid": {},
"wl_socket": "{}"
}},)#",
el.id, el.time, el.pid, el.wlSocket);
}
result.pop_back();
result += "\n]";
}
std::cout << result << "\n";
}
std::deque<std::string> splitArgs(int argc, char** argv) {
std::deque<std::string> result;
@@ -284,9 +281,11 @@ int main(int argc, char** argv) {
return 1;
}
std::string fullRequest = "";
std::string fullArgs = "";
const auto ARGS = splitArgs(argc, argv);
std::string fullRequest = "";
std::string fullArgs = "";
const auto ARGS = splitArgs(argc, argv);
bool json = false;
std::string overrideInstance = "";
for (auto i = 0; i < ARGS.size(); ++i) {
if (ARGS[i] == "--") {
@@ -298,8 +297,18 @@ int main(int argc, char** argv) {
// parse
if (ARGS[i] == "-j" && !fullArgs.contains("j")) {
fullArgs += "j";
json = true;
} else if (ARGS[i] == "--batch") {
fullRequest = "--batch ";
} else if (ARGS[i] == "--instance" || ARGS[i] == "-i") {
++i;
if (i >= ARGS.size()) {
printf("%s\n", USAGE.c_str());
return 1;
}
overrideInstance = ARGS[i];
} else {
printf("%s\n", USAGE.c_str());
return 1;
@@ -320,6 +329,35 @@ int main(int argc, char** argv) {
fullRequest = fullArgs + "/" + fullRequest;
if (overrideInstance.contains("_"))
instanceSignature = overrideInstance;
else if (!overrideInstance.empty()) {
if (!isNumber(overrideInstance, false)) {
std::cout << "instance invalid\n";
return 1;
}
const auto INSTANCENO = std::stoi(overrideInstance);
const auto INSTANCES = instances();
if (INSTANCENO < 0 || INSTANCENO >= INSTANCES.size()) {
std::cout << "no such instance\n";
return 1;
}
instanceSignature = INSTANCES[INSTANCENO].id;
} else {
const auto ISIG = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!ISIG) {
std::cout << "HYPRLAND_INSTANCE_SIGNATURE not set! (is hyprland running?)";
return 1;
}
instanceSignature = ISIG;
}
int exitStatus = 0;
if (fullRequest.contains("/--batch"))
@@ -356,6 +394,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/globalshortcuts"))
request(fullRequest);
else if (fullRequest.contains("/instances"))
instancesRequest(json);
else if (fullRequest.contains("/switchxkblayout"))
request(fullRequest, 2);
else if (fullRequest.contains("/seterror"))
@@ -367,15 +407,15 @@ int main(int argc, char** argv) {
else if (fullRequest.contains("/notify"))
request(fullRequest, 2);
else if (fullRequest.contains("/output"))
exitStatus = outputRequest(argc, argv);
request(fullRequest, 2);
else if (fullRequest.contains("/setcursor"))
exitStatus = setcursorRequest(argc, argv);
request(fullRequest, 1);
else if (fullRequest.contains("/dispatch"))
exitStatus = dispatchRequest(argc, argv);
request(fullRequest, 1);
else if (fullRequest.contains("/keyword"))
exitStatus = keywordRequest(argc, argv);
request(fullRequest, 2);
else if (fullRequest.contains("/hyprpaper"))
exitStatus = hyprpaperRequest(argc, argv);
requestHyprpaper(fullRequest);
else if (fullRequest.contains("/--help"))
printf("%s", USAGE.c_str());
else {

View File

@@ -39,7 +39,7 @@ add_project_arguments(
],
language: 'cpp')
wlroots = subproject('wlroots', default_options: ['examples=false'])
wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
@@ -96,5 +96,5 @@ import('pkgconfig').generate(
url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files',
install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols'],
subdirs: ['', 'hyprland/protocols', 'hyprland/wlroots'],
)

View File

@@ -2,8 +2,10 @@
lib,
stdenv,
pkg-config,
makeWrapper,
meson,
ninja,
binutils,
cairo,
git,
hyprland-protocols,
@@ -24,102 +26,109 @@
xcbutilwm,
xwayland,
debug ? false,
enableNvidiaPatches ? false,
enableXWayland ? true,
hidpiXWayland ? false,
legacyRenderer ? false,
nvidiaPatches ? false,
withSystemd ? true,
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
wrapRuntimeDeps ? true,
version ? "git",
commit,
}: let
assertXWayland = lib.assertMsg (hidpiXWayland -> enableXWayland) ''
Hyprland: cannot have hidpiXWayland when enableXWayland is false.
'';
in
assert assertXWayland;
stdenv.mkDerivation {
pname = "hyprland" + lib.optionalString debug "-debug";
inherit version;
# deprecated flags
nvidiaPatches ? false,
hidpiXWayland ? false,
}:
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been renamed `enableNvidiaPatches`";
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
stdenv.mkDerivation {
pname = "hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}${lib.optionalString debug "-debug"}";
inherit version;
src = lib.cleanSourceWith {
filter = name: type: let
baseName = baseNameOf (toString name);
in
! (
lib.hasSuffix ".nix" baseName
);
src = lib.cleanSource ../.;
};
src = lib.cleanSourceWith {
filter = name: type: let
baseName = baseNameOf (toString name);
in
! (lib.hasSuffix ".nix" baseName);
src = lib.cleanSource ../.;
};
nativeBuildInputs = [
jq
meson
ninja
pkg-config
];
nativeBuildInputs = [
jq
meson
ninja
pkg-config
makeWrapper
wayland-scanner
];
outputs = [
"out"
"man"
"dev"
];
outputs = [
"out"
"man"
"dev"
];
buildInputs =
[
git
cairo
hyprland-protocols
libdrm
libinput
libxkbcommon
mesa
pango
udis86
wayland
wayland-protocols
wayland-scanner
pciutils
(wlroots.override {inherit enableXWayland hidpiXWayland nvidiaPatches;})
]
++ lib.optionals enableXWayland [libxcb xcbutilwm xwayland]
++ lib.optionals withSystemd [systemd];
buildInputs =
[
git
cairo
hyprland-protocols
libdrm
libinput
libxkbcommon
mesa
pango
udis86
wayland
wayland-protocols
pciutils
(wlroots.override {inherit enableNvidiaPatches;})
]
++ lib.optionals enableXWayland [libxcb xcbutilwm xwayland]
++ lib.optionals withSystemd [systemd];
mesonBuildType =
if debug
then "debug"
else "release";
mesonBuildType =
if debug
then "debug"
else "release";
mesonFlags = builtins.concatLists [
["-Dauto_features=disabled"]
(lib.optional enableXWayland "-Dxwayland=enabled")
(lib.optional legacyRenderer "-Dlegacy_renderer=enabled")
(lib.optional withSystemd "-Dsystemd=enabled")
];
mesonFlags = builtins.concatLists [
["-Dauto_features=disabled"]
(lib.optional enableXWayland "-Dxwayland=enabled")
(lib.optional legacyRenderer "-Dlegacy_renderer=enabled")
(lib.optional withSystemd "-Dsystemd=enabled")
];
patches = [
# make meson use the provided wlroots instead of the git submodule
./meson-build.patch
];
patches = [
# make meson use the provided wlroots instead of the git submodule
./patches/meson-build.patch
];
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
substituteInPlace meson.build \
--replace "@GIT_COMMIT_HASH@" '${commit}' \
--replace "@GIT_DIRTY@" '${
if commit == ""
then "dirty"
else ""
}'
'';
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
substituteInPlace meson.build \
--replace "@GIT_COMMIT_HASH@" '${commit}' \
--replace "@GIT_DIRTY@" '${
if commit == ""
then "dirty"
else ""
}'
'';
passthru.providedSessions = ["hyprland"];
postInstall = ''
ln -s ${wlroots}/include/wlr $dev/include/hyprland/wlroots
${lib.optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [binutils pciutils]}
''}
'';
meta = with lib; {
homepage = "https://github.com/vaxerski/Hyprland";
description = "A dynamic tiling Wayland compositor that doesn't sacrifice on its looks";
license = licenses.bsd3;
platforms = platforms.linux;
mainProgram = "Hyprland";
};
}
passthru.providedSessions = ["hyprland"];
meta = with lib; {
homepage = "https://github.com/vaxerski/Hyprland";
description = "A dynamic tiling Wayland compositor that doesn't sacrifice on its looks";
license = licenses.bsd3;
platforms = platforms.linux;
mainProgram = "Hyprland";
};
}

View File

@@ -7,10 +7,11 @@ self: {
cfg = config.wayland.windowManager.hyprland;
defaultHyprlandPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable;
hidpiXWayland = cfg.xwayland.hidpi;
inherit (cfg) nvidiaPatches;
inherit (cfg) enableNvidiaPatches;
};
in {
disabledModules = ["services/window-managers/hyprland.nix"];
meta.maintainers = [lib.maintainers.fufexan];
options.wayland.windowManager.hyprland = {
@@ -34,13 +35,12 @@ in {
defaultText = lib.literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.wayland.windowManager.hyprland.xwayland.enable;
hidpiXWayland = config.wayland.windowManager.hyprland.xwayland.hidpi;
inherit (config.wayland.windowManager.hyprland) nvidiaPatches;
inherit (config.wayland.windowManager.hyprland) enableNvidiaPatches;
}
'';
description = lib.mdDoc ''
Hyprland package to use. Will override the 'xwayland' and
'nvidiaPatches' options.
'enableNvidiaPatches' options.
Defaults to the one provided by the flake. Set it to
{package}`pkgs.hyprland` to use the one provided by nixpkgs or
@@ -55,7 +55,7 @@ in {
type = with lib.types; listOf (either package path);
default = [];
description = lib.mdDoc ''
List of hyprlad plugins to use. Can either be packages or
List of Hyprland plugins to use. Can either be packages or
absolute plugin paths.
'';
};
@@ -65,7 +65,7 @@ in {
default = pkgs.stdenv.isLinux;
description = lib.mdDoc ''
Whether to enable {file}`hyprland-session.target` on
hyprland startup. This links to {file}`graphical-session.target`.
Hyprland startup. This links to {file}`graphical-session.target`.
Some important environment variables will be imported to systemd
and dbus user environment before reaching the target, including
- {env}`DISPLAY`
@@ -84,19 +84,9 @@ in {
'';
};
xwayland = {
enable = lib.mkEnableOption (lib.mdDoc "XWayland") // {default = true;};
hidpi =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Enable HiDPI XWayland, based on [XWayland MR 733](https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/733).
See <https://wiki.hyprland.org/Nix/Options-Overrides/#xwayland-hidpi> for more info.
'';
};
};
xwayland.enable = lib.mkEnableOption (lib.mdDoc "XWayland") // {default = true;};
nvidiaPatches = lib.mkEnableOption (lib.mdDoc "patching wlroots for better Nvidia support.");
enableNvidiaPatches = lib.mkEnableOption (lib.mdDoc "patching wlroots for better Nvidia support.");
extraConfig = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
@@ -117,10 +107,14 @@ in {
config = lib.mkIf cfg.enable {
warnings =
if (cfg.systemdIntegration || cfg.plugins != []) && cfg.extraConfig == null then
[ ''You have enabled hyprland.systemdIntegration or listed plugins in hyprland.plugins.
Your hyprland config will be linked by home manager.
Set hyprland.extraConfig or unset hyprland.systemdIntegration and hyprland.plugins to remove this warning.'' ]
if (cfg.systemdIntegration || cfg.plugins != []) && cfg.extraConfig == null
then [
''
You have enabled hyprland.systemdIntegration or listed plugins in hyprland.plugins.
Your Hyprland config will be linked by home manager.
Set hyprland.extraConfig or unset hyprland.systemdIntegration and hyprland.plugins to remove this warning.
''
]
else [];
home.packages =
@@ -136,9 +130,17 @@ in {
exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && systemctl --user start hyprland-session.target
'')
+ lib.concatStrings (builtins.map (entry: let
plugin = if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry;
in "plugin = ${plugin}\n") cfg.plugins)
+ (if cfg.extraConfig != null then cfg.extraConfig else "");
plugin =
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in "plugin = ${plugin}\n")
cfg.plugins)
+ (
if cfg.extraConfig != null
then cfg.extraConfig
else ""
);
onChange = let
hyprlandPackage =
@@ -159,7 +161,7 @@ in {
systemd.user.targets.hyprland-session = lib.mkIf cfg.systemdIntegration {
Unit = {
Description = "hyprland compositor session";
Description = "Hyprland compositor session";
Documentation = ["man:systemd.special(7)"];
BindsTo = ["graphical-session.target"];
Wants = ["graphical-session-pre.target"];
@@ -167,4 +169,9 @@ in {
};
};
};
imports = [
(lib.mkRemovedOptionModule ["wayland" "windowManager" "hyprland" "xwayland" "hidpi"]
"Support for this option has been removed. Refer to https://wiki.hyprland.org/Configuring/XWayland for more info")
];
}

View File

@@ -1,8 +0,0 @@
final: prev: let
lib = final;
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in prev // {
inherit mkJoinedOverlays;
}

View File

@@ -2,15 +2,15 @@ inputs: {
config,
lib,
pkgs,
options,
...
}:
with lib; let
cfg = config.programs.hyprland;
inherit (pkgs.stdenv.hostPlatform) system;
defaultHyprlandPackage = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable;
hidpiXWayland = cfg.xwayland.hidpi;
inherit (cfg) nvidiaPatches;
finalPortalPackage = cfg.portalPackage.override {
hyprland = cfg.finalPackage;
};
in {
# disables Nixpkgs Hyprland module to avoid conflicts
@@ -30,69 +30,70 @@ in {
'';
};
package = mkOption {
type = types.path;
default = defaultHyprlandPackage;
defaultText = literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.programs.hyprland.xwayland.enable;
hidpiXWayland = config.programs.hyprland.xwayland.hidpi;
inherit (config.programs.hyprland) nvidiaPatches;
}
'';
example = literalExpression "pkgs.hyprland";
package = mkPackageOptionMD inputs.self.packages.${system} "hyprland" { };
finalPackage = mkOption {
type = types.package;
readOnly = true;
default = cfg.package.override {
enableXWayland = cfg.xwayland.enable;
enableNvidiaPatches = cfg.enableNvidiaPatches;
};
defaultText =
literalExpression
"`programs.hyprland.package` with applied configuration";
description = mdDoc ''
The Hyprland package to use.
Setting this option will make {option}`programs.hyprland.xwayland` and
{option}`programs.hyprland.nvidiaPatches` not work.
The Hyprland package after applying configuration.
'';
};
xwayland = {
enable = mkEnableOption (mdDoc "XWayland") // {default = true;};
hidpi =
mkEnableOption null
// {
description = mdDoc ''
Enable HiDPI XWayland, based on [XWayland MR 733](https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/733).
See <https://wiki.hyprland.org/Nix/Options-Overrides/#xwayland-hidpi> for more info.
'';
};
};
portalPackage = mkPackageOptionMD inputs.xdph.packages.${system} "xdg-desktop-portal-hyprland" {};
nvidiaPatches = mkEnableOption (mdDoc "patching wlroots for better Nvidia support");
xwayland.enable = mkEnableOption (mdDoc "support for XWayland") // {default = true;};
enableNvidiaPatches =
mkEnableOption null
// {
description = mdDoc "Whether to apply patches to wlroots for better Nvidia support.";
};
};
config = mkIf cfg.enable {
environment = {
systemPackages = [cfg.package];
environment.systemPackages = [cfg.finalPackage];
sessionVariables = {
NIXOS_OZONE_WL = mkDefault "1";
};
};
# NixOS changed the name of this attribute between NixOS 23.05 and
# 23.11
fonts = if builtins.hasAttr "enableDefaultPackages" options.fonts
then {enableDefaultPackages = mkDefault true;}
else {enableDefaultFonts = mkDefault true;};
fonts.enableDefaultFonts = mkDefault true;
hardware.opengl.enable = mkDefault true;
programs = {
dconf.enable = mkDefault true;
xwayland.enable = mkDefault true;
xwayland.enable = mkDefault cfg.xwayland.enable;
};
security.polkit.enable = true;
services.xserver.displayManager.sessionPackages = [cfg.package];
services.xserver.displayManager.sessionPackages = [cfg.finalPackage];
xdg.portal = {
enable = mkDefault true;
extraPortals = lib.mkIf (cfg.package != null) [
(inputs.xdph.packages.${pkgs.stdenv.hostPlatform.system}.xdg-desktop-portal-hyprland.override {
hyprland-share-picker = inputs.xdph.packages.${pkgs.stdenv.hostPlatform.system}.hyprland-share-picker.override {
hyprland = cfg.package;
};
})
];
extraPortals = [finalPortalPackage];
};
};
imports = with lib; [
(
mkRemovedOptionModule
["programs" "hyprland" "xwayland" "hidpi"]
"XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland"
)
(
mkRenamedOptionModule
["programs" "hyprland" "nvidiaPatches"]
["programs" "hyprland" "enableNvidiaPatches"]
)
];
}

View File

@@ -10,66 +10,62 @@
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in {
# Packages for variations of Hyprland, and its dependencies.
hyprland-packages = final: prev: {
hyprland = final.callPackage ./default.nix {
version =
props.version
+ "+date="
+ (mkDate (self.lastModifiedDate or "19700101"))
+ "_"
+ (self.shortRev or "dirty");
wlroots = final.wlroots-hyprland;
commit = self.rev or "";
inherit (final) udis86 hyprland-protocols;
};
# Contains what a user is most likely to care about:
# Hyprland itself, XDPH and the Share Picker.
default = mkJoinedOverlays (with self.overlays; [
hyprland-packages
hyprland-extras
]);
hyprland-debug = final.hyprland.override {debug = true;};
hyprland-hidpi = final.hyprland.override {hidpiXWayland = true;};
hyprland-nvidia = final.hyprland.override {nvidiaPatches = true;};
hyprland-no-hidpi =
builtins.trace
"hyprland-no-hidpi was removed. Please use the default package."
final.hyprland;
udis86 = final.callPackage ./udis86.nix {};
};
# Packages for variations of Hyprland, dependencies included.
hyprland-packages = mkJoinedOverlays [
# Dependencies
inputs.hyprland-protocols.overlays.default
self.overlays.wlroots-hyprland
self.overlays.udis86
# Hyprland packages themselves
(final: prev: {
hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${mkDate (self.lastModifiedDate or "19700101")}_${self.shortRev or "dirty"}";
wlroots = final.wlroots-hyprland;
commit = self.rev or "";
inherit (final) udis86 hyprland-protocols;
};
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
hyprland-debug = final.hyprland.override {debug = true;};
hyprland-nvidia = final.hyprland.override {enableNvidiaPatches = true;};
hyprland-hidpi =
builtins.trace ''
hyprland-hidpi was removed. Please use the hyprland package.
For more information, refer to https://wiki.hyprland.org/Configuring/XWayland.
''
final.hyprland;
})
];
# Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility.
hyprland-extras = lib.mkJoinedOverlays [
# Include any inputs' specific overlays whose attributes should
# be re-exported by the Hyprland flake.
#
inputs.xdph.overlays.default
# Provides:
# - xdg-desktop-portal-hyprland
# - hyprland-share-picker
#
# Attributes for `hyprland-extras` defined by this flake can
# go in the oberlay below.
(final: prev: {
waybar-hyprland = prev.waybar.overrideAttrs (old: {
postPatch = ''
# use hyprctl to switch workspaces
sed -i 's/zext_workspace_handle_v1_activate(workspace_handle_);/const std::string command = "hyprctl dispatch workspace " + name_;\n\tsystem(command.c_str());/g' src/modules/wlr/workspace_manager.cpp
'';
mesonFlags = old.mesonFlags ++ ["-Dexperimental=true"];
});
})
hyprland-extras = mkJoinedOverlays [
inputs.xdph.overlays.xdg-desktop-portal-hyprland
];
udis86 = final: prev: {
udis86 = final.callPackage ./udis86.nix {};
};
# Patched version of wlroots for Hyprland.
# It is under a new package name so as to not conflict with
# the standard version in nixpkgs.
wlroots-hyprland = final: prev: {
wlroots-hyprland = final.callPackage ./wlroots.nix {
version =
mkDate (inputs.wlroots.lastModifiedDate or "19700101")
+ "_"
+ (inputs.wlroots.shortRev or "dirty");
version = "${mkDate (inputs.wlroots.lastModifiedDate or "19700101")}_${inputs.wlroots.shortRev or "dirty"}";
src = inputs.wlroots;
libdisplay-info = prev.libdisplay-info.overrideAttrs (old: {
version = "0.1.1+date=2023-03-02";
src = final.fetchFromGitLab {
@@ -80,6 +76,7 @@ in {
sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4=";
};
});
libliftoff = prev.libliftoff.overrideAttrs (old: {
version = "0.5.0-dev";
src = final.fetchFromGitLab {

View File

@@ -18,7 +18,7 @@ index f3802553..6a924a79 100644
],
language: 'cpp')
-wlroots = subproject('wlroots', default_options: ['examples=false'])
-wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
-have_xwlr = wlroots.get_variable('features').get('xwayland')
-xcb_dep = dependency('xcb', required: get_option('xwayland'))
-

View File

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

View File

@@ -1,21 +1,19 @@
#!/usr/bin/env -S nix shell nixpkgs#gawk nixpkgs#git nixpkgs#moreutils nixpkgs#jq nixpkgs#ripgrep -c bash
#!/usr/bin/env -S nix shell nixpkgs#jq -c bash
set -ex
# Update inputs when the Mesa version is outdated. We don't want
# incompatibilities between the user's system and Hyprland.
# get wlroots revision from submodule
SUB_REV=$(git submodule status | rg wlroots | awk '{ print substr($1,2)}')
# and from lockfile
CRT_REV=$(jq <flake.lock '.nodes.wlroots.locked.rev' -r)
# get the current Nixpkgs revision
REV=$(jq <flake.lock '.nodes.nixpkgs.locked.rev' -r)
# check versions for current and remote nixpkgs' mesa
CRT_VER=$(nix eval --raw github:nixos/nixpkgs/"$REV"#mesa.version)
NEW_VER=$(nix eval --raw github:nixos/nixpkgs/nixos-unstable#mesa.version)
if [ "$CRT_VER" != "$NEW_VER" ]; then
echo "Updating Mesa $CRT_VER -> $NEW_VER and flake inputs"
if [ "$SUB_REV" != "$CRT_REV" ]; then
# update inputs to latest versions
nix flake update
# update wlroots to submodule revision
nix flake lock --override-input wlroots "gitlab:wlroots/wlroots/$SUB_REV?host=gitlab.freedesktop.org"
# remove "dirty" mark from lockfile
jq <flake.lock 'del(.nodes.wlroots.original.rev)' | sponge flake.lock
else
echo "wlroots is up to date!"
echo "nixpkgs is up to date!"
fi

17
nix/update-wlroots.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env -S nix shell nixpkgs#gawk nixpkgs#git nixpkgs#gnused nixpkgs#ripgrep -c bash
# get wlroots revision from submodule
SUB_REV=$(git submodule status | rg wlroots | awk '{ print substr($1,2) }')
# and from lockfile
CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }')
if [ "$SUB_REV" != "$CRT_REV" ]; then
echo "Updating wlroots..."
# update wlroots to submodule revision
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap
nix flake lock
echo "wlroots: $CRT_REV -> $SUB_REV"
else
echo "wlroots is up to date!"
fi

View File

@@ -1,164 +0,0 @@
diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h
index 3d540522..1c5a2e37 100644
--- a/include/xwayland/xwm.h
+++ b/include/xwayland/xwm.h
@@ -88,6 +88,7 @@ enum atom_name {
DND_ACTION_PRIVATE,
NET_CLIENT_LIST,
NET_CLIENT_LIST_STACKING,
+ XWAYLAND_GLOBAL_OUTPUT_SCALE,
ATOM_LAST // keep last
};
@@ -96,6 +97,7 @@ struct wlr_xwm {
struct wl_event_source *event_source;
struct wlr_seat *seat;
uint32_t ping_timeout;
+ uint32_t scale;
xcb_atom_t atoms[ATOM_LAST];
xcb_connection_t *xcb_conn;
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index 5f857f24..21584ebd 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -19,6 +19,14 @@
#include <xcb/xfixes.h>
#include "xwayland/xwm.h"
+static int32_t scale(struct wlr_xwm *xwm, uint32_t val) {
+ return val * xwm->scale;
+}
+
+static int32_t unscale(struct wlr_xwm *xwm, uint32_t val) {
+ return (val + xwm->scale/2) / xwm->scale;
+}
+
static const char *const atom_map[ATOM_LAST] = {
[WL_SURFACE_ID] = "WL_SURFACE_ID",
[WL_SURFACE_SERIAL] = "WL_SURFACE_SERIAL",
@@ -90,6 +98,7 @@ static const char *const atom_map[ATOM_LAST] = {
[DND_ACTION_PRIVATE] = "XdndActionPrivate",
[NET_CLIENT_LIST] = "_NET_CLIENT_LIST",
[NET_CLIENT_LIST_STACKING] = "_NET_CLIENT_LIST_STACKING",
+ [XWAYLAND_GLOBAL_OUTPUT_SCALE] = "_XWAYLAND_GLOBAL_OUTPUT_SCALE",
};
#define STARTUP_INFO_REMOVE_PREFIX "remove: ID="
@@ -965,8 +974,8 @@ static void xwm_handle_create_notify(struct wlr_xwm *xwm,
return;
}
- xwayland_surface_create(xwm, ev->window, ev->x, ev->y,
- ev->width, ev->height, ev->override_redirect);
+ xwayland_surface_create(xwm, ev->window, unscale(xwm, ev->x), unscale(xwm, ev->y),
+ unscale(xwm, ev->width), unscale(xwm, ev->height), ev->override_redirect);
}
static void xwm_handle_destroy_notify(struct wlr_xwm *xwm,
@@ -997,10 +1006,10 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm,
struct wlr_xwayland_surface_configure_event wlr_event = {
.surface = surface,
- .x = mask & XCB_CONFIG_WINDOW_X ? ev->x : surface->x,
- .y = mask & XCB_CONFIG_WINDOW_Y ? ev->y : surface->y,
- .width = mask & XCB_CONFIG_WINDOW_WIDTH ? ev->width : surface->width,
- .height = mask & XCB_CONFIG_WINDOW_HEIGHT ? ev->height : surface->height,
+ .x = mask & XCB_CONFIG_WINDOW_X ? unscale(xwm, ev->x) : surface->x,
+ .y = mask & XCB_CONFIG_WINDOW_Y ? unscale(xwm, ev->y) : surface->y,
+ .width = mask & XCB_CONFIG_WINDOW_WIDTH ? unscale(xwm, ev->width) : surface->width,
+ .height = mask & XCB_CONFIG_WINDOW_HEIGHT ? unscale(xwm, ev->height) : surface->height,
.mask = mask,
};
@@ -1015,14 +1024,14 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm,
}
bool geometry_changed =
- (xsurface->x != ev->x || xsurface->y != ev->y ||
- xsurface->width != ev->width || xsurface->height != ev->height);
+ (xsurface->x != unscale(xwm, ev->x) || xsurface->y != unscale(xwm, ev->y) ||
+ xsurface->width != unscale(xwm, ev->width) || xsurface->height != unscale(xwm, ev->height));
if (geometry_changed) {
- xsurface->x = ev->x;
- xsurface->y = ev->y;
- xsurface->width = ev->width;
- xsurface->height = ev->height;
+ xsurface->x = unscale(xwm, ev->x);
+ xsurface->y = unscale(xwm, ev->y);
+ xsurface->width = unscale(xwm, ev->width);
+ xsurface->height = unscale(xwm, ev->height);
}
if (xsurface->override_redirect != ev->override_redirect) {
@@ -1133,6 +1142,20 @@ static void xwm_handle_property_notify(struct wlr_xwm *xwm,
xcb_property_notify_event_t *ev) {
struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window);
if (xsurface == NULL) {
+ if (ev->atom == xwm->atoms[XWAYLAND_GLOBAL_OUTPUT_SCALE]) {
+ xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 0,
+ ev->window, ev->atom, XCB_ATOM_ANY, 0, 2048);
+ xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn,
+ cookie, NULL);
+ if (reply == NULL) {
+ return;
+ }
+ if (reply->type == XCB_ATOM_CARDINAL) {
+ xwm->scale = *(uint32_t*)xcb_get_property_value(reply);
+ }
+ free(reply);
+ }
+
return;
}
@@ -1760,16 +1783,17 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
int old_w = xsurface->width;
int old_h = xsurface->height;
+ struct wlr_xwm *xwm = xsurface->xwm;
+
xsurface->x = x;
xsurface->y = y;
xsurface->width = width;
xsurface->height = height;
- struct wlr_xwm *xwm = xsurface->xwm;
uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_BORDER_WIDTH;
- uint32_t values[] = {x, y, width, height, 0};
+ uint32_t values[] = {scale(xwm, x), scale(xwm, y), scale(xwm, width), scale(xwm, height), 0};
xcb_configure_window(xwm->xcb_conn, xsurface->window_id, mask, values);
// If the window size did not change, then we cannot rely on
@@ -1777,15 +1801,15 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
// we are supposed to send a synthetic event. See ICCCM part
// 4.1.5. But we ignore override-redirect windows as ICCCM does
// not apply to them.
- if (width == old_w && height == old_h && !xsurface->override_redirect) {
+ if (scale(xwm, width) == scale(xwm, old_w) && scale(xwm, height) == scale(xwm, old_h) && !xsurface->override_redirect) {
xcb_configure_notify_event_t configure_notify = {
.response_type = XCB_CONFIGURE_NOTIFY,
.event = xsurface->window_id,
.window = xsurface->window_id,
- .x = x,
- .y = y,
- .width = width,
- .height = height,
+ .x = scale(xwm, x),
+ .y = scale(xwm, y),
+ .width = scale(xwm, width),
+ .height = scale(xwm, height),
};
xcb_send_event(xwm->xcb_conn, 0, xsurface->window_id,
@@ -2122,6 +2146,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
wl_list_init(&xwm->pending_startup_ids);
xwm->ping_timeout = 10000;
+ xwm->scale = 1;
xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);
int rc = xcb_connection_has_error(xwm->xcb_conn);

View File

@@ -1,76 +1,28 @@
{
lib,
version,
src,
#
wlroots,
xwayland,
fetchpatch,
lib,
hwdata,
libliftoff,
libdisplay-info,
hidpiXWayland ? true,
libliftoff,
enableXWayland ? true,
nvidiaPatches ? false,
enableNvidiaPatches ? false,
}:
assert (lib.assertMsg (hidpiXWayland -> enableXWayland) ''
wlroots-hyprland: cannot have hidpiXWayland when enableXWayland is false.
'');
(wlroots.overrideAttrs
(old: {
inherit version src;
pname =
old.pname
+ "-hyprland"
+ (
if hidpiXWayland
then "-hidpi"
else ""
)
+ (
if nvidiaPatches
then "-nvidia"
else ""
);
patches =
(old.patches or [])
++ (lib.optionals (enableXWayland && hidpiXWayland) [
# adapted from https://gitlab.freedesktop.org/lilydjwg/wlroots/-/commit/6c5ffcd1fee9e44780a6a8792f74ecfbe24a1ca7
./wlroots-hidpi.patch
(fetchpatch {
url = "https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/18595000f3a21502fd60bf213122859cc348f9af.diff";
sha256 = "sha256-jvfkAMh3gzkfuoRhB4E9T5X1Hu62wgUjj4tZkJm0mrI=";
revert = true;
})
])
++ (lib.optionals nvidiaPatches [
(fetchpatch {
url = "https://aur.archlinux.org/cgit/aur.git/plain/0001-nvidia-format-workaround.patch?h=hyprland-nvidia-screenshare-git";
sha256 = "A9f1p5EW++mGCaNq8w7ZJfeWmvTfUm4iO+1KDcnqYX8=";
})
]);
postPatch =
(old.postPatch or "")
+ (
if nvidiaPatches
then ''
substituteInPlace render/gles2/renderer.c --replace "glFlush();" "glFinish();"
''
else ""
);
buildInputs = old.buildInputs ++ [hwdata libliftoff libdisplay-info];
wlroots.overrideAttrs (old: {
inherit version src enableXWayland;
NIX_CFLAGS_COMPILE = toString [
"-Wno-error=maybe-uninitialized"
];
}))
.override {
xwayland = xwayland.overrideAttrs (old: {
patches =
(old.patches or [])
++ (lib.optionals hidpiXWayland [
./xwayland-vsync.patch
./xwayland-hidpi.patch
]);
});
}
pname = "${old.pname}-hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}";
patches =
(old.patches or [])
++ (lib.optionals enableNvidiaPatches [
./patches/wlroots-nvidia.patch
]);
buildInputs = old.buildInputs ++ [hwdata libliftoff libdisplay-info];
NIX_CFLAGS_COMPILE = toString [
"-Wno-error=maybe-uninitialized"
];
})

View File

@@ -1,493 +0,0 @@
diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c
index e3c1aaa50..eba29b5ba 100644
--- a/hw/xwayland/xwayland-cursor.c
+++ b/hw/xwayland/xwayland-cursor.c
@@ -164,6 +164,8 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat,
}
wl_surface_attach(xwl_cursor->surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(xwl_cursor->surface,
+ xwl_seat->xwl_screen->global_output_scale);
xwl_surface_damage(xwl_seat->xwl_screen, xwl_cursor->surface, 0, 0,
xwl_seat->x_cursor->bits->width,
xwl_seat->x_cursor->bits->height);
@@ -195,6 +197,7 @@ xwl_cursor_clear_frame_cb(struct xwl_cursor *xwl_cursor)
void
xwl_seat_set_cursor(struct xwl_seat *xwl_seat)
{
+ struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
struct xwl_cursor *xwl_cursor = &xwl_seat->cursor;
PixmapPtr pixmap;
CursorPtr cursor;
@@ -225,8 +228,8 @@ xwl_seat_set_cursor(struct xwl_seat *xwl_seat)
wl_pointer_set_cursor(xwl_seat->wl_pointer,
xwl_seat->pointer_enter_serial,
xwl_cursor->surface,
- xwl_seat->x_cursor->bits->xhot,
- xwl_seat->x_cursor->bits->yhot);
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot),
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot));
xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap);
}
@@ -235,6 +238,7 @@ void
xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool)
{
struct xwl_seat *xwl_seat = xwl_tablet_tool->seat;
+ struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
struct xwl_cursor *xwl_cursor = &xwl_tablet_tool->cursor;
PixmapPtr pixmap;
CursorPtr cursor;
@@ -263,8 +267,9 @@ xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool)
zwp_tablet_tool_v2_set_cursor(xwl_tablet_tool->tool,
xwl_tablet_tool->proximity_in_serial,
xwl_cursor->surface,
- xwl_seat->x_cursor->bits->xhot,
- xwl_seat->x_cursor->bits->yhot);
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot),
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot));
+ wl_surface_set_buffer_scale(xwl_cursor->surface, xwl_screen->global_output_scale);
xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap);
}
diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index 6e0600e4e..4a22ebff0 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -507,8 +507,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
DeviceIntPtr dev = get_pointer_device(xwl_seat);
DeviceIntPtr master;
int i;
- int sx = wl_fixed_to_int(sx_w);
- int sy = wl_fixed_to_int(sy_w);
+ int sx = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;
+ int sy = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;
int dx, dy;
ScreenPtr pScreen = xwl_seat->xwl_screen->screen;
ValuatorMask mask;
@@ -731,13 +731,14 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
{
struct xwl_seat *xwl_seat = data;
+ int32_t scale = xwl_seat->xwl_screen->global_output_scale;
if (!xwl_seat->focus_window)
return;
xwl_seat->pending_pointer_event.has_absolute = TRUE;
- xwl_seat->pending_pointer_event.x = sx_w;
- xwl_seat->pending_pointer_event.y = sy_w;
+ xwl_seat->pending_pointer_event.x = sx_w * scale;
+ xwl_seat->pending_pointer_event.y = sy_w * scale;
if (wl_proxy_get_version((struct wl_proxy *) xwl_seat->wl_pointer) < 5)
dispatch_pointer_motion_event(xwl_seat);
@@ -887,12 +888,13 @@ relative_pointer_handle_relative_motion(void *data,
wl_fixed_t dy_unaccelf)
{
struct xwl_seat *xwl_seat = data;
+ int32_t scale = xwl_seat->xwl_screen->global_output_scale;
xwl_seat->pending_pointer_event.has_relative = TRUE;
- xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf);
- xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf);
- xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf);
- xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf);
+ xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf) * scale;
+ xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf) * scale;
+ xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf) * scale;
+ xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf) * scale;
if (!xwl_seat->focus_window)
return;
@@ -1382,8 +1384,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch,
xwl_touch->window = wl_surface_get_user_data(surface);
xwl_touch->id = id;
- xwl_touch->x = wl_fixed_to_int(sx_w);
- xwl_touch->y = wl_fixed_to_int(sy_w);
+ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;
+ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;
xorg_list_add(&xwl_touch->link_touch, &xwl_seat->touches);
xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchBegin);
@@ -1419,8 +1421,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch,
if (!xwl_touch)
return;
- xwl_touch->x = wl_fixed_to_int(sx_w);
- xwl_touch->y = wl_fixed_to_int(sy_w);
+ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;
+ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;
xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchUpdate);
}
@@ -2110,8 +2112,8 @@ tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool,
struct xwl_tablet_tool *xwl_tablet_tool = data;
struct xwl_seat *xwl_seat = xwl_tablet_tool->seat;
int32_t dx, dy;
- double sx = wl_fixed_to_double(x);
- double sy = wl_fixed_to_double(y);
+ double sx = wl_fixed_to_double(x) * xwl_seat->xwl_screen->global_output_scale;
+ double sy = wl_fixed_to_double(y) * xwl_seat->xwl_screen->global_output_scale;
if (!xwl_seat->tablet_focus_window)
return;
@@ -3152,6 +3154,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
int x,
int y)
{
+ struct xwl_screen *xwl_screen;
struct zwp_locked_pointer_v1 *locked_pointer =
warp_emulator->locked_pointer;
WindowPtr window;
@@ -3163,6 +3166,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
if (!warp_emulator->xwl_seat->focus_window)
return;
+ xwl_screen = warp_emulator->xwl_seat->xwl_screen;
window = warp_emulator->xwl_seat->focus_window->window;
if (x >= window->drawable.x ||
y >= window->drawable.y ||
@@ -3171,8 +3175,8 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
sx = x - window->drawable.x;
sy = y - window->drawable.y;
zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
- wl_fixed_from_int(sx),
- wl_fixed_from_int(sy));
+ wl_fixed_from_int(xwl_scale_to(xwl_screen, sx)),
+ wl_fixed_from_int(xwl_scale_to(xwl_screen, sy)));
wl_surface_commit(warp_emulator->xwl_seat->focus_window->surface);
}
}
diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 661e1828d..6c60aba34 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -186,6 +186,9 @@ update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height)
static void
update_screen_size(struct xwl_screen *xwl_screen, int width, int height)
{
+ width *= xwl_screen->global_output_scale;
+ height *= xwl_screen->global_output_scale;
+
xwl_screen->width = width;
xwl_screen->height = height;
@@ -597,14 +600,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
new_emulated_height);
}
-static void
-apply_output_change(struct xwl_output *xwl_output)
+void
+xwl_output_apply_changes(struct xwl_output *xwl_output)
{
struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
struct xwl_output *it;
int mode_width, mode_height, count;
int width = 0, height = 0, has_this_output = 0;
RRModePtr *randr_modes;
+ int32_t scale = xwl_screen->global_output_scale;
/* Clear out the "done" received flags */
xwl_output->wl_output_done = FALSE;
@@ -623,10 +627,10 @@ apply_output_change(struct xwl_output *xwl_output)
}
if (xwl_output->randr_output) {
/* Build a fresh modes array using the current refresh rate */
- randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count);
+ randr_modes = output_get_rr_modes(xwl_output, mode_width * scale, mode_height * scale, &count);
RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
- xwl_output->x, xwl_output->y,
+ xwl_output->x * scale, xwl_output->y * scale,
xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
/* RROutputSetModes takes ownership of the passed in modes, so we only
* have to free the pointer array.
@@ -686,7 +690,7 @@ output_handle_done(void *data, struct wl_output *wl_output)
*/
if (xwl_output->xdg_output_done || !xwl_output->xdg_output ||
zxdg_output_v1_get_version(xwl_output->xdg_output) >= 3)
- apply_output_change(xwl_output);
+ xwl_output_apply_changes(xwl_output);
}
static void
@@ -746,7 +750,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
xwl_output->xdg_output_done = TRUE;
if (xwl_output->wl_output_done &&
zxdg_output_v1_get_version(xdg_output) < 3)
- apply_output_change(xwl_output);
+ xwl_output_apply_changes(xwl_output);
}
static void
@@ -857,6 +861,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id,
RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
RROutputSetConnection(xwl_output->randr_output, RR_Connected);
+
+ xwl_output->scale = 1;
}
/* We want the output to be in the list as soon as created so we can
* use it when binding to the xdg-output protocol...
diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h
index a95288e4f..46d1ead2a 100644
--- a/hw/xwayland/xwayland-output.h
+++ b/hw/xwayland/xwayland-output.h
@@ -53,7 +53,7 @@ struct xwl_output {
struct wl_output *output;
struct zxdg_output_v1 *xdg_output;
uint32_t server_output_id;
- int32_t x, y, width, height, refresh;
+ int32_t x, y, width, height, refresh, scale;
Rotation rotation;
Bool wl_output_done;
Bool xdg_output_done;
@@ -102,6 +102,8 @@ void xwl_output_set_emulated_mode(struct xwl_output *xwl_output,
void xwl_output_set_window_randr_emu_props(struct xwl_screen *xwl_screen,
WindowPtr window);
+void xwl_output_apply_changes(struct xwl_output *xwl_output);
+
void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen);
#endif /* XWAYLAND_OUTPUT_H */
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index 189e7cfd6..555434031 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -764,6 +764,8 @@ xwl_present_flip(WindowPtr present_window,
/* We can flip directly to the main surface (full screen window without clips) */
wl_surface_attach(xwl_window->surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(xwl_window->surface,
+ xwl_window->xwl_screen->global_output_scale);
if (xorg_list_is_empty(&xwl_present_window->frame_callback_list)) {
xorg_list_add(&xwl_present_window->frame_callback_list,
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index 46ab4fed7..b2d7022e6 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -51,6 +51,7 @@
#include "xwayland-pixmap.h"
#include "xwayland-present.h"
#include "xwayland-shm.h"
+#include "xwayland-window-buffers.h"
#ifdef MITSHM
#include "shmint.h"
@@ -111,6 +112,12 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen)
return xwl_screen->rootless && xwl_screen_has_viewport_support(xwl_screen);
}
+int
+xwl_scale_to(struct xwl_screen *xwl_screen, int value)
+{
+ return value / (double)xwl_screen->global_output_scale + 0.5;
+}
+
/* Return the output @ 0x0, falling back to the first output in the list */
struct xwl_output *
xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
@@ -128,6 +135,38 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link);
}
+static void
+xwl_screen_set_global_scale_from_property(struct xwl_screen *screen,
+ PropertyPtr prop)
+{
+ CARD32 *propdata;
+
+ if (prop->type != XA_CARDINAL || prop->format != 32 || prop->size != 1) {
+ // TODO: handle warnings more cleanly.
+ LogMessageVerb(X_WARNING, 0, "Bad value for property %s.\n",
+ NameForAtom(prop->propertyName));
+ return;
+ }
+
+ propdata = prop->data;
+ xwl_screen_set_global_scale(screen, propdata[0]);
+}
+
+static void
+xwl_screen_update_property(struct xwl_screen *screen,
+ PropertyStateRec *propstate)
+{
+ switch (propstate->state) {
+ case PropertyNewValue:
+ xwl_screen_set_global_scale_from_property(screen, propstate->prop);
+ break;
+ case PropertyDelete:
+ xwl_screen_set_global_scale(screen, 1);
+ break;
+ }
+}
+
+
struct xwl_output *
xwl_screen_get_fixed_or_first_output(struct xwl_screen *xwl_screen)
{
@@ -144,19 +183,24 @@ xwl_property_callback(CallbackListPtr *pcbl, void *closure,
ScreenPtr screen = closure;
PropertyStateRec *rec = calldata;
struct xwl_screen *xwl_screen;
- struct xwl_window *xwl_window;
if (rec->win->drawable.pScreen != screen)
return;
- xwl_window = xwl_window_get(rec->win);
- if (!xwl_window)
- return;
-
xwl_screen = xwl_screen_get(screen);
- if (rec->prop->propertyName == xwl_screen->allow_commits_prop)
- xwl_window_update_property(xwl_window, rec);
+ if (rec->prop->propertyName == xwl_screen->allow_commits_prop) {
+ struct xwl_window *xwl_window;
+
+ xwl_window = xwl_window_get(rec->win);
+ if (!xwl_window)
+ return;
+
+ xwl_window_update_property(xwl_window, rec);
+ }
+ else if (rec->prop->propertyName == xwl_screen->global_output_scale_prop) {
+ xwl_screen_update_property(xwl_screen, rec);
+ }
}
static void
@@ -638,8 +682,14 @@ void xwl_surface_damage(struct xwl_screen *xwl_screen,
{
if (wl_surface_get_version(surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)
wl_surface_damage_buffer(surface, x, y, width, height);
- else
+ else {
+ x = xwl_scale_to(xwl_screen, x);
+ y = xwl_scale_to(xwl_screen, y);
+ width = xwl_scale_to(xwl_screen, width);
+ height = xwl_scale_to(xwl_screen, height);
+
wl_surface_damage(surface, x, y, width, height);
+ }
}
void
@@ -655,6 +705,30 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
xwl_give_up("could not connect to wayland server\n");
}
+void
+xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale)
+{
+ struct xwl_output *it;
+ struct xwl_window *xwl_window;
+
+ xwl_screen->global_output_scale = scale;
+
+ /* change randr resolutions and positions */
+ xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
+ xwl_output_apply_changes(it);
+ }
+
+ if (!xwl_screen->rootless && xwl_screen->screen->root) {
+ /* Clear all the buffers, so that they'll be remade with the new sizes
+ * (this doesn't occur automatically because as far as Xorg is
+ * concerned, the window's size is the same) */
+ xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window) {
+ xwl_window_buffers_recycle(xwl_window);
+ }
+ }
+}
+
+
static int
xwl_server_grab(ClientPtr client)
{
@@ -712,6 +786,7 @@ Bool
xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
{
static const char allow_commits[] = "_XWAYLAND_ALLOW_COMMITS";
+ static const char global_output_scale[] = "_XWAYLAND_GLOBAL_OUTPUT_SCALE";
struct xwl_screen *xwl_screen;
Pixel red_mask, blue_mask, green_mask;
int ret, bpc, green_bpc, i;
@@ -746,6 +821,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
#ifdef XWL_HAS_GLAMOR
xwl_screen->glamor = 1;
#endif
+ xwl_screen->global_output_scale = 1;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-rootless") == 0) {
@@ -988,6 +1064,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
if (xwl_screen->allow_commits_prop == BAD_RESOURCE)
return FALSE;
+ xwl_screen->global_output_scale_prop = MakeAtom(global_output_scale,
+ strlen(global_output_scale),
+ TRUE);
+ if (xwl_screen->global_output_scale_prop == BAD_RESOURCE)
+ return FALSE;
+
+
AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen);
AddCallback(&RootWindowFinalizeCallback, xwl_root_window_finalized_callback, pScreen);
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index fadd0526e..2ce6ce5ab 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -87,6 +87,7 @@ struct xwl_screen {
struct xorg_list damage_window_list;
struct xorg_list window_list;
+ int32_t global_output_scale;
int wayland_fd;
struct wl_display *display;
struct wl_registry *registry;
@@ -134,6 +135,7 @@ struct xwl_screen {
struct glamor_context *glamor_ctx;
Atom allow_commits_prop;
+ Atom global_output_scale_prop;
/* The preferred GLVND vendor. If NULL, "mesa" is assumed. */
const char *glvnd_vendor;
@@ -166,6 +168,8 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen);
void xwl_surface_damage(struct xwl_screen *xwl_screen,
struct wl_surface *surface,
int32_t x, int32_t y, int32_t width, int32_t height);
+int xwl_scale_to(struct xwl_screen *xwl_screen, int value);
+void xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale);
int xwl_screen_get_next_output_serial(struct xwl_screen * xwl_screen);
#endif /* XWAYLAND_SCREEN_H */
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 6b7f38605..2f1e0dee1 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -788,7 +788,8 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
}
wl_region_add(region, 0, 0,
- window->drawable.width, window->drawable.height);
+ xwl_scale_to(xwl_screen, window->drawable.width),
+ xwl_scale_to(xwl_screen, window->drawable.height));
wl_surface_set_opaque_region(xwl_window->surface, region);
wl_region_destroy(region);
@@ -1322,6 +1323,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
#endif
wl_surface_attach(xwl_window->surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(xwl_window->surface, xwl_screen->global_output_scale);
/* Arbitrary limit to try to avoid flooding the Wayland
* connection. If we flood it too much anyway, this could

View File

@@ -1,12 +0,0 @@
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -824,7 +824,8 @@
dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
vblank->pixmap = NULL;
- if (xwl_present_queue_vblank(screen, window, vblank->crtc,
+ if (vblank->target_msc > crtc_msc &&
+ xwl_present_queue_vblank(screen, window, vblank->crtc,
vblank->event_id, crtc_msc + 1)
== Success)
return;

View File

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

View File

@@ -1,306 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_workspace_unstable_v1">
<copyright>
Copyright © 2019 Christopher Billington
Copyright © 2020 Ilia Bozhinov
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>
<interface name="zext_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
Workspaces are grouped by which sets of outputs they correspond to, and
may contain surfaces only from those outputs. In this way, it is possible
for each output to have its own set of workspaces, or for all outputs (or
any other arbitrary grouping) to share workspaces. Compositors may
optionally conceptually arrange each group of workspaces in an
N-dimensional grid.
The purpose of this protocol is to enable the creation of taskbars and
docks by providing them with a list of workspaces and their properties,
and allowing them to activate and deactivate workspaces.
After a client binds the zext_workspace_manager_v1, each workspace will be
sent via the workspace event.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
This event is emitted whenever a new workspace group has been created.
All initial details of the workspace group (workspaces, outputs) will be
sent immediately after this event via the corresponding events in
zext_workspace_group_handle_v1.
</description>
<arg name="workspace_group" type="new_id" interface="zext_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
The client must send this request after it has finished sending other
requests. The compositor must process a series of requests preceding a
commit request atomically.
This allows changes to the workspace properties to be seen as atomic,
even if they happen via multiple events, and even if they involve
multiple zext_workspace_handle_v1 objects, for example, deactivating one
workspace and activating another.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zext_workspace_group_handle_v1
properties to be seen as atomic, even if they happen via multiple
events. In particular, an output moving from one workspace group to
another sends an output_enter event and an output_leave event to the two
zext_workspace_group_handle_v1 objects in question. The compositor sends
the done event only after updating the output information in both
workspace groups.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
This event indicates that the compositor is done sending events to the
zext_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new
workspace groups. However the compositor may emit further workspace
events, until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
</interface>
<interface name="zext_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
A zext_workspace_group_handle_v1 object represents a a workspace group
that is assigned a set of outputs and contains a number of workspaces.
The set of outputs assigned to the workspace group is conveyed to the client via
output_enter and output_leave events, and its workspaces are conveyed with
workspace events.
For example, a compositor which has a set of workspaces for each output may
advertise a workspace group (and its workspaces) per output, whereas a compositor
where a workspace spans all outputs may advertise a single workspace group for all
outputs.
</description>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to workspace group">
This event is emitted whenever a new workspace has been created.
All initial details of the workspace (name, coordinates, state) will
be sent immediately after this event via the corresponding events in
zext_workspace_handle_v1.
</description>
<arg name="workspace" type="new_id" interface="zext_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
This event means the zext_workspace_group_handle_v1 has been destroyed.
It is guaranteed there won't be any more events for this
zext_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes
inert so any requests will be ignored except the destroy request.
The compositor must remove all workspaces belonging to a workspace group
before removing the workspace group.
</description>
</event>
<request name="create_workspace">
<description summary="create a new workspace">
Request that the compositor create a new workspace with the given name.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name.
</description>
<arg name="workspace" type="string"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
</interface>
<interface name="zext_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
A zext_workspace_handle_v1 object represents a a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event; a
list of states, conveyed to the client with the state event; and
optionally a set of coordinates, conveyed to the client with the
coordinates event. The client may request that the compositor activate or
deactivate the workspace.
Each workspace can belong to only a single workspace group.
Depepending on the compositor policy, there might be workspaces with
the same name in different workspace groups, but these workspaces are still
separate (e.g. one of them might be active while the other is not).
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
This event is used to organize workspaces into an N-dimensional grid
within a workspace group, and if supported, is emitted immediately after
the zext_workspace_handle_v1 is created and whenever the coordinates of
the workspace change. Compositors may not send this event if they do not
conceptually arrange workspaces in this way. If compositors simply
number workspaces, without any geometric interpretation, they may send
1D coordinates, which clients should not interpret as implying any
geometry. Sending an empty array means that the compositor no longer
orders the workspace geometrically.
Coordinates have an arbitrary number of dimensions N with an uint32
position along each dimension. By convention if N > 1, the first
dimension is X, the second Y, the third Z, and so on. The compositor may
chose to utilize these events for a more novel workspace layout
convention, however. No guarantee is made about the grid being filled or
bounded; there may be a workspace at coordinate 1 and another at
coordinate 1000 and none in between. Within a workspace group, however,
workspaces must have unique coordinates of equal dimensionality.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and each time the workspace state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
<entry name="urgent" value="1" summary="the workspace requests attention"/>
<entry name="hidden" value="2">
<description summary="the workspace is not visible">
The workspace is not visible in its workspace group, and clients
attempting to visualize the compositor workspace state should not
display such workspaces.
</description>
</entry>
</enum>
<event name="remove">
<description summary="this workspace has been destroyed">
This event means the zext_workspace_handle_v1 has been destroyed. It is
guaranteed there won't be any more events for this
zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so
any requests will be ignored except the destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependent. For example, activating a
workspace may or may not deactivate all other workspaces in the same
group.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
<request name="remove">
<description summary="remove the workspace">
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
</description>
</request>
</interface>
</protocol>

View File

@@ -22,12 +22,13 @@ 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'],
['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'],
['ext-workspace-unstable-v1.xml'],
['pointer-constraints-unstable-v1.xml'],
['tablet-unstable-v2.xml'],
['idle.xml'],

View File

@@ -1,6 +1,7 @@
#include "Compositor.hpp"
#include "helpers/Splashes.hpp"
#include <random>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp"
#ifdef USES_SYSTEMD
@@ -9,13 +10,12 @@
#include <ranges>
int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal %d", signo);
Debug::log(LOG, "Hyprland received signal {}", signo);
if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL) {
if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL)
g_pCompositor->cleanup();
}
return 0; // everything went fine
return 0;
}
void handleUnrecoverableSignal(int sig) {
@@ -53,9 +53,9 @@ CCompositor::CCompositor() {
Debug::init(m_szInstanceSignature);
Debug::log(LOG, "Instance Signature: %s", m_szInstanceSignature.c_str());
Debug::log(LOG, "Instance Signature: {}", m_szInstanceSignature);
Debug::log(LOG, "Hyprland PID: %i", m_iHyprlandPID);
Debug::log(LOG, "Hyprland PID: {}", m_iHyprlandPID);
Debug::log(LOG, "===== SYSTEM INFO: =====");
@@ -69,11 +69,29 @@ CCompositor::CCompositor() {
setRandomSplash();
Debug::log(LOG, "\nCurrent splash: %s\n\n", m_szCurrentSplash.c_str());
Debug::log(LOG, "\nCurrent splash: {}\n\n", m_szCurrentSplash);
}
CCompositor::~CCompositor() {
cleanup();
g_pPluginSystem.reset();
g_pHyprNotificationOverlay.reset();
g_pDebugOverlay.reset();
g_pEventManager.reset();
g_pSessionLockManager.reset();
g_pProtocolManager.reset();
g_pXWaylandManager.reset();
g_pHyprRenderer.reset();
g_pHyprOpenGL.reset();
g_pInputManager.reset();
g_pThreadManager.reset();
g_pConfigManager.reset();
g_pLayoutManager.reset();
g_pHyprError.reset();
g_pConfigManager.reset();
g_pAnimationManager.reset();
g_pKeybindManager.reset();
g_pHookSystem.reset();
}
void CCompositor::setRandomSplash() {
@@ -98,6 +116,9 @@ void CCompositor::initServer() {
initManagers(STAGE_PRIORITY);
if (const auto ENV = getenv("HYPRLAND_TRACE"); ENV && std::string(ENV) == "1")
Debug::trace = true;
wlr_log_init(WLR_INFO, NULL);
const auto LOGWLR = getenv("HYPRLAND_LOG_WLR");
@@ -108,20 +129,20 @@ void CCompositor::initServer() {
if (!m_sWLRBackend) {
Debug::log(CRIT, "m_sWLRBackend was NULL!");
throw std::runtime_error("wlr_backend_autocreate() failed!");
throwError("wlr_backend_autocreate() failed!");
}
m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend);
if (m_iDRMFD < 0) {
Debug::log(CRIT, "Couldn't query the DRM FD!");
throw std::runtime_error("wlr_backend_get_drm_fd() failed!");
throwError("wlr_backend_get_drm_fd() failed!");
}
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
if (!m_sWLRRenderer) {
Debug::log(CRIT, "m_sWLRRenderer was NULL!");
throw std::runtime_error("wlr_gles2_renderer_create_with_drm_fd() failed!");
throwError("wlr_gles2_renderer_create_with_drm_fd() failed!");
}
wlr_renderer_init_wl_shm(m_sWLRRenderer, m_sWLDisplay);
@@ -137,14 +158,14 @@ void CCompositor::initServer() {
if (!m_sWLRAllocator) {
Debug::log(CRIT, "m_sWLRAllocator was NULL!");
throw std::runtime_error("wlr_allocator_autocreate() failed!");
throwError("wlr_allocator_autocreate() failed!");
}
m_sWLREGL = wlr_gles2_renderer_get_egl(m_sWLRRenderer);
if (!m_sWLREGL) {
Debug::log(CRIT, "m_sWLREGL was NULL!");
throw std::runtime_error("wlr_gles2_renderer_get_egl() failed!");
throwError("wlr_gles2_renderer_get_egl() failed!");
}
m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, 6, m_sWLRRenderer);
@@ -153,17 +174,15 @@ void CCompositor::initServer() {
wlr_export_dmabuf_manager_v1_create(m_sWLDisplay);
wlr_data_control_manager_v1_create(m_sWLDisplay);
wlr_gamma_control_manager_v1_create(m_sWLDisplay);
wlr_primary_selection_v1_device_manager_create(m_sWLDisplay);
wlr_viewporter_create(m_sWLDisplay);
m_sWLRGammaCtrlMgr = wlr_gamma_control_manager_v1_create(m_sWLDisplay);
m_sWLROutputLayout = wlr_output_layout_create();
m_sWLROutputPowerMgr = wlr_output_power_manager_v1_create(m_sWLDisplay);
m_sWLRScene = wlr_scene_create();
wlr_scene_attach_output_layout(m_sWLRScene, m_sWLROutputLayout);
m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 5);
m_sWLRCursor = wlr_cursor_create();
@@ -176,7 +195,7 @@ void CCompositor::initServer() {
int cursorSize = 24;
try {
cursorSize = std::stoi(XCURSORENV);
} catch (std::exception& e) { Debug::log(ERR, "XCURSOR_SIZE invalid in check #2? (%s)", XCURSORENV); }
} catch (std::exception& e) { Debug::log(ERR, "XCURSOR_SIZE invalid in check #2? ({})", XCURSORENV); }
m_sWLRXCursorMgr = wlr_xcursor_manager_create(nullptr, cursorSize);
wlr_xcursor_manager_load(m_sWLRXCursorMgr, 1);
@@ -185,7 +204,8 @@ void CCompositor::initServer() {
m_sWLRPresentation = wlr_presentation_create(m_sWLDisplay, m_sWLRBackend);
m_sWLRIdle = wlr_idle_create(m_sWLDisplay);
m_sWLRIdle = wlr_idle_create(m_sWLDisplay);
m_sWLRIdleNotifier = wlr_idle_notifier_v1_create(m_sWLDisplay);
m_sWLRLayerShell = wlr_layer_shell_v1_create(m_sWLDisplay, 4);
@@ -193,14 +213,11 @@ void CCompositor::initServer() {
m_sWLRXDGDecoMgr = wlr_xdg_decoration_manager_v1_create(m_sWLDisplay);
wlr_server_decoration_manager_set_default_mode(m_sWLRServerDecoMgr, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
wlr_xdg_output_manager_v1_create(m_sWLDisplay, m_sWLROutputLayout);
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
m_sWLRInhibitMgr = wlr_input_inhibit_manager_create(m_sWLDisplay);
m_sWLRKbShInhibitMgr = wlr_keyboard_shortcuts_inhibit_v1_create(m_sWLDisplay);
m_sWLREXTWorkspaceMgr = wlr_ext_workspace_manager_v1_create(m_sWLDisplay);
m_sWLRPointerConstraints = wlr_pointer_constraints_v1_create(m_sWLDisplay);
m_sWLRRelPointerMgr = wlr_relative_pointer_manager_v1_create(m_sWLDisplay);
@@ -238,9 +255,11 @@ void CCompositor::initServer() {
m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay);
m_sWLRCursorShapeMgr = wlr_cursor_shape_manager_v1_create(m_sWLDisplay, 1);
if (!m_sWLRHeadlessBackend) {
Debug::log(CRIT, "Couldn't create the headless backend");
throw std::runtime_error("wlr_headless_backend_create() failed!");
throwError("wlr_headless_backend_create() failed!");
}
wlr_single_pixel_buffer_manager_v1_create(m_sWLDisplay);
@@ -294,6 +313,8 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRTextInputMgr->events.text_input, &Events::listen_newTextInput, m_sWLRTextInputMgr, "TextInputMgr");
addWLSignal(&m_sWLRActivation->events.request_activate, &Events::listen_activateXDG, m_sWLRActivation, "ActivationV1");
addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr");
addWLSignal(&m_sWLRGammaCtrlMgr->events.set_gamma, &Events::listen_setGamma, m_sWLRGammaCtrlMgr, "GammaCtrlMgr");
addWLSignal(&m_sWLRCursorShapeMgr->events.request_set_shape, &Events::listen_setCursorShape, m_sWLRCursorShapeMgr, "CursorShapeMgr");
if (m_sWRLDRMLeaseMgr)
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
@@ -306,8 +327,15 @@ void CCompositor::cleanup() {
if (!m_sWLDisplay || m_bIsShuttingDown)
return;
removeLockFile();
m_bIsShuttingDown = true;
#ifdef USES_SYSTEMD
if (sd_booted() > 0)
sd_notify(0, "STOPPING=1");
#endif
// unload all remaining plugins while the compositor is
// still in a normal working state.
g_pPluginSystem->unloadAllPlugins();
@@ -405,6 +433,23 @@ void CCompositor::initManagers(eManagersInitStage stage) {
}
}
void CCompositor::createLockFile() {
const auto PATH = "/tmp/hypr/" + m_szInstanceSignature + ".lock";
std::ofstream ofs(PATH, std::ios::trunc);
ofs << m_iHyprlandPID << "\n" << m_szWLDisplaySocket << "\n";
ofs.close();
}
void CCompositor::removeLockFile() {
const auto PATH = "/tmp/hypr/" + m_szInstanceSignature + ".lock";
if (std::filesystem::exists(PATH))
std::filesystem::remove(PATH);
}
void CCompositor::startCompositor() {
initAllSignals();
@@ -414,10 +459,10 @@ void CCompositor::startCompositor() {
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for %s succeeded with %i", CANDIDATESTR.c_str(), RETVAL);
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else {
Debug::log(WARN, "wl_display_add_socket for %s returned %i: skipping candidate %i", CANDIDATESTR.c_str(), RETVAL, candidate);
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
}
}
@@ -431,7 +476,7 @@ void CCompositor::startCompositor() {
if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
wlr_backend_destroy(m_sWLRBackend);
throw std::runtime_error("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
@@ -447,16 +492,16 @@ void CCompositor::startCompositor() {
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
nullptr);
Debug::log(LOG, "Running on WAYLAND_DISPLAY: %s", m_szWLDisplaySocket.c_str());
Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket);
if (!wlr_backend_start(m_sWLRBackend)) {
Debug::log(CRIT, "Backend did not start!");
wlr_backend_destroy(m_sWLRBackend);
wl_display_destroy(m_sWLDisplay);
throw std::runtime_error("The backend could not start!");
throwError("The backend could not start!");
}
wlr_xcursor_manager_set_cursor_image(m_sWLRXCursorMgr, "left_ptr", m_sWLRCursor);
wlr_cursor_set_xcursor(m_sWLRCursor, m_sWLRXCursorMgr, "left_ptr");
#ifdef USES_SYSTEMD
if (sd_booted() > 0)
@@ -466,6 +511,8 @@ void CCompositor::startCompositor() {
Debug::log(LOG, "systemd integration is baked in but system itself is not booted à la systemd!");
#endif
createLockFile();
// This blocks until we are done.
Debug::log(LOG, "Hyprland is ready, running the event loop!");
wl_display_run(m_sWLDisplay);
@@ -487,7 +534,14 @@ CMonitor* CCompositor::getMonitorFromName(const std::string& name) {
return m.get();
}
}
return nullptr;
}
CMonitor* CCompositor::getMonitorFromDesc(const std::string& desc) {
for (auto& m : m_vMonitors) {
if (m->output->description && std::string(m->output->description).find(desc) == 0)
return m.get();
}
return nullptr;
}
@@ -793,6 +847,8 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
return;
}
g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow);
if (!pWindow || !windowValidMapped(pWindow)) {
const auto PLASTWINDOW = m_pLastWindow;
m_pLastWindow = nullptr;
@@ -836,8 +892,9 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
// This is to fix incorrect feedback on the focus history.
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
PWORKSPACE->m_pLastFocusedWindow = pWindow;
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
PMONITOR->changeWorkspace(PWORKSPACE);
PWORKSPACE->rememberPrevWorkspace(getWorkspaceByID(m_pLastMonitor->activeWorkspace));
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
PMONITOR->changeWorkspace(PWORKSPACE, false, true);
// changeworkspace already calls focusWindow
return;
}
@@ -864,31 +921,27 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow
// do pointer focus too
const auto POINTERLOCAL = g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.goalv();
wlr_seat_pointer_notify_enter(m_sSeat.seat, PWINDOWSURFACE, POINTERLOCAL.x, POINTERLOCAL.y);
updateWindowAnimatedDecorationValues(pWindow);
// Handle urgency hint on the workspace
if (pWindow->m_bIsUrgent) {
if (pWindow->m_bIsUrgent)
pWindow->m_bIsUrgent = false;
if (!hasUrgentWindowOnWorkspace(pWindow->m_iWorkspaceID)) {
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
if (PWORKSPACE->m_pWlrHandle) {
wlr_ext_workspace_handle_v1_set_urgent(PWORKSPACE->m_pWlrHandle, 0);
}
}
}
// Send an event
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%lx", pWindow)});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)pWindow)});
EMIT_HOOK_EVENT("activeWindow", pWindow);
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(pWindow);
// TODO: implement this better
if (!PLASTWINDOW && pWindow->m_sGroupData.pNextWindow) {
for (auto curr = pWindow->m_sGroupData.pNextWindow; curr != pWindow; curr = curr->m_sGroupData.pNextWindow) {
if (curr->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(curr->m_phForeignToplevel, false);
}
}
if (pWindow->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(pWindow->m_phForeignToplevel, true);
@@ -904,7 +957,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
// move to front of the window history
const auto HISTORYPIVOT = std::find_if(m_vWindowFocusHistory.begin(), m_vWindowFocusHistory.end(), [&](const auto& other) { return other == pWindow; });
if (HISTORYPIVOT == m_vWindowFocusHistory.end()) {
Debug::log(ERR, "BUG THIS: Window %lx has no pivot in history", pWindow);
Debug::log(ERR, "BUG THIS: {} has no pivot in history", pWindow);
} else {
std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1);
}
@@ -949,9 +1002,9 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
wl_signal_emit_mutable(&m_sSeat.seat->keyboard_state.events.focus_change, &event);
if (pWindowOwner)
Debug::log(LOG, "Set keyboard focus to surface %lx, with window name: %s", pSurface, pWindowOwner->m_szTitle.c_str());
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface, pWindowOwner);
else
Debug::log(LOG, "Set keyboard focus to surface %lx", pSurface);
Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface);
g_pXWaylandManager->activateSurface(pSurface, true);
m_pLastFocus = pSurface;
@@ -990,7 +1043,7 @@ CWindow* CCompositor::getWindowForPopup(wlr_xdg_popup* popup) {
wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<std::unique_ptr<SLayerSurface>>* layerSurfaces, Vector2D* sCoords,
SLayerSurface** ppLayerSurfaceFound) {
for (auto& ls : *layerSurfaces | std::views::reverse) {
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha.fl() == 0.f)
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha.fl() == 0.f)
continue;
auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y);
@@ -1068,7 +1121,7 @@ bool CCompositor::isWorkspaceVisible(const int& w) {
if (m->activeWorkspace == w)
return true;
if (m->specialWorkspaceID && isWorkspaceSpecial(w))
if (m->specialWorkspaceID == w)
return true;
}
@@ -1093,14 +1146,20 @@ void CCompositor::sanityCheckWorkspaces() {
const auto WINDOWSONWORKSPACE = getWindowsOnWorkspace((*it)->m_iID);
if ((*it)->m_bIsSpecialWorkspace && WINDOWSONWORKSPACE == 0) {
getMonitorFromID((*it)->m_iMonitorID)->setSpecialWorkspace(nullptr);
it = m_vWorkspaces.erase(it);
continue;
}
if ((WINDOWSONWORKSPACE == 0 && !isWorkspaceVisible((*it)->m_iID))) {
if ((*it)->m_bIsSpecialWorkspace) {
if ((*it)->m_fAlpha.fl() > 0.f /* don't abruptly end the fadeout */) {
++it;
continue;
}
const auto PMONITOR = getMonitorFromID((*it)->m_iMonitorID);
if (PMONITOR && PMONITOR->specialWorkspaceID == (*it)->m_iID)
PMONITOR->setSpecialWorkspace(nullptr);
}
it = m_vWorkspaces.erase(it);
continue;
}
@@ -1146,6 +1205,26 @@ CWindow* CCompositor::getFirstWindowOnWorkspace(const int& id) {
return nullptr;
}
CWindow* CCompositor::getTopLeftWindowOnWorkspace(const int& id) {
const auto PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
return nullptr;
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID != id || !w->m_bIsMapped || w->isHidden())
continue;
const auto WINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
if (WINDOWIDEALBB.x <= PMONITOR->vecPosition.x + 1 && WINDOWIDEALBB.y <= PMONITOR->vecPosition.y + 1)
return w.get();
}
return nullptr;
}
bool CCompositor::doesSeatAcceptInput(wlr_surface* surface) {
if (g_pSessionLockManager->isSessionLocked()) {
if (g_pSessionLockManager->isSurfaceSessionLock(surface))
@@ -1179,15 +1258,24 @@ bool CCompositor::isWindowActive(CWindow* pWindow) {
return PSURFACE == m_pLastFocus || pWindow == m_pLastWindow;
}
void CCompositor::moveWindowToTop(CWindow* pWindow) {
void CCompositor::changeWindowZOrder(CWindow* pWindow, bool top) {
if (!windowValidMapped(pWindow))
return;
auto moveToTop = [&](CWindow* pw) -> void {
for (auto it = m_vWindows.begin(); it != m_vWindows.end(); ++it) {
if (it->get() == pw) {
std::rotate(it, it + 1, m_vWindows.end());
break;
auto moveToZ = [&](CWindow* pw, bool top) -> void {
if (top) {
for (auto it = m_vWindows.begin(); it != m_vWindows.end(); ++it) {
if (it->get() == pw) {
std::rotate(it, it + 1, m_vWindows.end());
break;
}
}
} else {
for (auto it = m_vWindows.rbegin(); it != m_vWindows.rend(); ++it) {
if (it->get() == pw) {
std::rotate(it, it + 1, m_vWindows.rend());
break;
}
}
}
@@ -1195,28 +1283,36 @@ void CCompositor::moveWindowToTop(CWindow* pWindow) {
g_pHyprRenderer->damageMonitor(getMonitorFromID(pw->m_iMonitorID));
};
moveToTop(pWindow);
if (top)
pWindow->m_bCreatedOverFullscreen = true;
pWindow->m_bCreatedOverFullscreen = true;
if (!pWindow->m_bIsX11)
if (!pWindow->m_bIsX11) {
moveToZ(pWindow, top);
return;
} else {
// move X11 window stack
// move all children
std::deque<CWindow*> toMove;
std::deque<CWindow*> toMove;
auto x11Stack = [&](CWindow* pw, bool top, auto&& x11Stack) -> void {
if (top)
toMove.emplace_back(pw);
else
toMove.emplace_front(pw);
for (auto& w : m_vWindows) {
if (w->m_bIsMapped && w->m_bMappedX11 && !w->isHidden() && w->m_bIsX11 && w->X11TransientFor() == pWindow) {
toMove.emplace_back(w.get());
for (auto& w : m_vWindows) {
if (w->m_bIsMapped && w->m_bMappedX11 && !w->isHidden() && w->m_bIsX11 && w->X11TransientFor() == pw) {
x11Stack(w.get(), top, x11Stack);
}
}
};
x11Stack(pWindow, top, x11Stack);
for (auto it : toMove) {
moveToZ(it, top);
}
}
for (auto& pw : toMove) {
moveToTop(pw);
moveWindowToTop(pw);
}
}
void CCompositor::cleanupFadingOut(const int& monid) {
@@ -1405,13 +1501,6 @@ CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) {
return nullptr;
}
void CCompositor::deactivateAllWLRWorkspaces(wlr_ext_workspace_handle_v1* exclude) {
for (auto& w : m_vWorkspaces) {
if (w->m_pWlrHandle && w->m_pWlrHandle != exclude)
w->setActive(false);
}
}
CWindow* CCompositor::getNextWindowOnWorkspace(CWindow* pWindow, bool focusableOnly) {
bool gotToWindow = false;
for (auto& w : m_vWindows) {
@@ -1601,16 +1690,21 @@ void CCompositor::updateAllWindowsAnimatedDecorationValues() {
void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
// optimization
static auto* const ACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.active_border")->data.get();
static auto* const INACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->data.get();
static auto* const GROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border_active")->data.get();
static auto* const GROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border")->data.get();
static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue;
static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue;
static auto* const PFULLSCREENALPHA = &g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity")->floatValue;
static auto* const PSHADOWCOL = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow")->intValue;
static auto* const PSHADOWCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive")->intValue;
static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue;
static auto* const ACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.active_border")->data.get();
static auto* const INACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->data.get();
static auto* const NOGROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border_active")->data.get();
static auto* const NOGROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border")->data.get();
static auto* const GROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border_active")->data.get();
static auto* const GROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border")->data.get();
static auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border_locked_active")->data.get();
static auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border_locked")->data.get();
static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue;
static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue;
static auto* const PFULLSCREENALPHA = &g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity")->floatValue;
static auto* const PSHADOWCOL = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow")->intValue;
static auto* const PSHADOWCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive")->intValue;
static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue;
static auto* const PDIMENABLED = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue;
auto setBorderColor = [&](CGradientValueData grad) -> void {
if (grad == pWindow->m_cRealBorderColor)
@@ -1627,13 +1721,16 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
if (RENDERDATA.isBorderGradient)
setBorderColor(*RENDERDATA.borderGradient);
else {
const bool GROUPLOCKED = pWindow->m_sGroupData.pNextWindow ? pWindow->getGroupHead()->m_sGroupData.locked : false;
if (pWindow == m_pLastWindow) {
const auto* const ACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? ACTIVECOL : GROUPACTIVECOL;
const auto* const ACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) :
*ACTIVECOLOR);
} else {
const auto* const INACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? INACTIVECOL : GROUPINACTIVECOL;
const auto* const INACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) :
*INACTIVECOLOR);
@@ -1660,7 +1757,7 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
}
// dim
if (pWindow == m_pLastWindow) {
if (pWindow == m_pLastWindow || pWindow->m_sAdditionalConfigData.forceNoDim || !*PDIMENABLED) {
pWindow->m_fDimPercent = 0;
} else {
pWindow->m_fDimPercent = *PDIMSTRENGTH;
@@ -1681,14 +1778,23 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
d->updateWindow(pWindow);
}
int CCompositor::getNextAvailableMonitorID() {
int64_t topID = -1;
for (auto& m : m_vRealMonitors) {
if ((int64_t)m->ID > topID)
topID = m->ID;
int CCompositor::getNextAvailableMonitorID(std::string const& name) {
// reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor
if (m_mMonitorIDMap.contains(name) && !std::any_of(m_vRealMonitors.begin(), m_vRealMonitors.end(), [&](auto m) { return m->ID == m_mMonitorIDMap[name]; }))
return m_mMonitorIDMap[name];
// otherwise, find minimum available ID that is not in the map
std::unordered_set<uint64_t> usedIDs;
for (auto const& monitor : m_vRealMonitors) {
usedIDs.insert(monitor->ID);
}
return topID + 1;
uint64_t nextID = 0;
while (usedIDs.count(nextID) > 0) {
nextID++;
}
m_mMonitorIDMap[name] = nextID;
return nextID;
}
void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB) {
@@ -1755,7 +1861,7 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
updateFullscreenFadeOnWorkspace(PWORKSPACEB);
updateFullscreenFadeOnWorkspace(PWORKSPACEA);
g_pInputManager->refocus();
g_pInputManager->simulateMouseMovement();
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEA->m_szName + "," + pMonitorB->szName});
@@ -1859,11 +1965,11 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
if (pWorkspace->m_iMonitorID == pMonitor->ID)
return;
Debug::log(LOG, "moveWorkspaceToMonitor: Moving %d to monitor %d", pWorkspace->m_iID, pMonitor->ID);
Debug::log(LOG, "moveWorkspaceToMonitor: Moving {} to monitor {}", pWorkspace->m_iID, pMonitor->ID);
const auto POLDMON = getMonitorFromID(pWorkspace->m_iMonitorID);
const bool SWITCHINGISACTIVE = POLDMON->activeWorkspace == pWorkspace->m_iID;
const bool SWITCHINGISACTIVE = POLDMON ? POLDMON->activeWorkspace == pWorkspace->m_iID : false;
// fix old mon
int nextWorkspaceOnMonitorID = -1;
@@ -1886,12 +1992,12 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
}())
nextWorkspaceOnMonitorID++;
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new %d", nextWorkspaceOnMonitorID);
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new {}", nextWorkspaceOnMonitorID);
g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->ID);
}
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing %d", nextWorkspaceOnMonitorID);
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID);
POLDMON->changeWorkspace(nextWorkspaceOnMonitorID);
}
@@ -1910,12 +2016,16 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
// additionally, move floating and fs windows manually
if (w->m_bIsMapped && !w->isHidden()) {
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.vec() - POLDMON->vecPosition + pMonitor->vecPosition;
if (POLDMON) {
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.vec() - POLDMON->vecPosition + pMonitor->vecPosition;
if (w->m_bIsFullscreen) {
w->m_vRealPosition = pMonitor->vecPosition;
w->m_vRealSize = pMonitor->vecSize;
if (w->m_bIsFullscreen) {
w->m_vRealPosition = pMonitor->vecPosition;
w->m_vRealSize = pMonitor->vecSize;
}
} else {
w->m_vRealPosition = Vector2D{(int)w->m_vRealPosition.goalv().x % (int)pMonitor->vecSize.x, (int)w->m_vRealPosition.goalv().y % (int)pMonitor->vecSize.y};
}
}
@@ -1924,7 +2034,7 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
}
if (SWITCHINGISACTIVE && POLDMON == g_pCompositor->m_pLastMonitor) { // if it was active, preserve its' status. If it wasn't, don't.
Debug::log(LOG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active %d -> %d", pMonitor->activeWorkspace, pWorkspace->m_iID);
Debug::log(LOG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active {} -> {}", pMonitor->activeWorkspace, pWorkspace->m_iID);
if (const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace); PWORKSPACE)
getWorkspaceByID(pMonitor->activeWorkspace)->startAnim(false, false);
@@ -1935,24 +2045,26 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
pWorkspace->startAnim(true, true, true);
wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2);
g_pInputManager->simulateMouseMovement();
}
// finalize
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
if (POLDMON) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
updateFullscreenFadeOnWorkspace(getWorkspaceByID(POLDMON->activeWorkspace));
}
updateFullscreenFadeOnWorkspace(pWorkspace);
updateFullscreenFadeOnWorkspace(getWorkspaceByID(POLDMON->activeWorkspace));
g_pInputManager->refocus();
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<void*>{pWorkspace, pMonitor}));
}
bool CCompositor::workspaceIDOutOfBounds(const int& id) {
int lowestID = 99999;
int highestID = -99999;
bool CCompositor::workspaceIDOutOfBounds(const int64_t& id) {
int64_t lowestID = INT64_MAX;
int64_t highestID = INT64_MIN;
for (auto& w : m_vWorkspaces) {
if (w->m_bIsSpecialWorkspace)
@@ -1975,7 +2087,7 @@ void CCompositor::updateFullscreenFadeOnWorkspace(CWorkspace* pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
if (w->m_bFadingOut || w->m_bPinned)
if (w->m_bFadingOut || w->m_bPinned || w->m_bIsFullscreen)
continue;
if (!FULLSCREEN)
@@ -1987,9 +2099,11 @@ void CCompositor::updateFullscreenFadeOnWorkspace(CWorkspace* pWorkspace) {
const auto PMONITOR = getMonitorFromID(pWorkspace->m_iMonitorID);
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
if (pWorkspace->m_iID == PMONITOR->activeWorkspace) {
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
}
}
}
@@ -2016,7 +2130,7 @@ void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->m_bIsFullscreen && mode == FULLSCREEN_FULL);
pWindow->updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
updateWindowAnimatedDecorationValues(pWindow);
// make all windows on the same workspace under the fullscreen window
for (auto& w : m_vWindows) {
@@ -2091,7 +2205,7 @@ CWindow* CCompositor::getWindowByRegex(const std::string& regexp) {
}
for (auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || (w->isHidden() && !w->m_sGroupData.pNextWindow))
if (!w->m_bIsMapped || (w->isHidden() && !g_pLayoutManager->getCurrentLayout()->isWindowReachable(w.get())))
continue;
switch (mode) {
@@ -2108,13 +2222,13 @@ CWindow* CCompositor::getWindowByRegex(const std::string& regexp) {
break;
}
case MODE_ADDRESS: {
std::string addr = getFormat("0x%lx", w.get());
std::string addr = std::format("0x{:x}", (uintptr_t)w.get());
if (matchCheck != addr)
continue;
break;
}
case MODE_PID: {
std::string pid = getFormat("%d", w->getPID());
std::string pid = std::format("{}", w->getPID());
if (matchCheck != pid)
continue;
break;
@@ -2202,27 +2316,29 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con
if (!args.contains(' '))
return relativeTo;
const auto PMONITOR = m_pLastMonitor;
bool xIsPercent = false;
bool yIsPercent = false;
bool isExact = false;
std::string x = args.substr(0, args.find_first_of(' '));
std::string y = args.substr(args.find_first_of(' ') + 1);
if (x == "exact") {
std::string newX = y.substr(0, y.find_first_of(' '));
std::string newY = y.substr(y.find_first_of(' ') + 1);
x = y.substr(0, y.find_first_of(' '));
y = y.substr(y.find_first_of(' ') + 1);
isExact = true;
}
if (!isNumber(newX) || !isNumber(newY)) {
Debug::log(ERR, "parseWindowVectorArgsRelative: exact args not numbers");
return relativeTo;
}
if (x.contains('%')) {
xIsPercent = true;
x = x.substr(0, x.length() - 1);
}
const int X = std::stoi(newX);
const int Y = std::stoi(newY);
if (X < 0 || Y < 0) {
Debug::log(ERR, "parseWindowVectorArgsRelative: exact args cannot be < 0");
return relativeTo;
}
return Vector2D(X, Y);
if (y.contains('%')) {
yIsPercent = true;
y = y.substr(0, y.length() - 1);
}
if (!isNumber(x) || !isNumber(y)) {
@@ -2230,10 +2346,18 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con
return relativeTo;
}
const int X = std::stoi(x);
const int Y = std::stoi(y);
int X = 0;
int Y = 0;
return Vector2D(X + relativeTo.x, Y + relativeTo.y);
if (isExact) {
X = xIsPercent ? std::stof(x) * 0.01 * PMONITOR->vecSize.x : std::stoi(x);
Y = yIsPercent ? std::stof(y) * 0.01 * PMONITOR->vecSize.y : std::stoi(y);
} else {
X = xIsPercent ? std::stof(x) * 0.01 * relativeTo.x + relativeTo.x : std::stoi(x) + relativeTo.x;
Y = yIsPercent ? std::stof(y) * 0.01 * relativeTo.y + relativeTo.y : std::stoi(y) + relativeTo.y;
}
return Vector2D(X, Y);
}
void CCompositor::forceReportSizesToWindowsOnWorkspace(const int& wid) {
@@ -2268,10 +2392,6 @@ CWorkspace* CCompositor::createNewWorkspace(const int& id, const int& monid, con
const auto PWORKSPACE = m_vWorkspaces.emplace_back(std::make_unique<CWorkspace>(monID, NAME, SPECIAL)).get();
// We are required to set the name here immediately
if (!SPECIAL)
wlr_ext_workspace_handle_v1_set_name(PWORKSPACE->m_pWlrHandle, NAME.c_str());
PWORKSPACE->m_iID = id;
PWORKSPACE->m_iMonitorID = monID;
@@ -2287,9 +2407,10 @@ void CCompositor::renameWorkspace(const int& id, const std::string& name) {
if (isWorkspaceSpecial(id))
return;
Debug::log(LOG, "renameWorkspace: Renaming workspace %d to '%s'", id, name.c_str());
wlr_ext_workspace_handle_v1_set_name(PWORKSPACE->m_pWlrHandle, name.c_str());
Debug::log(LOG, "renameWorkspace: Renaming workspace {} to '{}'", id, name);
PWORKSPACE->m_szName = name;
g_pEventManager->postEvent({"renameworkspace", std::to_string(PWORKSPACE->m_iID) + "," + PWORKSPACE->m_szName});
}
void CCompositor::setActiveMonitor(CMonitor* pMonitor) {
@@ -2313,7 +2434,7 @@ bool CCompositor::isWorkspaceSpecial(const int& id) {
}
int CCompositor::getNewSpecialID() {
int highest = -100;
int highest = SPECIAL_WORKSPACE_START;
for (auto& ws : m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace && ws->m_iID > highest) {
highest = ws->m_iID;
@@ -2324,28 +2445,16 @@ int CCompositor::getNewSpecialID() {
}
void CCompositor::performUserChecks() {
static constexpr auto BAD_PORTALS = {"kde", "gnome"};
static auto* const PSUPPRESSPORTAL = &g_pConfigManager->getConfigValuePtr("misc:suppress_portal_warnings")->intValue;
if (!*PSUPPRESSPORTAL) {
if (std::ranges::any_of(BAD_PORTALS, [&](const std::string& portal) { return std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/" + portal + ".portal"); })) {
// bad portal detected
g_pHyprNotificationOverlay->addNotification("You have one or more incompatible xdg-desktop-portal impls installed. Please remove incompatible ones to avoid issues.",
CColor(0), 15000, ICON_ERROR);
}
if (std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/hyprland.portal") && std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/wlr.portal")) {
g_pHyprNotificationOverlay->addNotification("You have xdg-desktop-portal-hyprland and -wlr installed simultaneously. Please uninstall one to avoid issues.", CColor(0),
15000, ICON_ERROR);
}
}
// empty
}
void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) {
if (!pWindow || !pWorkspace)
return;
if (pWindow->m_bPinned && pWorkspace->m_bIsSpecialWorkspace)
return;
const bool FULLSCREEN = pWindow->m_bIsFullscreen;
const auto FULLSCREENMODE = getWorkspaceByID(pWindow->m_iWorkspaceID)->m_efFullscreenMode;
@@ -2354,6 +2463,7 @@ void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorks
pWindow->moveToWorkspace(pWorkspace->m_iID);
pWindow->updateToplevel();
pWindow->updateDynamicRules();
if (!pWindow->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow);
@@ -2372,6 +2482,97 @@ void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorks
pWindow->m_vRealPosition = POSTOMON + PWORKSPACEMONITOR->vecPosition;
}
if (pWindow->m_sGroupData.pNextWindow) {
CWindow* next = pWindow->m_sGroupData.pNextWindow;
while (next != pWindow) {
next->moveToWorkspace(pWorkspace->m_iID);
next->updateToplevel();
next = next->m_sGroupData.pNextWindow;
}
}
if (FULLSCREEN)
setWindowFullscreen(pWindow, true, FULLSCREENMODE);
}
CWindow* CCompositor::getForceFocus() {
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped || w->isHidden() || !isWorkspaceVisible(w->m_iWorkspaceID))
continue;
if (!w->m_bStayFocused)
continue;
return w.get();
}
return nullptr;
}
void CCompositor::notifyIdleActivity() {
wlr_idle_notify_activity(g_pCompositor->m_sWLRIdle, g_pCompositor->m_sSeat.seat);
wlr_idle_notifier_v1_notify_activity(g_pCompositor->m_sWLRIdleNotifier, g_pCompositor->m_sSeat.seat);
}
void CCompositor::setIdleActivityInhibit(bool enabled) {
wlr_idle_set_enabled(g_pCompositor->m_sWLRIdle, g_pCompositor->m_sSeat.seat, enabled);
wlr_idle_notifier_v1_set_inhibited(g_pCompositor->m_sWLRIdleNotifier, !enabled);
}
void CCompositor::arrangeMonitors() {
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
std::vector<CMonitor*> toArrange;
std::vector<CMonitor*> arranged;
for (auto& m : m_vMonitors)
toArrange.push_back(m.get());
Debug::log(LOG, "arrangeMonitors: {} to arrange", toArrange.size());
for (auto it = toArrange.begin(); it != toArrange.end();) {
auto m = *it;
if (m->activeMonitorRule.offset != Vector2D{-INT32_MAX, -INT32_MAX}) {
// explicit.
Debug::log(LOG, "arrangeMonitors: {} explicit {:j2}", m->szName, m->activeMonitorRule.offset);
m->moveTo(m->activeMonitorRule.offset);
arranged.push_back(m);
it = toArrange.erase(it);
if (it == toArrange.end())
break;
continue;
}
++it;
}
// auto left
int maxOffset = 0;
for (auto& m : arranged) {
if (m->vecPosition.x + m->vecSize.x > maxOffset)
maxOffset = m->vecPosition.x + m->vecSize.x;
}
for (auto& m : toArrange) {
Debug::log(LOG, "arrangeMonitors: {} auto [{}, {:.2f}]", m->szName, maxOffset, 0.f);
m->moveTo({maxOffset, 0});
maxOffset += m->vecSize.x;
}
// reset maxOffset (reuse)
// and set xwayland positions aka auto for all
maxOffset = 0;
for (auto& m : m_vMonitors) {
Debug::log(LOG, "arrangeMonitors: {} xwayland [{}, {:.2f}]", m->szName, maxOffset, 0.f);
m->vecXWaylandPosition = {maxOffset, 0};
maxOffset += (*PXWLFORCESCALEZERO ? m->vecTransformedSize.x : m->vecSize.x);
if (*PXWLFORCESCALEZERO)
m->xwaylandScale = m->scale;
else
m->xwaylandScale = 1.f;
}
}

View File

@@ -28,8 +28,7 @@
#include "hyprerror/HyprError.hpp"
#include "plugins/PluginSystem.hpp"
enum eManagersInitStage
{
enum eManagersInitStage {
STAGE_PRIORITY = 0,
STAGE_LATE
};
@@ -54,6 +53,7 @@ class CCompositor {
wlr_xdg_activation_v1* m_sWLRXDGActivation;
wlr_output_layout* m_sWLROutputLayout;
wlr_idle* m_sWLRIdle;
wlr_idle_notifier_v1* m_sWLRIdleNotifier;
wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell;
wlr_cursor* m_sWLRCursor;
@@ -61,12 +61,10 @@ class CCompositor {
wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr;
wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation;
wlr_scene* m_sWLRScene;
wlr_input_inhibit_manager* m_sWLRInhibitMgr;
wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_ext_workspace_manager_v1* m_sWLREXTWorkspaceMgr;
wlr_pointer_constraints_v1* m_sWLRPointerConstraints;
wlr_relative_pointer_manager_v1* m_sWLRRelPointerMgr;
wlr_server_decoration_manager* m_sWLRServerDecoMgr;
@@ -84,6 +82,8 @@ class CCompositor {
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;
// ------------------------------------------------- //
std::string m_szWLDisplaySocket = "";
@@ -99,9 +99,13 @@ class CCompositor {
std::vector<CWindow*> m_vWindowsFadingOut;
std::vector<SLayerSurface*> m_vSurfacesFadingOut;
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
void initServer();
void startCompositor();
void cleanup();
void createLockFile();
void removeLockFile();
wlr_surface* m_pLastFocus = nullptr;
CWindow* m_pLastWindow = nullptr;
@@ -121,6 +125,7 @@ class CCompositor {
CMonitor* getMonitorFromID(const int&);
CMonitor* getMonitorFromName(const std::string&);
CMonitor* getMonitorFromDesc(const std::string&);
CMonitor* getMonitorFromCursor();
CMonitor* getMonitorFromVector(const Vector2D&);
void removeWindowFromVectorSafe(CWindow*);
@@ -150,13 +155,13 @@ class CCompositor {
CWindow* getUrgentWindow();
bool hasUrgentWindowOnWorkspace(const int&);
CWindow* getFirstWindowOnWorkspace(const int&);
CWindow* getTopLeftWindowOnWorkspace(const int&);
CWindow* getFullscreenWindowOnWorkspace(const int&);
bool doesSeatAcceptInput(wlr_surface*);
bool isWindowActive(CWindow*);
void moveWindowToTop(CWindow*);
void changeWindowZOrder(CWindow*, bool);
void cleanupFadingOut(const int& monid);
CWindow* getWindowInDirection(CWindow*, char);
void deactivateAllWLRWorkspaces(wlr_ext_workspace_handle_v1* exclude = nullptr);
CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false);
CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false);
int getNextAvailableNamedWorkspace();
@@ -165,11 +170,11 @@ class CCompositor {
CMonitor* getMonitorInDirection(const char&);
void updateAllWindowsAnimatedDecorationValues();
void updateWindowAnimatedDecorationValues(CWindow*);
int getNextAvailableMonitorID();
int getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int&);
bool workspaceIDOutOfBounds(const int64_t&);
void setWindowFullscreen(CWindow*, bool, eFullscreenMode);
void updateFullscreenFadeOnWorkspace(CWorkspace*);
CWindow* getX11Parent(CWindow*);
@@ -191,6 +196,10 @@ class CCompositor {
int getNewSpecialID();
void performUserChecks();
void moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace);
CWindow* getForceFocus();
void notifyIdleActivity();
void setIdleActivityInhibit(bool inhibit);
void arrangeMonitors();
std::string explicitConfigPath;

View File

@@ -20,4 +20,6 @@ enum eRenderStage
RENDER_LAST_MOMENT, /* Last moment to render with the gl context */
RENDER_POST, /* After rendering is finished, gl context not available anymore */
RENDER_POST_MIRROR, /* After rendering a mirror */
RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */
RENDER_POST_WINDOW, /* After rendering a window (any pass) */
};

View File

@@ -1,6 +1,7 @@
#include "Window.hpp"
#include "Compositor.hpp"
#include "render/decorations/CHyprDropShadowDecoration.hpp"
#include "render/decorations/CHyprGroupBarDecoration.hpp"
CWindow::CWindow() {
m_vRealPosition.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE);
@@ -22,15 +23,19 @@ CWindow::~CWindow() {
}
}
wlr_box CWindow::getFullWindowBoundingBox() {
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
SWindowDecorationExtents CWindow::getFullWindowExtents() {
if (m_bFadingOut)
return m_eOriginalClosedExtents;
const int BORDERSIZE = getRealBorderSize();
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
return {{m_vRealPosition.vec().x - PMONITOR->vecPosition.x, m_vRealPosition.vec().y - PMONITOR->vecPosition.y},
{PMONITOR->vecSize.x - (m_vRealPosition.vec().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.vec().y - PMONITOR->vecPosition.y)}};
}
SWindowDecorationExtents maxExtents = {{*PBORDERSIZE + 2, *PBORDERSIZE + 2}, {*PBORDERSIZE + 2, *PBORDERSIZE + 2}};
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
for (auto& wd : m_dWindowDecorations) {
@@ -49,7 +54,48 @@ wlr_box CWindow::getFullWindowBoundingBox() {
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
}
// Add extents to the real base BB and return
if (m_pWLSurface.exists() && !m_bIsX11) {
wlr_box surfaceExtents = {0, 0, 0, 0};
// TODO: this could be better, perhaps make a getFullWindowRegion?
wlr_xdg_surface_for_each_popup_surface(
m_uSurface.xdg,
[](wlr_surface* surf, int sx, int sy, void* data) {
wlr_box* pSurfaceExtents = (wlr_box*)data;
if (sx < pSurfaceExtents->x)
pSurfaceExtents->x = sx;
if (sy < pSurfaceExtents->y)
pSurfaceExtents->y = sy;
if (sx + surf->current.width > pSurfaceExtents->width)
pSurfaceExtents->width = sx + surf->current.width - pSurfaceExtents->x;
if (sy + surf->current.height > pSurfaceExtents->height)
pSurfaceExtents->height = sy + surf->current.height - pSurfaceExtents->y;
},
&surfaceExtents);
if (-surfaceExtents.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = -surfaceExtents.x;
if (-surfaceExtents.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = -surfaceExtents.y;
if (surfaceExtents.x + surfaceExtents.width > m_pWLSurface.wlr()->current.width + maxExtents.bottomRight.x)
maxExtents.bottomRight.x = surfaceExtents.x + surfaceExtents.width - m_pWLSurface.wlr()->current.width;
if (surfaceExtents.y + surfaceExtents.height > m_pWLSurface.wlr()->current.height + maxExtents.bottomRight.y)
maxExtents.bottomRight.y = surfaceExtents.y + surfaceExtents.height - m_pWLSurface.wlr()->current.height;
}
return maxExtents;
}
wlr_box CWindow::getFullWindowBoundingBox() {
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
auto maxExtents = getFullWindowExtents();
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
@@ -89,14 +135,14 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
}
wlr_box CWindow::getWindowInputBox() {
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
const int BORDERSIZE = getRealBorderSize();
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
SWindowDecorationExtents maxExtents = {{*PBORDERSIZE + 2, *PBORDERSIZE + 2}, {*PBORDERSIZE + 2, *PBORDERSIZE + 2}};
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
for (auto& wd : m_dWindowDecorations) {
@@ -145,16 +191,22 @@ void CWindow::updateWindowDecos() {
for (auto& wd : m_dWindowDecorations)
wd->updateWindow(this);
bool recalc = false;
for (auto& wd : m_vDecosToRemove) {
for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) {
if (it->get() == wd) {
it = m_dWindowDecorations.erase(it);
it = m_dWindowDecorations.erase(it);
recalc = true;
if (it == m_dWindowDecorations.end())
break;
}
}
}
if (recalc)
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
m_vDecosToRemove.clear();
}
@@ -197,7 +249,7 @@ void CWindow::createToplevelHandle() {
// handle events
hyprListener_toplevelActivate.initCallback(
&m_phForeignToplevel->events.request_activate, [&](void* owner, void* data) { g_pCompositor->focusWindow(this); }, this, "Toplevel");
&m_phForeignToplevel->events.request_activate, [&](void* owner, void* data) { g_pLayoutManager->getCurrentLayout()->requestFocusForWindow(this); }, this, "Toplevel");
hyprListener_toplevelFullscreen.initCallback(
&m_phForeignToplevel->events.request_fullscreen,
@@ -268,32 +320,6 @@ void CWindow::updateSurfaceOutputs() {
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output);
}
void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID == workspaceID)
return;
m_iWorkspaceID = workspaceID;
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
if (PWORKSPACE) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", getFormat("%lx,%s", this, PWORKSPACE->m_szName.c_str())});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{this, PWORKSPACE}));
}
if (m_pSwallowed) {
m_pSwallowed->moveToWorkspace(workspaceID);
m_pSwallowed->m_iMonitorID = m_iMonitorID;
}
if (PMONITOR)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(m_pWLSurface.wlr(), PMONITOR->scale);
if (!m_bIsMapped)
return;
wlr_surface_for_each_surface(
m_pWLSurface.wlr(),
@@ -304,6 +330,30 @@ void CWindow::moveToWorkspace(int workspaceID) {
this);
}
void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID == workspaceID)
return;
m_iWorkspaceID = workspaceID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
updateSpecialRenderData();
if (PWORKSPACE) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", std::format("{:x},{}", (uintptr_t)this, PWORKSPACE->m_szName)});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{this, PWORKSPACE}));
}
if (m_pSwallowed) {
m_pSwallowed->moveToWorkspace(workspaceID);
m_pSwallowed->m_iMonitorID = m_iMonitorID;
}
// update xwayland coords
g_pXWaylandManager->setWindowSize(this, m_vRealSize.vec());
}
CWindow* CWindow::X11TransientFor() {
if (!m_bIsX11)
return nullptr;
@@ -337,6 +387,8 @@ void unregisterVar(void* ptr) {
}
void CWindow::onUnmap() {
static auto* const PCLOSEONLASTSPECIAL = &g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty")->intValue;
if (g_pCompositor->m_pLastWindow == this)
g_pCompositor->m_pLastWindow = nullptr;
@@ -356,6 +408,12 @@ void CWindow::onUnmap() {
m_pWLSurface.unassign();
hyprListener_unmapWindow.removeCallback();
if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(m_iWorkspaceID) == 0 && g_pCompositor->isWorkspaceSpecial(m_iWorkspaceID)) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID)
PMONITOR->setSpecialWorkspace(nullptr);
}
}
void CWindow::onMap() {
@@ -388,7 +446,8 @@ void CWindow::onMap() {
g_pCompositor->m_vWindowFocusHistory.push_back(this);
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->events.unmap : &m_uSurface.xdg->events.unmap, &Events::listener_unmapWindow, this, "CWindow");
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this,
"CWindow");
}
void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -426,6 +485,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.forceNoBorder = true;
} else if (r.szRule == "noshadow") {
m_sAdditionalConfigData.forceNoShadow = true;
} else if (r.szRule == "nodim") {
m_sAdditionalConfigData.forceNoDim = true;
} else if (r.szRule == "forcergbx") {
m_sAdditionalConfigData.forceRGBX = true;
} else if (r.szRule == "opaque") {
@@ -434,28 +495,42 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} else if (r.szRule.find("rounding") == 0) {
try {
m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Rounding rule \"%s\" failed with: %s", r.szRule.c_str(), e.what()); }
} catch (std::exception& e) { Debug::log(ERR, "Rounding rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("bordersize") == 0) {
try {
m_sAdditionalConfigData.borderSize = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Bordersize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("opacity") == 0) {
try {
CVarList vars(r.szRule, 0, ' ');
for (size_t i = 1 /* first item is "opacity" */; i < vars.size(); ++i) {
if (i == 1) {
// first arg, alpha
m_sSpecialRenderData.alpha = std::stof(vars[i]);
int opacityIDX = 0;
for (auto& r : vars) {
if (r == "opacity")
continue;
if (r == "override") {
if (opacityIDX == 1) {
m_sSpecialRenderData.alphaOverride = true;
m_sSpecialRenderData.alphaInactiveOverride = true;
} else if (opacityIDX == 2)
m_sSpecialRenderData.alphaInactiveOverride = true;
} else {
if (vars[i] == "override") {
if (i == 2) {
m_sSpecialRenderData.alphaOverride = true;
} else {
m_sSpecialRenderData.alphaInactiveOverride = true;
}
if (opacityIDX == 0) {
m_sSpecialRenderData.alpha = std::stof(r);
m_sSpecialRenderData.alphaInactive = std::stof(r);
} else if (opacityIDX == 1) {
m_sSpecialRenderData.alphaInactive = std::stof(r);
m_sSpecialRenderData.alphaInactiveOverride = false;
} else {
m_sSpecialRenderData.alphaInactive = std::stof(vars[i]);
throw std::runtime_error("more than 2 alpha values");
}
opacityIDX++;
}
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"%s\" failed with: %s", r.szRule.c_str(), e.what()); }
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "noanim") {
m_sAdditionalConfigData.forceNoAnims = true;
} else if (r.szRule.find("animation") == 0) {
@@ -472,9 +547,17 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} else {
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart);
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"%s\" failed with: %s", r.szRule.c_str(), e.what()); }
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "dimaround") {
m_sAdditionalConfigData.dimAround = true;
} else if (r.szRule == "keepaspectratio") {
m_sAdditionalConfigData.keepAspectRatio = true;
} else if (r.szRule.find("xray") == 0) {
CVarList vars(r.szRule, 0, ' ');
try {
m_sAdditionalConfigData.xray = configStringToInt(vars[1]);
} catch (...) {}
}
}
@@ -486,47 +569,51 @@ void CWindow::updateDynamicRules() {
m_sAdditionalConfigData.forceNoBlur = false;
m_sAdditionalConfigData.forceNoBorder = false;
m_sAdditionalConfigData.forceNoShadow = false;
m_sAdditionalConfigData.forceNoDim = false;
if (!m_sAdditionalConfigData.forceOpaqueOverridden)
m_sAdditionalConfigData.forceOpaque = false;
m_sAdditionalConfigData.forceNoAnims = false;
m_sAdditionalConfigData.animationStyle = std::string("");
m_sAdditionalConfigData.rounding = -1;
m_sAdditionalConfigData.dimAround = false;
m_sAdditionalConfigData.forceRGBX = false;
m_sAdditionalConfigData.forceNoAnims = false;
m_sAdditionalConfigData.animationStyle = std::string("");
m_sAdditionalConfigData.rounding = -1;
m_sAdditionalConfigData.dimAround = false;
m_sAdditionalConfigData.forceRGBX = false;
m_sAdditionalConfigData.borderSize = -1;
m_sAdditionalConfigData.keepAspectRatio = false;
m_sAdditionalConfigData.xray = -1;
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this);
for (auto& r : WINDOWRULES) {
applyDynamicRule(r);
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_iMonitorID);
}
// check if the point is "hidden" under a rounded corner of the window
// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize)
// otherwise behaviour is undefined
bool CWindow::isInCurvedCorner(double x, double y) {
static auto* const ROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
static auto* const BORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
if (BORDERSIZE >= ROUNDING || ROUNDING == 0)
const int ROUNDING = rounding();
if (getRealBorderSize() >= ROUNDING)
return false;
// (x0, y0), (x0, y1), ... are the center point of rounding at each corner
double x0 = m_vRealPosition.vec().x + *ROUNDING;
double y0 = m_vRealPosition.vec().y + *ROUNDING;
double x1 = m_vRealPosition.vec().x + m_vRealSize.vec().x - *ROUNDING;
double y1 = m_vRealPosition.vec().y + m_vRealSize.vec().y - *ROUNDING;
double x0 = m_vRealPosition.vec().x + ROUNDING;
double y0 = m_vRealPosition.vec().y + ROUNDING;
double x1 = m_vRealPosition.vec().x + m_vRealSize.vec().x - ROUNDING;
double y1 = m_vRealPosition.vec().y + m_vRealSize.vec().y - ROUNDING;
if (x < x0 && y < y0) {
return Vector2D{x0, y0}.distance(Vector2D{x, y}) > (double)*ROUNDING;
return Vector2D{x0, y0}.distance(Vector2D{x, y}) > (double)ROUNDING;
}
if (x > x1 && y < y0) {
return Vector2D{x1, y0}.distance(Vector2D{x, y}) > (double)*ROUNDING;
return Vector2D{x1, y0}.distance(Vector2D{x, y}) > (double)ROUNDING;
}
if (x < x0 && y > y1) {
return Vector2D{x0, y1}.distance(Vector2D{x, y}) > (double)*ROUNDING;
return Vector2D{x0, y1}.distance(Vector2D{x, y}) > (double)ROUNDING;
}
if (x > x1 && y > y1) {
return Vector2D{x1, y1}.distance(Vector2D{x, y}) > (double)*ROUNDING;
return Vector2D{x1, y1}.distance(Vector2D{x, y}) > (double)ROUNDING;
}
return false;
@@ -554,6 +641,69 @@ bool CWindow::hasPopupAt(const Vector2D& pos) {
return resultSurf;
}
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))
getGroupHead()->m_sGroupData.locked = true;
}
void CWindow::createGroup() {
if (m_sGroupData.deny) {
Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle);
return;
}
if (!m_sGroupData.pNextWindow) {
m_sGroupData.pNextWindow = this;
m_sGroupData.head = true;
m_sGroupData.locked = false;
m_sGroupData.deny = false;
m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(this));
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
}
}
void CWindow::destroyGroup() {
if (m_sGroupData.pNextWindow == 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;
updateWindowDecos();
return;
}
CWindow* curr = this;
std::vector<CWindow*> members;
do {
const auto PLASTWIN = curr;
curr = curr->m_sGroupData.pNextWindow;
PLASTWIN->m_sGroupData.pNextWindow = nullptr;
curr->setHidden(false);
members.push_back(curr);
} while (curr != this);
for (auto& w : members) {
if (w->m_sGroupData.head)
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr);
w->m_sGroupData.head = false;
}
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked;
g_pKeybindManager->m_bGroupsLocked = true;
for (auto& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
w->updateWindowDecos();
}
g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV;
}
CWindow* CWindow::getGroupHead() {
CWindow* curr = this;
while (!curr->m_sGroupData.head)
@@ -575,6 +725,27 @@ CWindow* CWindow::getGroupCurrent() {
return curr;
}
int CWindow::getGroupSize() {
int size = 1;
CWindow* curr = this;
while (curr->m_sGroupData.pNextWindow != this) {
curr = curr->m_sGroupData.pNextWindow;
size++;
}
return size;
}
CWindow* CWindow::getGroupWindowByIndex(int index) {
const int SIZE = getGroupSize();
index = ((index % SIZE) + SIZE) % SIZE;
CWindow* curr = getGroupHead();
while (index > 0) {
curr = curr->m_sGroupData.pNextWindow;
index--;
}
return curr;
}
void CWindow::setGroupCurrent(CWindow* pWindow) {
CWindow* curr = this->m_sGroupData.pNextWindow;
bool isMember = false;
@@ -602,7 +773,7 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
g_pCompositor->setWindowFullscreen(PCURRENT, false, WORKSPACE->m_efFullscreenMode);
PCURRENT->setHidden(true);
pWindow->setHidden(false);
pWindow->setHidden(false); // can remove m_pLastWindow
g_pLayoutManager->getCurrentLayout()->replaceWindowDataWith(PCURRENT, pWindow);
@@ -618,34 +789,62 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
if (FULLSCREEN)
g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
g_pHyprRenderer->damageWindow(pWindow);
}
void CWindow::insertWindowToGroup(CWindow* pWindow) {
const auto PHEAD = getGroupHead();
const auto PTAIL = getGroupTail();
const auto BEGINAT = this;
const auto ENDAT = m_sGroupData.pNextWindow;
if (pWindow->m_sGroupData.pNextWindow) {
std::vector<CWindow*> members;
CWindow* curr = pWindow;
do {
const auto PLAST = curr;
members.push_back(curr);
curr = curr->m_sGroupData.pNextWindow;
PLAST->m_sGroupData.pNextWindow = nullptr;
PLAST->m_sGroupData.head = false;
} while (curr != pWindow);
for (auto& w : members) {
insertWindowToGroup(w);
}
if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
if (!pWindow->m_sGroupData.pNextWindow) {
BEGINAT->m_sGroupData.pNextWindow = pWindow;
pWindow->m_sGroupData.pNextWindow = ENDAT;
pWindow->m_sGroupData.head = false;
return;
}
PTAIL->m_sGroupData.pNextWindow = pWindow;
pWindow->m_sGroupData.pNextWindow = PHEAD;
const auto SHEAD = pWindow->getGroupHead();
const auto STAIL = pWindow->getGroupTail();
setGroupCurrent(pWindow);
SHEAD->m_sGroupData.head = false;
BEGINAT->m_sGroupData.pNextWindow = SHEAD;
STAIL->m_sGroupData.pNextWindow = ENDAT;
}
CWindow* CWindow::getGroupPrevious() {
CWindow* curr = m_sGroupData.pNextWindow;
while (curr != this && curr->m_sGroupData.pNextWindow != this)
curr = curr->m_sGroupData.pNextWindow;
return curr;
}
void CWindow::switchWithWindowInGroup(CWindow* pWindow) {
if (!m_sGroupData.pNextWindow || !pWindow->m_sGroupData.pNextWindow)
return;
if (m_sGroupData.pNextWindow == 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;
} else if (pWindow->m_sGroupData.pNextWindow == this) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
pWindow->getGroupPrevious()->m_sGroupData.pNextWindow = this;
pWindow->m_sGroupData.pNextWindow = m_sGroupData.pNextWindow;
m_sGroupData.pNextWindow = pWindow;
} else { // A -> this -> B | C -> pWindow -> D >> A -> pWindow -> B | C -> this -> D
std::swap(m_sGroupData.pNextWindow, pWindow->m_sGroupData.pNextWindow);
std::swap(getGroupPrevious()->m_sGroupData.pNextWindow, pWindow->getGroupPrevious()->m_sGroupData.pNextWindow);
}
std::swap(m_sGroupData.head, pWindow->m_sGroupData.head);
std::swap(m_sGroupData.locked, pWindow->m_sGroupData.locked);
}
void CWindow::updateGroupOutputs() {
@@ -670,6 +869,14 @@ Vector2D CWindow::middle() {
}
bool CWindow::opaque() {
if (m_fAlpha.fl() != 1.f || m_fActiveInactiveAlpha.fl() != 1.f)
return false;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
if (PWORKSPACE->m_fAlpha.fl() != 1.f)
return false;
if (m_bIsX11)
return !m_uSurface.xwayland->has_alpha;
@@ -677,9 +884,44 @@ bool CWindow::opaque() {
return true;
const auto EXTENTS = pixman_region32_extents(&m_uSurface.xdg->surface->opaque_region);
if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width
&& EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height)
if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height)
return true;
return false;
}
float CWindow::rounding() {
static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
float rounding = m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_sAdditionalConfigData.rounding.toUnderlying();
return rounding;
}
void CWindow::updateSpecialRenderData() {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{};
bool border = true;
if (m_bIsFloating && g_pConfigManager->getConfigValuePtr("general:no_border_on_floating")->intValue == 1)
border = false;
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)
return 0;
if (m_sAdditionalConfigData.borderSize.toUnderlying() != -1)
return m_sAdditionalConfigData.borderSize.toUnderlying();
if (m_sSpecialRenderData.borderSize.toUnderlying() != -1)
return m_sSpecialRenderData.borderSize.toUnderlying();
return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include "defines.hpp"
#include "events/Events.hpp"
#include "helpers/SubsurfaceTree.hpp"
#include "helpers/AnimatedVariable.hpp"
#include "render/decorations/IHyprWindowDecoration.hpp"
@@ -9,6 +8,8 @@
#include "config/ConfigDataValues.hpp"
#include "helpers/Vector2D.hpp"
#include "helpers/WLSurface.hpp"
#include "macros.hpp"
#include "managers/XWaylandManager.hpp"
enum eIdleInhibitMode {
IDLEINHIBIT_NONE = 0,
@@ -17,6 +18,18 @@ enum eIdleInhibitMode {
IDLEINHIBIT_FOCUS
};
enum eGroupRules {
// effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0,
GROUP_SET = 1 << 0, // Open as new group or add to focused group
GROUP_SET_ALWAYS = 1 << 1,
GROUP_BARRED = 1 << 2, // Don't insert to focused group.
GROUP_LOCK = 1 << 3, // Lock m_sGroupData.lock
GROUP_LOCK_ALWAYS = 1 << 4,
GROUP_INVADE = 1 << 5, // Force enter a group, event if lock is engaged
GROUP_OVERRIDE = 1 << 6, // Override other rules
};
template <typename T>
class CWindowOverridableVar {
public:
@@ -100,10 +113,11 @@ struct SWindowSpecialRenderData {
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset
// set by the layout
int borderSize = -1;
bool rounding = true;
bool border = true;
bool decorate = true;
CWindowOverridableVar<int> borderSize = -1; // -1 means unset
bool rounding = true;
bool border = true;
bool decorate = true;
bool shadow = true;
};
struct SWindowAdditionalConfigData {
@@ -116,10 +130,14 @@ struct SWindowAdditionalConfigData {
CWindowOverridableVar<bool> forceNoAnims = false;
CWindowOverridableVar<bool> forceNoBorder = false;
CWindowOverridableVar<bool> forceNoShadow = false;
CWindowOverridableVar<bool> forceNoDim = false;
CWindowOverridableVar<bool> windowDanceCompat = false;
CWindowOverridableVar<bool> noMaxSize = false;
CWindowOverridableVar<bool> dimAround = false;
CWindowOverridableVar<bool> forceRGBX = false;
CWindowOverridableVar<bool> keepAspectRatio = false;
CWindowOverridableVar<int> xray = -1; // -1 means unset, takes precedence over the renderdata one
CWindowOverridableVar<int> borderSize = -1; // -1 means unset, takes precedence over the renderdata one
};
struct SWindowRule {
@@ -133,6 +151,7 @@ struct SWindowRule {
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
std::string szWorkspace = ""; // empty means any
};
class CWindow {
@@ -158,6 +177,8 @@ class CWindow {
DYNLISTENER(toplevelActivate);
DYNLISTENER(toplevelFullscreen);
DYNLISTENER(setOverrideRedirect);
DYNLISTENER(associateX11);
DYNLISTENER(dissociateX11);
// DYNLISTENER(newSubsurfaceWindow);
CWLSurface m_pWLSurface;
@@ -188,7 +209,7 @@ class CWindow {
bool m_bIsPseudotiled = false;
Vector2D m_vPseudoSize = Vector2D(0, 0);
uint64_t m_iTags = 0;
bool m_bFirstMap = false; // for layouts
bool m_bIsFloating = false;
bool m_bDraggingTiled = false; // for dragging around tiled windows
bool m_bIsFullscreen = false;
@@ -214,15 +235,17 @@ class CWindow {
bool m_bIsModal = false;
bool m_bX11DoesntWantBorders = false;
bool m_bX11ShouldntFocus = false;
float m_fX11SurfaceScaledBy = 1.f;
//
// For nofocus
bool m_bNoFocus = false;
bool m_bNoInitialFocus = false;
// initial fullscreen and fullscreen disabled
// Fullscreen and Maximize
bool m_bWantsInitialFullscreen = false;
bool m_bNoFullscreenRequest = false;
bool m_bNoMaximizeRequest = false;
SSurfaceTreeNode* m_pSurfaceTree = nullptr;
@@ -233,11 +256,12 @@ class CWindow {
CAnimatedVariable m_fBorderAngleAnimationProgress;
// Fade in-out
CAnimatedVariable m_fAlpha;
bool m_bFadingOut = false;
bool m_bReadyToDelete = false;
Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in
Vector2D m_vOriginalClosedSize; // drawing the closing animations
CAnimatedVariable m_fAlpha;
bool m_bFadingOut = false;
bool m_bReadyToDelete = false;
Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in
Vector2D m_vOriginalClosedSize; // drawing the closing animations
SWindowDecorationExtents m_eOriginalClosedExtents;
// For pinned (sticky) windows
bool m_bPinned = false;
@@ -274,6 +298,9 @@ class CWindow {
// swallowing
CWindow* m_pSwallowed = nullptr;
// focus stuff
bool m_bStayFocused = false;
// for toplevel monitor events
uint64_t m_iLastToplevelMonitorID = -1;
uint64_t m_iLastSurfaceMonitorID = -1;
@@ -285,7 +312,10 @@ class CWindow {
struct SGroupData {
CWindow* pNextWindow = nullptr; // 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
} m_sGroupData;
uint16_t m_eGroupRules = GROUP_NONE;
// For the list lookup
bool operator==(const CWindow& rhs) {
@@ -295,6 +325,7 @@ class CWindow {
// methods
wlr_box getFullWindowBoundingBox();
SWindowDecorationExtents getFullWindowExtents();
wlr_box getWindowInputBox();
wlr_box getWindowIdealBoundingBoxIgnoreReserved();
void updateWindowDecos();
@@ -316,19 +347,71 @@ class CWindow {
SWindowDecorationExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
float rounding();
int getRealBorderSize();
void updateSpecialRenderData();
void onBorderAngleAnimEnd(void* ptr);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
void applyGroupRules();
void createGroup();
void destroyGroup();
CWindow* getGroupHead();
CWindow* getGroupTail();
CWindow* getGroupCurrent();
CWindow* getGroupPrevious();
CWindow* getGroupWindowByIndex(int);
int getGroupSize();
void setGroupCurrent(CWindow* pWindow);
void insertWindowToGroup(CWindow* pWindow);
void updateGroupOutputs();
void switchWithWindowInGroup(CWindow* pWindow);
private:
// For hidden windows and stuff
bool m_bHidden = false;
};
/**
format specification
- 'x', only address, equivalent of (uintpr_t)CWindow*
- 'm', with monitor id
- 'w', with workspace id
- 'c', with application class
*/
template <typename CharT>
struct std::formatter<CWindow*, CharT> : std::formatter<CharT> {
bool formatAddressOnly = false;
bool formatWorkspace = false;
bool formatMonitor = false;
bool formatClass = false;
FORMAT_PARSE( //
FORMAT_FLAG('x', formatAddressOnly) //
FORMAT_FLAG('m', formatMonitor) //
FORMAT_FLAG('w', formatWorkspace) //
FORMAT_FLAG('c', formatClass),
CWindow*)
template <typename FormatContext>
auto format(CWindow* const& w, FormatContext& ctx) const {
auto&& out = ctx.out();
if (formatAddressOnly)
return std::format_to(out, "{:x}", (uintptr_t)w);
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);
if (formatWorkspace)
std::format_to(out, ", workspace: {}", w->m_iWorkspaceID);
if (formatMonitor)
std::format_to(out, ", monitor: {}", w->m_iMonitorID);
if (formatClass)
std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w));
return std::format_to(out, "]");
}
};

View File

@@ -2,13 +2,12 @@
#include "../defines.hpp"
#include <vector>
enum eConfigValueDataTypes
{
enum eConfigValueDataTypes {
CVD_TYPE_INVALID = -1,
CVD_TYPE_GRADIENT = 0
};
interface ICustomConfigValueData {
class ICustomConfigValueData {
public:
virtual ~ICustomConfigValueData() = 0;
@@ -38,7 +37,8 @@ class CGradientValueData : public ICustomConfigValueData {
/* Float corresponding to the angle (rad) */
float m_fAngle = 0;
bool operator==(const CGradientValueData& other) {
//
bool operator==(const CGradientValueData& other) const {
if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle)
return false;

File diff suppressed because it is too large Load Diff

View File

@@ -11,14 +11,15 @@
#include <algorithm>
#include <regex>
#include <optional>
#include <xf86drmMode.h>
#include "../Window.hpp"
#include "../helpers/WLClasses.hpp"
#include "../helpers/Monitor.hpp"
#include "../helpers/VarList.hpp"
#include "defaultConfig.hpp"
#include "ConfigDataValues.hpp"
#define STRVAL_EMPTY "[[EMPTY]]"
#define INITANIMCFG(name) animationConfig[name] = {}
#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]}
@@ -34,29 +35,19 @@ struct SConfigValue {
bool set = false; // used for device configs
};
struct SMonitorRule {
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60;
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
};
struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
std::string workspaceName = "";
int workspaceId = -1;
bool isDefault = false;
std::optional<int64_t> gapsIn;
std::optional<int64_t> gapsOut;
std::optional<int64_t> borderSize;
std::optional<int> border;
std::optional<int> rounding;
std::optional<int> decorate;
std::optional<int> shadow;
};
struct SMonitorAdditionalReservedArea {
@@ -83,62 +74,6 @@ struct SExecRequestedRule {
uint64_t iPid = 0;
};
class CVarList {
public:
CVarList(const std::string& in, long unsigned int lastArgNo = 0, const char separator = ',') {
std::string curitem = "";
std::string argZ = in;
auto nextItem = [&]() {
auto idx = lastArgNo != 0 && m_vArgs.size() >= lastArgNo - 1 ? std::string::npos : argZ.find_first_of(separator);
if (idx != std::string::npos) {
curitem = argZ.substr(0, idx);
argZ = argZ.substr(idx + 1);
} else {
curitem = argZ;
argZ = STRVAL_EMPTY;
}
};
nextItem();
while (curitem != STRVAL_EMPTY) {
m_vArgs.push_back(removeBeginEndSpacesTabs(curitem));
nextItem();
}
};
~CVarList() = default;
size_t size() const {
return m_vArgs.size();
}
std::string operator[](const long unsigned int& idx) const {
if (idx >= m_vArgs.size())
return "";
return m_vArgs[idx];
}
// for range-based loops
std::vector<std::string>::iterator begin() {
return m_vArgs.begin();
}
std::vector<std::string>::const_iterator begin() const {
return m_vArgs.begin();
}
std::vector<std::string>::iterator end() {
return m_vArgs.end();
}
std::vector<std::string>::const_iterator end() const {
return m_vArgs.end();
}
private:
std::vector<std::string> m_vArgs;
};
class CConfigManager {
public:
CConfigManager();
@@ -153,14 +88,16 @@ class CConfigManager {
void setInt(const std::string&, int);
void setString(const std::string&, const std::string&);
int getDeviceInt(const std::string&, const std::string&);
float getDeviceFloat(const std::string&, const std::string&);
std::string getDeviceString(const std::string&, const std::string&);
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = "");
bool deviceConfigExists(const std::string&);
bool shouldBlurLS(const std::string&);
SConfigValue* getConfigValuePtr(const std::string&);
SConfigValue* getConfigValuePtrSafe(const std::string&);
static std::string getConfigDir();
static std::string getMainConfigPath();
SMonitorRule getMonitorRuleFor(const std::string&, const std::string& displayName = "");
SWorkspaceRule getWorkspaceRuleFor(CWorkspace*);
@@ -168,6 +105,7 @@ class CConfigManager {
CMonitor* getBoundMonitorForWS(const std::string&);
std::string getBoundMonitorStringForWS(const std::string&);
const std::deque<SWorkspaceRule>& getAllWorkspaceRules();
std::vector<SWindowRule> getMatchingRules(CWindow*);
std::vector<SLayerRule> getMatchingRules(SLayerSurface*);
@@ -216,8 +154,6 @@ class CConfigManager {
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
std::vector<std::pair<std::string, std::string>> boundWorkspaces;
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<std::string> m_vDeclaredPlugins;
@@ -226,7 +162,7 @@ class CConfigManager {
bool isFirstLaunch = true; // For exec-once
std::deque<SMonitorRule> m_dMonitorRules;
std::unordered_map<int, SWorkspaceRule> m_mWorkspaceRules;
std::deque<SWorkspaceRule> m_dWorkspaceRules;
std::deque<SWindowRule> m_dWindowRules;
std::deque<SLayerRule> m_dLayerRules;
std::deque<std::string> m_dBlurLSNamespaces;
@@ -237,6 +173,8 @@ class CConfigManager {
std::vector<std::pair<std::string, std::string>> environmentVariables;
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
// internal methods
void setDefaultVars();
void setDefaultAnimationVars();
@@ -249,7 +187,7 @@ class CConfigManager {
void applyUserDefinedVars(std::string&, const size_t);
void loadConfigLoadVars();
SConfigValue getConfigValueSafe(const std::string&);
SConfigValue getConfigValueSafeDevice(const std::string&, const std::string&);
SConfigValue getConfigValueSafeDevice(const std::string&, const std::string&, const std::string& fallback = "");
void parseLine(std::string&);
void configSetValueSafe(const std::string&, const std::string&);
void handleDeviceConfig(const std::string&, const std::string&);

View File

@@ -64,10 +64,12 @@ decoration {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
rounding = 10
blur = yes
blur_size = 3
blur_passes = 1
blur_new_optimizations = on
blur {
enabled = true
size = 3
passes = 1
}
drop_shadow = yes
shadow_range = 4

View File

@@ -14,18 +14,19 @@
std::string getRandomMessage() {
const std::vector<std::string> MESSAGES = {"Sorry, didn't mean to...",
"This was an accident, I swear!",
"Calm down, it was a misinput! MISINPUT!",
"Oops",
"Vaxry is going to be upset.",
"Who tried dividing by zero?!",
"Maybe you should try dusting your PC in the meantime?",
"I tried so hard, and got so far...",
"I don't feel so good...",
"*thud*",
"Well this is awkward.",
"\"stable\"",
"I hope you didn't have any unsaved progress."};
"This was an accident, I swear!",
"Calm down, it was a misinput! MISINPUT!",
"Oops",
"Vaxry is going to be upset.",
"Who tried dividing by zero?!",
"Maybe you should try dusting your PC in the meantime?",
"I tried so hard, and got so far...",
"I don't feel so good...",
"*thud*",
"Well this is awkward.",
"\"stable\"",
"I hope you didn't have any unsaved progress.",
"All these computers..."};
std::random_device dev;
std::mt19937 engine(dev());
@@ -44,15 +45,15 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "--------------------------------------------\n Hyprland Crash Report\n--------------------------------------------\n";
finalCrashReport += getRandomMessage() + "\n\n";
finalCrashReport += getFormat("Hyprland received signal %d (%s)\n\n", sig, strsignal(sig));
finalCrashReport += std::format("Hyprland received signal {} ({})\n\n", sig, (const char*)strsignal(sig));
finalCrashReport += getFormat("Version: %s\n\n", GIT_COMMIT_HASH);
finalCrashReport += std::format("Version: {}\nTag: {}\n\n", GIT_COMMIT_HASH, GIT_TAG);
if (g_pPluginSystem && !g_pPluginSystem->getAllPlugins().empty()) {
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 += getFormat("\t%s (%s) %s\n", p->name.c_str(), p->author.c_str(), p->version.c_str());
finalCrashReport += std::format("\t{} ({}) {}\n", p->name, p->author, p->version);
}
finalCrashReport += "\n\n";
@@ -64,7 +65,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
uname(&unameInfo);
finalCrashReport +=
getFormat("\tSystem name: %s\n\tNode name: %s\n\tRelease: %s\n\tVersion: %s\n\n", unameInfo.sysname, unameInfo.nodename, unameInfo.release, unameInfo.version);
std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", unameInfo.sysname, unameInfo.nodename, unameInfo.release, unameInfo.version);
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
@@ -74,16 +75,11 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "GPU:\n\t" + GPUINFO;
finalCrashReport += getFormat("\n\nos-release:\n\t%s\n\n\n", replaceInString(execAndGet("cat /etc/os-release"), "\n", "\n\t").c_str());
finalCrashReport += std::format("\n\nos-release:\n\t{}\n\n\n", replaceInString(execAndGet("cat /etc/os-release"), "\n", "\n\t"));
finalCrashReport += "Backtrace:\n";
void* bt[1024];
size_t btSize;
char** btSymbols;
btSize = backtrace(bt, 1024);
btSymbols = backtrace_symbols(bt, btSize);
const auto CALLSTACK = getBacktrace();
#if defined(KERN_PROC_PATHNAME)
int mib[] = {
@@ -110,20 +106,18 @@ void CrashReporter::createAndSaveCrash(int sig) {
const auto FPATH = std::filesystem::canonical("/proc/self/exe");
#endif
for (size_t i = 0; i < btSize; ++i) {
finalCrashReport += getFormat("\t#%lu | %s\n", i, btSymbols[i]);
for (size_t i = 0; i < CALLSTACK.size(); ++i) {
finalCrashReport += std::format("\t#{} | {}\n", i, CALLSTACK[i].desc);
#ifdef __clang__
const auto CMD = getFormat("llvm-addr2line -e %s -f 0x%lx", FPATH.c_str(), (uint64_t)bt[i]);
const auto CMD = std::format("llvm-addr2line -e {} -f 0x{:x}", FPATH.c_str(), (uint64_t)CALLSTACK[i].adr);
#else
const auto CMD = getFormat("addr2line -e %s -f 0x%lx", FPATH.c_str(), (uint64_t)bt[i]);
const auto CMD = std::format("addr2line -e {} -f 0x{:x}", FPATH.c_str(), (uint64_t)CALLSTACK[i].adr);
#endif
const auto ADDR2LINE = replaceInString(execAndGet(CMD.c_str()), "\n", "\n\t\t");
finalCrashReport += "\t\t" + ADDR2LINE.substr(0, ADDR2LINE.length() - 2);
}
free(btSymbols);
finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += execAndGet(("cat \"" + Debug::logFile + "\" | tail -n 50").c_str());
@@ -135,7 +129,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
return;
std::ofstream ofs;
std::string path;
std::string path;
if (!CACHE_HOME) {
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) {
std::filesystem::create_directory(std::string(HOME) + "/.hyprland");
@@ -162,5 +156,5 @@ void CrashReporter::createAndSaveCrash(int sig) {
ofs.close();
Debug::disableStdout = false;
Debug::log(CRIT, "Hyprland has crashed :( Consult the crash report at %s for more information.", path.c_str());
Debug::log(CRIT, "Hyprland has crashed :( Consult the crash report at {} for more information.", path);
}

View File

@@ -14,6 +14,20 @@
#include <sstream>
#include <string>
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;
}
std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
@@ -23,40 +37,43 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
if (!m->output)
continue;
result += getFormat(
R"#({
"id": %i,
"name": "%s",
"description": "%s",
"make": "%s",
"model": "%s",
"serial": "%s",
"width": %i,
"height": %i,
"refreshRate": %f,
"x": %i,
"y": %i,
"activeWorkspace": {
"id": %i,
"name": "%s"
},
"reserved": [%i, %i, %i, %i],
"scale": %.2f,
"transform": %i,
"focused": %s,
"dpmsStatus": %s,
"vrr": %s
},)#",
m->ID, escapeJSONStrings(m->szName).c_str(), escapeJSONStrings(m->output->description ? m->output->description : "").c_str(),
(m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x,
(int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspace,
escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName).c_str(), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
result += std::format(
R"#({{
"id": {},
"name": "{}",
"description": "{}",
"make": "{}",
"model": "{}",
"serial": "{}",
"width": {},
"height": {},
"refreshRate": {:.5f},
"x": {},
"y": {},
"activeWorkspace": {{
"id": {},
"name": "{}"
}},
"specialWorkspace": {{
"id": {},
"name": "{}"
}},
"reserved": [{}, {}, {}, {}],
"scale": {:.2f},
"transform": {},
"focused": {},
"dpmsStatus": {},
"vrr": {}
}},)#",
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""),
(m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate,
(int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspace, escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName),
m->specialWorkspaceID, escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(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"));
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "]";
} else {
@@ -64,15 +81,17 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
if (!m->output)
continue;
result += getFormat("Monitor %s (ID %i):\n\t%ix%i@%f at %ix%i\n\tdescription: %s\n\tmake: %s\n\tmodel: %s\n\tserial: %s\n\tactive workspace: %i (%s)\n\treserved: %i "
"%i %i %i\n\tscale: %.2f\n\ttransform: "
"%i\n\tfocused: %s\n\tdpmsStatus: %i\n\tvrr: %i\n\n",
m->szName.c_str(), m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->serial ? m->output->serial : ""), m->activeWorkspace, g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName.c_str(),
(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));
result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial "
"workspace: {} ({})\n\treserved: {} "
"{} {} {}\n\tscale: {:.2f}\n\ttransform: "
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->serial ? m->output->serial : ""), m->activeWorkspace, g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName, m->specialWorkspaceID,
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));
}
}
@@ -93,7 +112,6 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
} while (curr != w);
const auto comma = isJson ? ", " : ",";
const auto fmt = isJson ? "\"0x%lx\"" : "%lx";
std::ostringstream result;
bool first = true;
@@ -102,8 +120,10 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
first = false;
else
result << comma;
result << getFormat(fmt, gw);
if (isJson)
result << std::format("\"0x{:x}\"", (uintptr_t)gw);
else
result << std::format("{:x}", (uintptr_t)gw);
}
return result.str();
@@ -111,58 +131,57 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) {
if (format == HyprCtl::FORMAT_JSON) {
return getFormat(
R"#({
"address": "0x%lx",
"mapped": %s,
"hidden": %s,
"at": [%i, %i],
"size": [%i, %i],
"workspace": {
"id": %i,
"name": "%s"
},
"floating": %s,
"monitor": %i,
"class": "%s",
"title": "%s",
"initialClass": "%s",
"initialTitle": "%s",
"pid": %i,
"xwayland": %s,
"pinned": %s,
"fullscreen": %s,
"fullscreenMode": %i,
"fakeFullscreen": %s,
"grouped": [%s],
"swallowing": %s
},)#",
w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
return std::format(
R"#({{
"address": "0x{:x}",
"mapped": {},
"hidden": {},
"at": [{}, {}],
"size": [{}, {}],
"workspace": {{
"id": {},
"name": "{}"
}},
"floating": {},
"monitor": {},
"class": "{}",
"title": "{}",
"initialClass": "{}",
"initialTitle": "{}",
"pid": {},
"xwayland": {},
"pinned": {},
"fullscreen": {},
"fullscreenMode": {},
"fakeFullscreen": {},
"grouped": [{}],
"swallowing": "0x{:x}"
}},)#",
(uintptr_t)w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
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)))
.c_str(),
((int)w->m_bIsFloating == 1 ? "true" : "false"), w->m_iMonitorID, escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)).c_str(),
escapeJSONStrings(g_pXWaylandManager->getTitle(w)).c_str(), escapeJSONStrings(w->m_szInitialClass).c_str(), escapeJSONStrings(w->m_szInitialTitle).c_str(), w->getPID(),
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) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format).c_str(), (w->m_pSwallowed ? getFormat("\"0x%lx\"", w->m_pSwallowed).c_str() : "null"));
(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);
} else {
return getFormat(
"Window %lx -> %s:\n\tmapped: %i\n\thidden: %i\n\tat: %i,%i\n\tsize: %i,%i\n\tworkspace: %i (%s)\n\tfloating: %i\n\tmonitor: %i\n\tclass: %s\n\ttitle: "
"%s\n\tinitialClass: %s\n\tinitialTitle: %s\n\tpid: "
"%i\n\txwayland: %i\n\tpinned: "
"%i\n\tfullscreen: %i\n\tfullscreenmode: %i\n\tfakefullscreen: %i\n\tgrouped: %s\n\tswallowing: %lx\n\n",
w, w->m_szTitle.c_str(), (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (int)w->m_vRealSize.goalv().x,
(int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\n",
(uintptr_t)w, w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
(w->m_iWorkspaceID == -1 ? "" :
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName.c_str() :
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)).c_str()),
(int)w->m_bIsFloating, w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w).c_str(), g_pXWaylandManager->getTitle(w).c_str(), w->m_szInitialClass.c_str(),
w->m_szInitialTitle.c_str(), w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
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).c_str(), w->m_pSwallowed);
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed);
}
}
@@ -175,9 +194,7 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += getWindowData(w.get(), format);
}
// remove trailing comma
if (result != "[")
result.pop_back();
trimTrailingComma(result);
result += "]";
} else {
@@ -192,22 +209,21 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == HyprCtl::FORMAT_JSON) {
return getFormat(R"#({
"id": %i,
"name": "%s",
"monitor": "%s",
"windows": %i,
"hasfullscreen": %s,
"lastwindow": "0x%lx",
"lastwindowtitle": "%s"
})#",
w->m_iID, escapeJSONStrings(w->m_szName).c_str(), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?").c_str(),
g_pCompositor->getWindowsOnWorkspace(w->m_iID), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), PLASTW,
PLASTW ? escapeJSONStrings(PLASTW->m_szTitle).c_str() : "");
return std::format(R"#({{
"id": {},
"name": "{}",
"monitor": "{}",
"windows": {},
"hasfullscreen": {},
"lastwindow": "0x{:x}",
"lastwindowtitle": "{}"
}})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
} else {
return getFormat("workspace ID %i (%s) on monitor %s:\n\twindows: %i\n\thasfullscreen: %i\n\tlastwindow: 0x%lx\n\tlastwindowtitle: %s\n\n", w->m_iID, w->m_szName.c_str(),
PMONITOR ? PMONITOR->szName.c_str() : "?", g_pCompositor->getWindowsOnWorkspace(w->m_iID), (int)w->m_bHasFullscreenWindow, PLASTW,
PLASTW ? PLASTW->m_szTitle.c_str() : "");
return std::format("workspace ID {} ({}) on monitor {}:\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID, w->m_szName,
PMONITOR ? PMONITOR->szName : "?", g_pCompositor->getWindowsOnWorkspace(w->m_iID), (int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW,
PLASTW ? PLASTW->m_szTitle : "");
}
}
@@ -227,7 +243,7 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += ",";
}
result.pop_back();
trimTrailingComma(result);
result += "]";
} else {
for (auto& w : g_pCompositor->m_vWorkspaces) {
@@ -259,34 +275,33 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) {
result += getFormat(
R"#("%s": {
"levels": {
result += std::format(
R"#("{}": {{
"levels": {{
)#",
escapeJSONStrings(mon->szName).c_str());
escapeJSONStrings(mon->szName));
int layerLevel = 0;
for (auto& level : mon->m_aLayerSurfaceLayers) {
result += getFormat(
result += std::format(
R"#(
"%i": [
"{}": [
)#",
layerLevel);
for (auto& layer : level) {
result += getFormat(
R"#( {
"address": "0x%lx",
"x": %i,
"y": %i,
"w": %i,
"h": %i,
"namespace": "%s"
},)#",
layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, layer->geometry.height, escapeJSONStrings(layer->szNamespace).c_str());
result += std::format(
R"#( {{
"address": "0x{:x}",
"x": {},
"y": {},
"w": {},
"h": {},
"namespace": "{}"
}},)#",
(uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, layer->geometry.height, escapeJSONStrings(layer->szNamespace));
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
if (level.size() > 0)
result += "\n ";
@@ -296,28 +311,26 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
layerLevel++;
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "\n }\n},";
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "\n}\n";
} else {
for (auto& mon : g_pCompositor->m_vMonitors) {
result += getFormat("Monitor %s:\n", mon->szName.c_str());
result += std::format("Monitor {}:\n", mon->szName);
int layerLevel = 0;
static const std::array<std::string, 4> levelNames = {"background", "bottom", "top", "overlay"};
for (auto& level : mon->m_aLayerSurfaceLayers) {
result += getFormat("\tLayer level %i (%s):\n", layerLevel, levelNames[layerLevel].c_str());
result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]);
for (auto& layer : level) {
result += getFormat("\t\tLayer %lx: xywh: %i %i %i %i, namespace: %s\n", layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width,
layer->geometry.height, layer->szNamespace.c_str());
result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}\n", (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width,
layer->geometry.height, layer->szNamespace);
}
layerLevel++;
@@ -337,112 +350,105 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "\"mice\": [\n";
for (auto& m : g_pInputManager->m_lMice) {
result += getFormat(
R"#( {
"address": "0x%lx",
"name": "%s",
"defaultSpeed": %f
},)#",
&m, escapeJSONStrings(m.name).c_str(),
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);
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "\n],\n";
result += "\"keyboards\": [\n";
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
result += getFormat(
R"#( {
"address": "0x%lx",
"name": "%s",
"rules": "%s",
"model": "%s",
"layout": "%s",
"variant": "%s",
"options": "%s",
"active_keymap": "%s",
"main": %s
},)#",
&k, escapeJSONStrings(k.name).c_str(), escapeJSONStrings(k.currentRules.rules).c_str(), escapeJSONStrings(k.currentRules.model).c_str(),
escapeJSONStrings(k.currentRules.layout).c_str(), escapeJSONStrings(k.currentRules.variant).c_str(), escapeJSONStrings(k.currentRules.options).c_str(),
escapeJSONStrings(KM).c_str(), (k.active ? "true" : "false"));
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}",
"rules": "{}",
"model": "{}",
"layout": "{}",
"variant": "{}",
"options": "{}",
"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"));
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "\n],\n";
result += "\"tablets\": [\n";
for (auto& d : g_pInputManager->m_lTabletPads) {
result += getFormat(
R"#( {
"address": "0x%lx",
result += std::format(
R"#( {{
"address": "0x{:x}",
"type": "tabletPad",
"belongsTo": {
"address": "0x%lx",
"name": "%s"
}
},)#",
&d, d.pTabletParent, escapeJSONStrings(d.pTabletParent ? d.pTabletParent->name : "").c_str());
"belongsTo": {{
"address": "0x{:x}",
"name": "{}"
}}
}},)#",
(uintptr_t)&d, (uintptr_t)d.pTabletParent, escapeJSONStrings(d.pTabletParent ? d.pTabletParent->name : ""));
}
for (auto& d : g_pInputManager->m_lTablets) {
result += getFormat(
R"#( {
"address": "0x%lx",
"name": "%s"
},)#",
&d, escapeJSONStrings(d.name).c_str());
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, escapeJSONStrings(d.name));
}
for (auto& d : g_pInputManager->m_lTabletTools) {
result += getFormat(
R"#( {
"address": "0x%lx",
result += std::format(
R"#( {{
"address": "0x{:x}",
"type": "tabletTool",
"belongsTo": "0x%lx"
},)#",
&d, d.wlrTabletTool ? d.wlrTabletTool->data : 0);
"belongsTo": "0x{:x}"
}},)#",
(uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0);
}
// remove trailing comma
result.pop_back();
trimTrailingComma(result);
result += "\n],\n";
result += "\"touch\": [\n";
for (auto& d : g_pInputManager->m_lTouchDevices) {
result += getFormat(
R"#( {
"address": "0x%lx",
"name": "%s"
},)#",
&d, d.name.c_str());
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, d.name);
}
// remove trailing comma
if (result[result.size() - 1] == ',')
result.pop_back();
trimTrailingComma(result);
result += "\n],\n";
result += "\"switches\": [\n";
for (auto& d : g_pInputManager->m_lSwitches) {
result += getFormat(
R"#( {
"address": "0x%lx",
"name": "%s"
},)#",
&d, d.pWlrDevice ? d.pWlrDevice->name : "");
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, d.pWlrDevice ? d.pWlrDevice->name : "");
}
// remove trailing comma
if (result[result.size() - 1] == ',')
result.pop_back();
trimTrailingComma(result);
result += "\n]\n";
result += "}\n";
@@ -451,8 +457,8 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "mice:\n";
for (auto& m : g_pInputManager->m_lMice) {
result += getFormat(
"\tMouse at %lx:\n\t\t%s\n\t\t\tdefault speed: %f\n", &m, m.name.c_str(),
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));
}
@@ -460,35 +466,35 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
result += getFormat("\tKeyboard at %lx:\n\t\t%s\n\t\t\trules: r \"%s\", m \"%s\", l \"%s\", v \"%s\", o \"%s\"\n\t\t\tactive keymap: %s\n\t\t\tmain: %s\n", &k,
k.name.c_str(), k.currentRules.rules.c_str(), k.currentRules.model.c_str(), k.currentRules.layout.c_str(), k.currentRules.variant.c_str(),
k.currentRules.options.c_str(), KM.c_str(), (k.active ? "yes" : "no"));
result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\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"));
}
result += "\n\nTablets:\n";
for (auto& d : g_pInputManager->m_lTabletPads) {
result += getFormat("\tTablet Pad at %lx (belongs to %lx -> %s)\n", &d, d.pTabletParent, d.pTabletParent ? d.pTabletParent->name.c_str() : "");
result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)&d, (uintptr_t)d.pTabletParent, d.pTabletParent ? d.pTabletParent->name : "");
}
for (auto& d : g_pInputManager->m_lTablets) {
result += getFormat("\tTablet at %lx:\n\t\t%s\n", &d, d.name.c_str());
result += std::format("\tTablet at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.name);
}
for (auto& d : g_pInputManager->m_lTabletTools) {
result += getFormat("\tTablet Tool at %lx (belongs to %lx)\n", &d, d.wlrTabletTool ? d.wlrTabletTool->data : 0);
result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0);
}
result += "\n\nTouch:\n";
for (auto& d : g_pInputManager->m_lTouchDevices) {
result += getFormat("\tTouch Device at %lx:\n\t\t%s\n", &d, d.name.c_str());
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.name);
}
result += "\n\nSwitches:\n";
for (auto& d : g_pInputManager->m_lSwitches) {
result += getFormat("\tSwitch Device at %lx:\n\t\t%s\n", &d, d.pWlrDevice ? d.pWlrDevice->name : "");
result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pWlrDevice ? d.pWlrDevice->name : "");
}
}
@@ -501,31 +507,31 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
ret += getFormat("\n\tname: %s\n\t\toverriden: %i\n\t\tbezier: %s\n\t\tenabled: %i\n\t\tspeed: %.2f\n\t\tstyle: %s\n", ac.first.c_str(), (int)ac.second.overridden,
ac.second.internalBezier.c_str(), ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle.c_str());
ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second.overridden,
ac.second.internalBezier, ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle);
}
ret += "beziers:\n";
for (auto& bz : g_pAnimationManager->getAllBeziers()) {
ret += getFormat("\n\tname: %s\n", bz.first.c_str());
ret += std::format("\n\tname: {}\n", bz.first);
}
} else {
// json
ret += "[[";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
ret += getFormat(R"#(
{
"name": "%s",
"overridden": %s,
"bezier": "%s",
"enabled": %s,
"speed": %.2f,
"style": "%s"
},)#",
ac.first.c_str(), ac.second.overridden ? "true" : "false", ac.second.internalBezier.c_str(), ac.second.internalEnabled ? "true" : "false",
ac.second.internalSpeed, ac.second.internalStyle.c_str());
ret += std::format(R"#(
{{
"name": "{}",
"overridden": {},
"bezier": "{}",
"enabled": {},
"speed": {:.2f},
"style": "{}"
}},)#",
ac.first, ac.second.overridden ? "true" : "false", ac.second.internalBezier, ac.second.internalEnabled ? "true" : "false", ac.second.internalSpeed,
ac.second.internalStyle);
}
ret[ret.length() - 1] = ']';
@@ -533,14 +539,14 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
ret += ",\n[";
for (auto& bz : g_pAnimationManager->getAllBeziers()) {
ret += getFormat(R"#(
{
"name": "%s"
},)#",
bz.first.c_str());
ret += std::format(R"#(
{{
"name": "{}"
}},)#",
bz.first);
}
ret.pop_back();
trimTrailingComma(ret);
ret += "]]";
}
@@ -553,18 +559,18 @@ std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
ret += getFormat("%s:%s -> %s\n", sh.appid.c_str(), sh.id.c_str(), sh.description.c_str());
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else {
ret += "[";
for (auto& sh : SHORTCUTS) {
ret += getFormat(R"#(
{
"name": "%s",
"description": "%s"
},)#",
escapeJSONStrings(sh.appid + ":" + sh.id).c_str(), escapeJSONStrings(sh.description).c_str());
ret += std::format(R"#(
{{
"name": "{}",
"description": "{}"
}},)#",
escapeJSONStrings(sh.appid + ":" + sh.id), escapeJSONStrings(sh.description));
}
ret.pop_back();
trimTrailingComma(ret);
ret += "]\n";
}
@@ -584,32 +590,35 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
ret += "r";
if (kb.repeat)
ret += "e";
if (kb.nonConsuming)
ret += "n";
ret += getFormat("\n\tmodmask: %u\n\tsubmap: %s\n\tkey: %s\n\tkeycode: %d\n\tdispatcher: %s\n\targ: %s\n\n", kb.modmask, kb.submap.c_str(), kb.key.c_str(), kb.keycode,
kb.handler.c_str(), kb.arg.c_str());
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap, kb.key, kb.keycode, kb.handler,
kb.arg);
}
} else {
// json
ret += "[";
for (auto& kb : g_pKeybindManager->m_lKeybinds) {
ret += getFormat(
ret += std::format(
R"#(
{
"locked": %s,
"mouse": %s,
"release": %s,
"repeat": %s,
"modmask": %u,
"submap": "%s",
"key": "%s",
"keycode": %i,
"dispatcher": "%s",
"arg": "%s"
},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.modmask,
escapeJSONStrings(kb.submap).c_str(), escapeJSONStrings(kb.key).c_str(), kb.keycode, escapeJSONStrings(kb.handler).c_str(), escapeJSONStrings(kb.arg).c_str());
{{
"locked": {},
"mouse": {},
"release": {},
"repeat": {},
"non_consuming": {},
"modmask": {},
"submap": "{}",
"key": "{}",
"keycode": {},
"dispatcher": "{}",
"arg": "{}"
}},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.nonConsuming ? "true" : "false",
kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, escapeJSONStrings(kb.handler), escapeJSONStrings(kb.arg));
}
ret.pop_back();
trimTrailingComma(ret);
ret += "]";
}
@@ -618,9 +627,12 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" +
removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE).c_str() + ").\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
@@ -637,14 +649,15 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
} else {
std::string result = getFormat(
R"#({
"branch": "%s",
"commit": "%s",
"dirty": %s,
"commit_message": "%s",
std::string result = std::format(
R"#({{
"branch": "{}",
"commit": "{}",
"dirty": {},
"commit_message": "{}",
"tag": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE).c_str());
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_TAG);
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
@@ -659,8 +672,7 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "\"no xwayland\",";
#endif
if (result[result.length() - 1] == ',')
result.pop_back();
trimTrailingComma(result);
result += "]\n}";
@@ -676,7 +688,9 @@ std::string dispatchRequest(std::string in) {
const auto DISPATCHSTR = in.substr(0, in.find_first_of(' '));
const auto DISPATCHARG = in.substr(in.find_first_of(' ') + 1);
auto DISPATCHARG = std::string();
if ((int)in.find_first_of(' ') != -1)
DISPATCHARG = in.substr(in.find_first_of(' ') + 1);
const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(DISPATCHSTR);
if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end())
@@ -684,7 +698,7 @@ std::string dispatchRequest(std::string in) {
DISPATCHER->second(DISPATCHARG);
Debug::log(LOG, "Hyprctl: dispatcher %s : %s", DISPATCHSTR.c_str(), DISPATCHARG.c_str());
Debug::log(LOG, "Hyprctl: dispatcher {} : {}", DISPATCHSTR, DISPATCHARG);
return "ok";
}
@@ -722,14 +736,14 @@ std::string dispatchKeyword(std::string in) {
}
// decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border")) {
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("cursor_zoom_factor")) {
for (auto& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
}
}
Debug::log(LOG, "Hyprctl: keyword %s : %s", COMMAND.c_str(), VALUE.c_str());
Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
if (retval == "")
return "ok";
@@ -766,15 +780,15 @@ std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();
if (format == HyprCtl::FORMAT_NORMAL) {
return getFormat("%i, %i", (int)CURSORPOS.x, (int)CURSORPOS.y);
return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y);
} else {
return getFormat(R"#(
{
"x": %i,
"y": %i
}
return std::format(R"#(
{{
"x": {},
"y": {}
}}
)#",
(int)CURSORPOS.x, (int)CURSORPOS.y);
(int)CURSORPOS.x, (int)CURSORPOS.y);
}
return "error";
@@ -961,6 +975,8 @@ std::string dispatchSetProp(std::string request) {
PWINDOW->m_sAdditionalConfigData.forceNoBorder.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forcenoshadow") {
PWINDOW->m_sAdditionalConfigData.forceNoShadow.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forcenodim") {
PWINDOW->m_sAdditionalConfigData.forceNoDim.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "windowdancecompat") {
PWINDOW->m_sAdditionalConfigData.windowDanceCompat.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "nomaxsize") {
@@ -981,6 +997,10 @@ std::string dispatchSetProp(std::string request) {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") {
PWINDOW->m_sSpecialRenderData.borderSize.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "keepaspectratio") {
PWINDOW->m_sAdditionalConfigData.keepAspectRatio.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else {
return "prop not found";
}
@@ -988,6 +1008,9 @@ std::string dispatchSetProp(std::string request) {
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
for (auto& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
return "ok";
}
@@ -1017,20 +1040,21 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
return "no such option";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL)
return getFormat("option %s\n\tint: %lld\n\tfloat: %f\n\tstr: \"%s\"\n\tdata: %lx", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(),
PCFGOPT->data.get());
return std::format("option {}\n\tint: {}\n\tfloat: {:.5f}\n\tstr: \"{}\"\n\tdata: {:x}\n\tset: {}", curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue,
(uintptr_t)PCFGOPT->data.get(), PCFGOPT->set);
else {
return getFormat(
return std::format(
R"#(
{
"option": "%s",
"int": %lld,
"float": %f,
"str": "%s",
"data": "0x%lx"
}
{{
"option": "{}",
"int": {},
"float": {:.5f},
"str": "{}",
"data": "0x{:x}",
"set": {}
}}
)#",
curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(), PCFGOPT->data.get());
curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue, (uintptr_t)PCFGOPT->data.get(), PCFGOPT->set);
}
}
@@ -1146,8 +1170,7 @@ std::string dispatchPlugin(std::string request) {
std::string list = "";
for (auto& p : PLUGINS) {
list += getFormat("\nPlugin %s by %s:\n\tHandle: %lx\n\tVersion: %s\n\tDescription: %s\n", p->name.c_str(), p->author.c_str(), p->m_pHandle, p->version.c_str(),
p->description.c_str());
list += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description);
}
return list;
@@ -1287,11 +1310,22 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize);
const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
char readBuffer[1024];
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer, 1024);
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(ACCEPTEDCONNECTION, &fdset);
timeval timeout = {.tv_sec = 0, .tv_usec = 5000};
auto success = select(ACCEPTEDCONNECTION + 1, &fdset, nullptr, nullptr, &timeout);
if (success <= 0) {
close(ACCEPTEDCONNECTION);
return 0;
}
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer, 1024);
readBuffer[messageSize == 1024 ? 1023 : messageSize] = '\0';
std::string request(readBuffer);
@@ -1301,7 +1335,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
try {
reply = getReply(request);
} catch (std::exception& e) {
Debug::log(ERR, "Error in request: %s", e.what());
Debug::log(ERR, "Error in request: {}", e.what());
reply = "Err: " + std::string(e.what());
}
@@ -1318,7 +1352,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
void HyprCtl::startHyprCtlSocket() {
iSocketFD = socket(AF_UNIX, SOCK_STREAM, 0);
iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (iSocketFD < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
@@ -1339,7 +1373,7 @@ void HyprCtl::startHyprCtlSocket() {
// 10 max queued.
listen(iSocketFD, 10);
Debug::log(LOG, "Hypr socket started at %s", socketPath.c_str());
Debug::log(LOG, "Hypr socket started at {}", socketPath);
wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View File

@@ -132,7 +132,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
yOffset += 17;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::string(getFormat("%i FPS", (int)FPS));
text = std::format("{} FPS", (int)FPS);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
@@ -143,7 +143,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::string(getFormat("Avg Frametime: %.2fms (var %.2fms)", avgFrametime, varFrametime));
text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
@@ -151,7 +151,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::string(getFormat("Avg Rendertime: %.2fms (var %.2fms)", avgRenderTime, varRenderTime));
text = std::format("Avg Rendertime: {:.2f}ms (var {:.2f}ms)", avgRenderTime, varRenderTime);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
@@ -159,7 +159,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::string(getFormat("Avg Rendertime (No Overlay): %.2fms (var %.2fms)", avgRenderTimeNoOverlay, varRenderTimeNoOverlay));
text = std::format("Avg Rendertime (No Overlay): {:.2f}ms (var {:.2f}ms)", avgRenderTimeNoOverlay, varRenderTimeNoOverlay);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
@@ -167,7 +167,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::string(getFormat("Avg Anim Tick: %.2fms (var %.2fms) (%.2f TPS)", avgAnimMgrTick, varAnimMgrTick, 1.0 / (avgAnimMgrTick / 1000.0)));
text = std::format("Avg Anim Tick: {:.2f}ms (var {:.2f}ms) ({:.2f} TPS)", avgAnimMgrTick, varAnimMgrTick, 1.0 / (avgAnimMgrTick / 1000.0));
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)

View File

@@ -27,57 +27,3 @@ void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
if (!disableStdout)
std::cout << output << "\n";
}
void Debug::log(LogLevel level, const char* fmt, ...) {
if (disableLogs && *disableLogs)
return;
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
switch (level) {
case LOG: ofs << "[LOG] "; break;
case WARN: ofs << "[WARN] "; break;
case ERR: ofs << "[ERR] "; break;
case CRIT: ofs << "[CRITICAL] "; break;
case INFO: ofs << "[INFO] "; break;
default: break;
}
// print date and time to the ofs
if (disableTime && !*disableTime) {
auto timet = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
const auto MILLIS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() % 1000;
ofs << std::put_time(std::localtime(&timet), "[%H:%M:%S:");
if (MILLIS > 99)
ofs << MILLIS;
else if (MILLIS > 9)
ofs << "0" << MILLIS;
else
ofs << "00" << MILLIS;
ofs << "] ";
}
char* outputStr = nullptr;
va_list args;
va_start(args, fmt);
vasprintf(&outputStr, fmt, args);
va_end(args);
std::string output = std::string(outputStr);
free(outputStr);
ofs << output << "\n";
ofs.close();
// log it to the stdout too.
if (!disableStdout)
std::cout << output << "\n";
}

View File

@@ -1,26 +1,82 @@
#pragma once
#include <string>
#include <wlr/util/log.h>
#include <format>
#include <iostream>
#include <fstream>
#include <chrono>
#include "../helpers/MiscFunctions.hpp"
#define LOGMESSAGESIZE 1024
enum LogLevel
{
enum LogLevel {
NONE = -1,
LOG = 0,
WARN,
ERR,
CRIT,
INFO
INFO,
TRACE
};
namespace Debug {
void init(const std::string& IS);
void log(LogLevel level, const char* fmt, ...);
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
inline std::string logFile;
inline int64_t* disableLogs = nullptr;
inline int64_t* disableTime = nullptr;
inline bool disableStdout = false;
inline bool trace = false;
void init(const std::string& IS);
template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
if (disableLogs && *disableLogs)
return;
if (level == TRACE && !trace)
return;
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;
}
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
// print date and time to the ofs
if (disableTime && !*disableTime) {
#ifndef _LIBCPP_VERSION
logMsg += std::format("[{:%T}] ", std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())});
#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
}
// no need for try {} catch {} because std::format_string<Args...> ensures that vformat never throw std::format_error
// because
// 1. any faulty format specifier that sucks will cause a compilation error.
// 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.)
// 3. this is actually what std::format in stdlib does
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
ofs << logMsg << "\n";
ofs.close();
// log it to the stdout too.
if (!disableStdout)
std::cout << logMsg << "\n";
}
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
};

View File

@@ -0,0 +1,35 @@
#pragma once
#ifdef USE_TRACY_GPU
#include "Log.hpp"
#include <GL/gl.h>
#include <GLES2/gl2ext.h>
inline PFNGLQUERYCOUNTEREXTPROC glQueryCounter;
inline PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv;
inline PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp"
inline void loadGLProc(void* pProc, const char* name) {
void* proc = (void*)eglGetProcAddress(name);
if (proc == NULL) {
Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
abort();
}
*(void**)pProc = proc;
}
#define TRACY_GPU_CONTEXT TracyGpuContext
#define TRACY_GPU_ZONE(e) TracyGpuZone(e)
#define TRACY_GPU_COLLECT TracyGpuCollect
#else
#define TRACY_GPU_CONTEXT
#define TRACY_GPU_ZONE(e)
#define TRACY_GPU_COLLECT
#endif

View File

@@ -1,95 +1,5 @@
#include "includes.hpp"
#include "debug/Log.hpp"
#include "helpers/MiscFunctions.hpp"
#include "helpers/WLListener.hpp"
#include "helpers/Color.hpp"
#include "wlrunstable/wlr_ext_workspace_v1.hpp"
#include <utility>
#ifndef NDEBUG
#ifdef HYPRLAND_DEBUG
#define ISDEBUG true
#else
#define ISDEBUG false
#endif
#else
#define ISDEBUG false
#endif
#define LISTENER(name) \
void listener_##name(wl_listener*, void*); \
inline wl_listener listen_##name = {.notify = listener_##name}
#define DYNLISTENFUNC(name) void listener_##name(void*, void*)
#define DYNLISTENER(name) CHyprWLListener hyprListener_##name
#define DYNMULTILISTENER(name) wl_listener listen_##name
#define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x <= (x2) && (vec).y >= (y1) && (vec).y <= (y2))
#define DELTALESSTHAN(a, b, delta) (abs((a) - (b)) < (delta))
#define PIXMAN_DAMAGE_FOREACH(region) \
int rectsNum = 0; \
const auto RECTSARR = pixman_region32_rectangles(region, &rectsNum); \
for (int i = 0; i < rectsNum; ++i)
#define PIXMAN_REGION_FOREACH(region) PIXMAN_DAMAGE_FOREACH(region)
#define interface class
#define STICKS(a, b) abs((a) - (b)) < 2
#define ALPHA(c) ((double)(((c) >> 24) & 0xff) / 255.0)
#define RED(c) ((double)(((c) >> 16) & 0xff) / 255.0)
#define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0)
#define BLUE(c) ((double)(((c)) & 0xff) / 255.0)
#define HYPRATOM(name) \
{ name, 0 }
#ifndef __INTELLISENSE__
#define RASSERT(expr, reason, ...) \
if (!(expr)) { \
Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n%s\n\nat: line %d in %s", \
getFormat(reason, ##__VA_ARGS__).c_str(), __LINE__, \
([]() constexpr->std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \
printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \
*((int*)nullptr) = 1; /* so that we crash and get a coredump */ \
}
#else
#define RASSERT(expr, reason, ...)
#endif
#define ASSERT(expr) RASSERT(expr, "?")
#if ISDEBUG
#define UNREACHABLE() \
{ \
Debug::log(CRIT, "\n\nMEMORY CORRUPTED: Unreachable failed! (Reached an unreachable position, memory corruption!!!)"); \
*((int*)nullptr) = 1; \
}
#else
#define UNREACHABLE() std::unreachable();
#endif
// git stuff
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "?"
#endif
#ifndef GIT_BRANCH
#define GIT_BRANCH "?"
#endif
#ifndef GIT_COMMIT_MESSAGE
#define GIT_COMMIT_MESSAGE "?"
#endif
#ifndef GIT_DIRTY
#define GIT_DIRTY "?"
#endif
#ifndef GIT_TAG
#define GIT_TAG "?"
#endif
#define SPECIAL_WORKSPACE_START (-99)
#define PI 3.14159265358979
#include "macros.hpp"

View File

@@ -19,7 +19,7 @@ void Events::listener_keyboardDestroy(void* owner, void* data) {
SKeyboard* PKEYBOARD = (SKeyboard*)owner;
g_pInputManager->destroyKeyboard(PKEYBOARD);
Debug::log(LOG, "Destroyed keyboard %lx", PKEYBOARD);
Debug::log(LOG, "Destroyed keyboard {:x}", (uintptr_t)PKEYBOARD);
}
void Events::listener_keyboardKey(void* owner, void* data) {
@@ -63,30 +63,30 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
switch (DEVICE->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
Debug::log(LOG, "Attached a keyboard with name %s", DEVICE->name);
Debug::log(LOG, "Attached a keyboard with name {}", DEVICE->name);
g_pInputManager->newKeyboard(DEVICE);
break;
case WLR_INPUT_DEVICE_POINTER:
Debug::log(LOG, "Attached a mouse with name %s", DEVICE->name);
Debug::log(LOG, "Attached a mouse with name {}", DEVICE->name);
g_pInputManager->newMouse(DEVICE);
break;
case WLR_INPUT_DEVICE_TOUCH:
Debug::log(LOG, "Attached a touch device with name %s", DEVICE->name);
Debug::log(LOG, "Attached a touch device with name {}", DEVICE->name);
g_pInputManager->newTouchDevice(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
Debug::log(LOG, "Attached a tablet tool with name %s", DEVICE->name);
Debug::log(LOG, "Attached a tablet tool with name {}", DEVICE->name);
g_pInputManager->newTabletTool(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
Debug::log(LOG, "Attached a tablet pad with name %s", DEVICE->name);
Debug::log(LOG, "Attached a tablet pad with name {}", DEVICE->name);
g_pInputManager->newTabletPad(DEVICE);
break;
case WLR_INPUT_DEVICE_SWITCH:
Debug::log(LOG, "Attached a switch device with name %s", DEVICE->name);
Debug::log(LOG, "Attached a switch device with name {}", DEVICE->name);
g_pInputManager->newSwitch(DEVICE);
break;
default: Debug::log(WARN, "Unrecognized input device plugged in: %s", DEVICE->name); break;
default: Debug::log(WARN, "Unrecognized input device plugged in: {}", DEVICE->name); break;
}
g_pInputManager->updateCapabilities();
@@ -95,7 +95,7 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
void Events::listener_newConstraint(wl_listener* listener, void* data) {
const auto PCONSTRAINT = (wlr_pointer_constraint_v1*)data;
Debug::log(LOG, "New mouse constraint at %lx", PCONSTRAINT);
Debug::log(LOG, "New mouse constraint at {:x}", (uintptr_t)PCONSTRAINT);
g_pInputManager->m_lConstraints.emplace_back();
const auto CONSTRAINT = &g_pInputManager->m_lConstraints.back();
@@ -122,24 +122,13 @@ void Events::listener_destroyConstraint(void* owner, void* data) {
const auto PWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
if (PWINDOW && PCONSTRAINT->positionHint != Vector2D{-1, -1}) {
if (PWINDOW->m_bIsX11) {
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, PCONSTRAINT->positionHint.x + PWINDOW->m_uSurface.xwayland->x,
PWINDOW->m_uSurface.xwayland->y + PCONSTRAINT->positionHint.y);
wlr_seat_pointer_warp(PCONSTRAINT->constraint->seat, PCONSTRAINT->positionHint.x, PCONSTRAINT->positionHint.y);
} else {
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, PCONSTRAINT->positionHint.x + PWINDOW->m_vRealPosition.vec().x,
PCONSTRAINT->positionHint.y + PWINDOW->m_vRealPosition.vec().y);
wlr_seat_pointer_warp(PCONSTRAINT->constraint->seat, PCONSTRAINT->positionHint.x, PCONSTRAINT->positionHint.y);
}
}
if (PWINDOW && PCONSTRAINT->active && PCONSTRAINT->constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
g_pInputManager->warpMouseToConstraintMiddle(PCONSTRAINT);
PCONSTRAINT->pMouse->currentConstraint = nullptr;
}
Debug::log(LOG, "Unconstrained mouse from %lx", PCONSTRAINT->constraint);
Debug::log(LOG, "Unconstrained mouse from {:x}", (uintptr_t)PCONSTRAINT->constraint);
g_pInputManager->m_lConstraints.remove(*PCONSTRAINT);
}

View File

@@ -39,6 +39,7 @@ namespace Events {
DYNLISTENFUNC(destroyPopupXDG);
DYNLISTENFUNC(commitPopupXDG);
DYNLISTENFUNC(newPopupFromPopupXDG);
DYNLISTENFUNC(repositionPopupXDG);
// Surface XDG (window)
LISTENER(newXDGSurface);
@@ -59,6 +60,8 @@ namespace Events {
DYNLISTENFUNC(requestMinimize);
DYNLISTENFUNC(requestMaximize);
DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(associateX11);
DYNLISTENFUNC(dissociateX11);
// Window subsurfaces
// LISTENER(newSubsurfaceWindow);
@@ -101,6 +104,7 @@ namespace Events {
DYNLISTENFUNC(monitorDamage);
DYNLISTENFUNC(monitorNeedsFrame);
DYNLISTENFUNC(monitorCommit);
DYNLISTENFUNC(monitorBind);
// XWayland
LISTENER(readyXWayland);
@@ -164,4 +168,10 @@ namespace Events {
// Session Lock
LISTENER(newSessionLock);
// Gamma control
LISTENER(setGamma);
// Cursor shape
LISTENER(setCursorShape);
};

View File

@@ -26,7 +26,7 @@ void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
return;
}
Debug::log(LOG, "New LayerSurface has no preferred monitor. Assigning Monitor %s", PMONITOR->szName.c_str());
Debug::log(LOG, "New LayerSurface has no preferred monitor. Assigning Monitor {}", PMONITOR->szName);
WLRLAYERSURFACE->output = PMONITOR->output;
}
@@ -44,8 +44,8 @@ void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
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->events.map, &Events::listener_mapLayerSurface, layerSurface, "layerSurface");
layerSurface->hyprListener_unmapLayerSurface.initCallback(&WLRLAYERSURFACE->events.unmap, &Events::listener_unmapLayerSurface, 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->hyprListener_newPopup.initCallback(&WLRLAYERSURFACE->events.new_popup, &Events::listener_newPopup, layerSurface, "layerSurface");
layerSurface->layerSurface = WLRLAYERSURFACE;
@@ -55,14 +55,14 @@ void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
layerSurface->forceBlur = g_pConfigManager->shouldBlurLS(layerSurface->szNamespace);
Debug::log(LOG, "LayerSurface %lx (namespace %s layer %d) created on monitor %s", layerSurface->layerSurface, layerSurface->layerSurface->_namespace, layerSurface->layer,
PMONITOR->szName.c_str());
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 %lx destroyed", layersurface->layerSurface);
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layersurface->layerSurface);
const auto PMONITOR = g_pCompositor->getMonitorFromID(layersurface->monitorID);
@@ -107,12 +107,11 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) {
void Events::listener_mapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface %lx mapped", layersurface->layerSurface);
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layersurface->layerSurface);
layersurface->layerSurface->mapped = true;
layersurface->mapped = true;
layersurface->surface = layersurface->layerSurface->surface;
layersurface->mapped = true;
layersurface->keyboardExclusive = layersurface->layerSurface->current.keyboard_interactive;
layersurface->surface = layersurface->layerSurface->surface;
// anim
layersurface->alpha.setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
@@ -143,8 +142,11 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output);
if (layersurface->layerSurface->current.keyboard_interactive &&
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint)) { // don't focus if constrained
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint);
if (GRABSFOCUS) {
g_pCompositor->focusSurface(layersurface->layerSurface->surface);
const auto LOCAL =
@@ -158,9 +160,11 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
layersurface->alpha.setValue(0);
layersurface->alpha = 1.f;
layersurface->alpha = ((layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS) ? 0.f : 1.f);
layersurface->readyToDelete = false;
layersurface->fadingOut = false;
@@ -173,7 +177,7 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
void Events::listener_unmapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface %lx unmapped", layersurface->layerSurface);
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);
@@ -204,9 +208,6 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
g_pCompositor->addToFadingOutSafe(layersurface);
if (layersurface->layerSurface->mapped)
layersurface->layerSurface->mapped = false;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layersurface->layerSurface->output);
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == layersurface->layerSurface->surface;
@@ -323,6 +324,22 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
}
}
if (layersurface->layerSurface->current.keyboard_interactive &&
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) // 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);
} else if (!layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) &&
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_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);

View File

@@ -49,7 +49,7 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
const auto XCBCONNECTION = xcb_connect(g_pXWaylandManager->m_sWLRXWayland->display_name, NULL);
const auto ERR = xcb_connection_has_error(XCBCONNECTION);
if (ERR) {
Debug::log(LogLevel::ERR, "XWayland -> xcb_connection_has_error failed with %i", ERR);
Debug::log(LogLevel::ERR, "XWayland -> xcb_connection_has_error failed with {}", ERR);
return;
}
@@ -58,7 +58,7 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(XCBCONNECTION, cookie, NULL);
if (!reply) {
Debug::log(LogLevel::ERR, "XWayland -> Atom failed: %s", ATOM.first.c_str());
Debug::log(LogLevel::ERR, "XWayland -> Atom failed: {}", ATOM.first);
continue;
}
@@ -98,20 +98,20 @@ void Events::listener_startDrag(wl_listener* listener, void* data) {
wlr_drag* wlrDrag = (wlr_drag*)data;
Debug::log(LOG, "Started drag %lx", wlrDrag);
Debug::log(LOG, "Started drag {:x}", (uintptr_t)wlrDrag);
wlrDrag->data = data;
g_pInputManager->m_sDrag.hyprListener_destroy.initCallback(&wlrDrag->events.destroy, &Events::listener_destroyDrag, &g_pInputManager->m_sDrag, "Drag");
if (wlrDrag->icon) {
Debug::log(LOG, "Drag started with an icon %lx", wlrDrag->icon);
Debug::log(LOG, "Drag started with an icon {:x}", (uintptr_t)wlrDrag->icon);
g_pInputManager->m_sDrag.dragIcon = wlrDrag->icon;
wlrDrag->icon->data = g_pInputManager->m_sDrag.dragIcon;
g_pInputManager->m_sDrag.hyprListener_mapIcon.initCallback(&wlrDrag->icon->events.map, &Events::listener_mapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_unmapIcon.initCallback(&wlrDrag->icon->events.unmap, &Events::listener_unmapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_mapIcon.initCallback(&wlrDrag->icon->surface->events.map, &Events::listener_mapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_unmapIcon.initCallback(&wlrDrag->icon->surface->events.unmap, &Events::listener_unmapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_destroyIcon.initCallback(&wlrDrag->icon->events.destroy, &Events::listener_destroyDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_commitIcon.initCallback(&wlrDrag->icon->surface->events.commit, &Events::listener_commitDragIcon, &g_pInputManager->m_sDrag,
"DragIcon");
@@ -157,7 +157,7 @@ void Events::listener_commitDragIcon(void* owner, void* data) {
}
void Events::listener_InhibitActivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Activated exclusive for %lx.", g_pCompositor->m_sSeat.exclusiveClient);
Debug::log(LOG, "Activated exclusive for {:x}.", (uintptr_t)g_pCompositor->m_sSeat.exclusiveClient);
g_pInputManager->refocus();
g_pCompositor->m_sSeat.exclusiveClient = g_pCompositor->m_sWLRInhibitMgr->active_client;
@@ -214,3 +214,26 @@ void Events::listener_newSessionLock(wl_listener* listener, void* data) {
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);
}

View File

@@ -28,8 +28,7 @@ void Events::listener_change(wl_listener* listener, void* data) {
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
// TODO: clients off of disabled
wlr_box BOX;
wlr_box BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, &BOX);
//m->vecSize.x = BOX.width;
@@ -39,8 +38,18 @@ void Events::listener_change(wl_listener* listener, void* data) {
CONFIGHEAD->state.enabled = m->output->enabled;
CONFIGHEAD->state.mode = m->output->current_mode;
CONFIGHEAD->state.x = m->vecPosition.x;
CONFIGHEAD->state.y = m->vecPosition.y;
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;
}
wlr_output_manager_v1_set_configuration(g_pCompositor->m_sWLROutputMgr, CONFIG);
@@ -58,27 +67,15 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
return;
}
if (g_pCompositor->m_bUnsafeState) {
if (g_pCompositor->m_bUnsafeState)
Debug::log(WARN, "Recovering from an unsafe state. May you be lucky.");
}
// add it to real
std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr;
for (auto& rm : g_pCompositor->m_vRealMonitors) {
if (rm->szName == OUTPUT->name) {
PNEWMONITORWRAP = &rm;
Debug::log(LOG, "Recovering a removed monitor.");
break;
}
}
PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>());
if (!PNEWMONITORWRAP) {
Debug::log(LOG, "Adding completely new monitor.");
PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>());
(*PNEWMONITORWRAP)->ID = g_pCompositor->getNextAvailableMonitorID();
}
(*PNEWMONITORWRAP)->ID = g_pCompositor->getNextAvailableMonitorID(OUTPUT->name);
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
@@ -92,6 +89,20 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
// ready to process cuz we have a monitor
if (PNEWMONITOR->m_bEnabled) {
if (g_pCompositor->m_bUnsafeState) {
// recover workspaces
for (auto& ws : g_pCompositor->m_vWorkspaces) {
g_pCompositor->moveWorkspaceToMonitor(ws.get(), PNEWMONITOR);
}
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
const auto POS = PNEWMONITOR->middle();
if (g_pCompositor->m_sSeat.mouse)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y);
}
g_pCompositor->m_bReadyToProcess = true;
g_pCompositor->m_bUnsafeState = false;
}
@@ -101,7 +112,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
if (firstLaunch) {
firstLaunch = false;
const auto POS = PNEWMONITOR->vecPosition + PNEWMONITOR->vecSize / 2.f;
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 {
@@ -178,19 +189,16 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
if (!pMonitor)
return;
Debug::log(LOG, "Destroy called for monitor %s", pMonitor->output->name);
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
pMonitor->onDisconnect();
pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false;
// cleanup if not unsafe
if (!g_pCompositor->m_bUnsafeState) {
Debug::log(LOG, "Removing monitor %s from realMonitors", pMonitor->szName.c_str());
Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; });
}
std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; });
}
void Events::listener_monitorStateRequest(void* owner, void* data) {
@@ -218,5 +226,12 @@ void Events::listener_monitorCommit(void* owner, void* data) {
const auto E = (wlr_output_event_commit*)data;
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
if (E->committed & WLR_OUTPUT_STATE_BUFFER) {
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E);
}
}
void Events::listener_monitorBind(void* owner, void* data) {
;
}

View File

@@ -56,10 +56,11 @@ void createNewPopup(wlr_xdg_popup* popup, SXDGPopup* pHyprPopup) {
pHyprPopup->popup = popup;
pHyprPopup->hyprListener_destroyPopupXDG.initCallback(&popup->base->events.destroy, &Events::listener_destroyPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_mapPopupXDG.initCallback(&popup->base->events.map, &Events::listener_mapPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_unmapPopupXDG.initCallback(&popup->base->events.unmap, &Events::listener_unmapPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_mapPopupXDG.initCallback(&popup->base->surface->events.map, &Events::listener_mapPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_unmapPopupXDG.initCallback(&popup->base->surface->events.unmap, &Events::listener_unmapPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_newPopupFromPopupXDG.initCallback(&popup->base->events.new_popup, &Events::listener_newPopupFromPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_commitPopupXDG.initCallback(&popup->base->surface->events.commit, &Events::listener_commitPopupXDG, pHyprPopup, "HyprPopup");
pHyprPopup->hyprListener_repositionPopupXDG.initCallback(&popup->events.reposition, &Events::listener_repositionPopupXDG, pHyprPopup, "HyprPopup");
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
@@ -69,8 +70,7 @@ void createNewPopup(wlr_xdg_popup* popup, SXDGPopup* pHyprPopup) {
pHyprPopup->monitor = PMONITOR;
Debug::log(LOG, "Popup: Unconstrained from lx ly: %f %f, pHyprPopup lx ly: %f %f", (float)PMONITOR->vecPosition.x, (float)PMONITOR->vecPosition.y, (float)pHyprPopup->lx,
(float)pHyprPopup->ly);
Debug::log(LOG, "Popup: Unconstrained from lx ly: {:j5}, pHyprPopup lx ly: {:.5f} {:.5f}", PMONITOR->vecPosition, (float)pHyprPopup->lx, (float)pHyprPopup->ly);
}
void Events::listener_newPopup(void* owner, void* data) {
@@ -78,7 +78,7 @@ void Events::listener_newPopup(void* owner, void* data) {
ASSERT(layersurface);
Debug::log(LOG, "New layer popup created from surface %lx", layersurface);
Debug::log(LOG, "New layer popup created from surface {:x}", (uintptr_t)layersurface);
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -102,7 +102,7 @@ void Events::listener_newPopupXDG(void* owner, void* data) {
if (!PWINDOW->m_bIsMapped)
return;
Debug::log(LOG, "New layer popup created from XDG window %lx -> %s", PWINDOW, PWINDOW->m_szTitle.c_str());
Debug::log(LOG, "New layer popup created from XDG window {}", PWINDOW);
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -124,9 +124,9 @@ void Events::listener_newPopupFromPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
if (PPOPUP->parentWindow)
Debug::log(LOG, "New popup created from XDG Window popup %lx -> %s", PPOPUP, PPOPUP->parentWindow->m_szTitle.c_str());
Debug::log(LOG, "New popup created from XDG Window popup {:x} -> {}", (uintptr_t)PPOPUP, PPOPUP->parentWindow);
else
Debug::log(LOG, "New popup created from Non-Window popup %lx", PPOPUP);
Debug::log(LOG, "New popup created from Non-Window popup {:x}", (uintptr_t)PPOPUP);
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -147,7 +147,7 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
Debug::log(LOG, "New XDG Popup mapped at %d %d", (int)PPOPUP->lx, (int)PPOPUP->ly);
Debug::log(LOG, "New XDG Popup mapped at {} {}", (int)PPOPUP->lx, (int)PPOPUP->ly);
if (PPOPUP->parentWindow)
PPOPUP->parentWindow->m_lPopupSurfaces.emplace_back(PPOPUP->popup->base->surface);
@@ -167,7 +167,22 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
if (PPOPUP->monitor)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale);
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode %lx", PPOPUP->pSurfaceTree);
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode {:x}", (uintptr_t)PPOPUP->pSurfaceTree);
}
void Events::listener_repositionPopupXDG(void* owner, void* data) {
SXDGPopup* PPOPUP = (SXDGPopup*)owner;
Debug::log(LOG, "XDG Popup {:x} asks for a reposition", (uintptr_t)PPOPUP);
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
PPOPUP->lastPos = {lx - extents.x, ly - extents.y};
PPOPUP->repositionRequested = true;
}
void Events::listener_unmapPopupXDG(void* owner, void* data) {
@@ -202,9 +217,22 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) {
void Events::listener_commitPopupXDG(void* owner, void* data) {
SXDGPopup* PPOPUP = (SXDGPopup*)owner;
int lx = 0, ly = 0;
if (g_pCompositor->windowValidMapped(PPOPUP->parentWindow)) {
PPOPUP->lx = PPOPUP->parentWindow->m_vRealPosition.vec().x;
PPOPUP->ly = PPOPUP->parentWindow->m_vRealPosition.vec().y;
}
int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents);
if (PPOPUP->repositionRequested)
g_pHyprRenderer->damageBox(PPOPUP->lastPos.x, PPOPUP->lastPos.y, extents.width + 2, extents.height + 2);
PPOPUP->repositionRequested = false;
g_pHyprRenderer->damageSurface(PPOPUP->popup->base->surface, lx, ly);
}
@@ -213,7 +241,7 @@ void Events::listener_destroyPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
Debug::log(LOG, "Destroyed popup XDG %lx", PPOPUP);
Debug::log(LOG, "Destroyed popup XDG {:x}", (uintptr_t)PPOPUP);
if (PPOPUP->pSurfaceTree) {
SubsurfaceTree::destroySurfaceTree(PPOPUP->pSurfaceTree);

View File

@@ -58,6 +58,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
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;
if (g_pInputManager->m_bLastFocusOnLS) // waybar fix
g_pInputManager->releaseAllMouseButtons();
@@ -108,8 +109,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
(PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen);
bool requestsFakeFullscreen = false;
bool requestsMaximize = false;
bool shouldFocus = true;
bool workspaceSpecial = false;
bool overridingNoFullscreen = false;
bool overridingNoMaximize = false;
PWINDOW->m_szInitialTitle = g_pXWaylandManager->getTitle(PWINDOW);
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
@@ -133,20 +134,22 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PMONITOR)
PWINDOW->m_iMonitorID = PMONITOR->ID;
else {
Debug::log(ERR, "No monitor in monitor %s rule", MONITORSTR.c_str());
Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
continue;
}
}
}
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeWorkspace;
const auto PMONITORFROMID = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PWINDOW->m_iMonitorID != PMONITOR->ID) {
g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->m_iMonitorID));
PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
PMONITOR = PMONITORFROMID;
}
PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
Debug::log(ERR, "Rule monitor, applying to window %lx -> mon: %i, workspace: %i", PWINDOW, PWINDOW->m_iMonitorID, PWINDOW->m_iWorkspaceID);
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: %s -> %s | err: %s", r.szRule.c_str(), r.szValue.c_str(), e.what()); }
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r.szRule, r.szValue, e.what()); }
} else if (r.szRule.find("workspace") == 0) {
// check if it isnt unset
const auto WORKSPACERQ = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
@@ -162,7 +165,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName)
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by window %lx, %s applied.", PWINDOW, r.szValue.c_str());
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r.szValue);
} else if (r.szRule.find("float") == 0) {
PWINDOW->m_bIsFloating = true;
} else if (r.szRule.find("tile") == 0) {
@@ -175,8 +178,11 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.find("nofullscreenrequest") == 0) {
PWINDOW->m_bNoFullscreenRequest = true;
} else if (r.szRule.find("nomaximizerequest") == 0) {
PWINDOW->m_bNoMaximizeRequest = true;
} else if (r.szRule == "fullscreen") {
requestsFullscreen = true;
requestsFullscreen = true;
overridingNoFullscreen = true;
} else if (r.szRule == "fakefullscreen") {
requestsFakeFullscreen = true;
} else if (r.szRule == "windowdance") {
@@ -188,7 +194,57 @@ void Events::listener_mapWindow(void* owner, void* data) {
} else if (r.szRule == "pin") {
PWINDOW->m_bPinned = true;
} else if (r.szRule == "maximize") {
requestsMaximize = true;
requestsMaximize = true;
overridingNoMaximize = true;
} else if (r.szRule == "stayfocused") {
PWINDOW->m_bStayFocused = true;
} else if (r.szRule.find("group") == 0) {
if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE)
continue;
// `group` is a shorthand of `group set`
if (removeBeginEndSpacesTabs(r.szRule) == "group") {
PWINDOW->m_eGroupRules |= GROUP_SET;
continue;
}
CVarList vars(r.szRule, 0, 's');
std::string vPrev = "";
for (auto const& v : vars) {
if (v == "group")
continue;
if (v == "set") {
PWINDOW->m_eGroupRules |= GROUP_SET;
} else if (v == "new") {
// shorthand for `group barred set`
PWINDOW->m_eGroupRules |= (GROUP_SET | GROUP_BARRED);
} else if (v == "lock") {
PWINDOW->m_eGroupRules |= GROUP_LOCK;
} else if (v == "invade") {
PWINDOW->m_eGroupRules |= GROUP_INVADE;
} else if (v == "barred") {
PWINDOW->m_eGroupRules |= GROUP_BARRED;
} else if (v == "deny") {
PWINDOW->m_sGroupData.deny = true;
} else if (v == "override") {
// Clear existing rules
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
} else if (v == "unset") {
// Clear existing rules and stop processing
PWINDOW->m_eGroupRules = GROUP_OVERRIDE;
break;
} else if (v == "always") {
if (vPrev == "set" || vPrev == "group")
PWINDOW->m_eGroupRules |= GROUP_SET_ALWAYS;
else if (vPrev == "lock")
PWINDOW->m_eGroupRules |= GROUP_LOCK_ALWAYS;
else
Debug::log(ERR, "windowrule `group` does not support `{} always`", vPrev);
}
vPrev = v;
}
} else if (r.szRule.find("idleinhibit") == 0) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
@@ -201,7 +257,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
} else if (IDLERULE == "fullscreen") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
} else {
Debug::log(ERR, "Rule idleinhibit: unknown mode %s", IDLERULE.c_str());
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
}
}
PWINDOW->applyDynamicRule(r);
@@ -214,76 +270,43 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, PWORKSPACE->m_efFullscreenMode);
}
PWINDOW->updateSpecialRenderData();
// disallow tiled pinned
if (PWINDOW->m_bPinned && !PWINDOW->m_bIsFloating)
PWINDOW->m_bPinned = false;
if (requestedWorkspace != "") {
// process requested workspace
if (requestedWorkspace.contains(' ')) {
// check for silent
if (requestedWorkspace.contains("silent")) {
const CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' ');
if (!WORKSPACEARGS[0].empty()) {
if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].find("silent") == 0)
workspaceSilent = true;
std::string requestedWorkspaceName;
const int REQUESTEDWORKSPACEID = getWorkspaceIDFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0), requestedWorkspaceName);
if (REQUESTEDWORKSPACEID != INT_MAX) {
auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID);
if (!pWorkspace)
pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->m_iMonitorID, requestedWorkspaceName);
PWINDOW->m_iWorkspaceID = pWorkspace->m_iID;
PWINDOW->m_iMonitorID = pWorkspace->m_iMonitorID;
if (g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->specialWorkspaceID && !pWorkspace->m_bIsSpecialWorkspace)
workspaceSilent = true;
shouldFocus = false;
requestedWorkspace = requestedWorkspace.substr(0, requestedWorkspace.find_first_of(' '));
if (!workspaceSilent) {
if (pWorkspace->m_bIsSpecialWorkspace)
g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID)->setSpecialWorkspace(pWorkspace);
else
g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspaceName);
PMONITOR = g_pCompositor->m_pLastMonitor;
}
if (!shouldFocus && requestedWorkspace == std::to_string(PMONITOR->activeWorkspace))
shouldFocus = true;
}
if (requestedWorkspace.find("special") == 0) {
workspaceSpecial = true;
workspaceSilent = true;
}
if (!workspaceSilent) {
g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspace);
PWINDOW->m_iMonitorID = g_pCompositor->m_pLastMonitor->ID;
PWINDOW->m_iWorkspaceID = g_pCompositor->m_pLastMonitor->activeWorkspace;
PMONITOR = g_pCompositor->m_pLastMonitor;
}
}
if (workspaceSilent) {
// get the workspace
auto PWORKSPACE = g_pCompositor->getWorkspaceByString(requestedWorkspace);
if (!PWORKSPACE) {
std::string workspaceName = "";
int workspaceID = 0;
if (requestedWorkspace.find("name:") == 0) {
workspaceName = requestedWorkspace.substr(5);
workspaceID = g_pCompositor->getNextAvailableNamedWorkspace();
} else if (workspaceSpecial) {
workspaceName = "";
workspaceID = getWorkspaceIDFromString(requestedWorkspace, workspaceName);
} else {
try {
workspaceID = std::stoi(requestedWorkspace);
} catch (...) {
workspaceID = -1;
Debug::log(ERR, "Invalid workspace requested in workspace silent rule!");
}
if (workspaceID < 1) {
workspaceID = -1; // means invalid
}
}
if (workspaceID != -1)
PWORKSPACE = g_pCompositor->createNewWorkspace(workspaceID, PWINDOW->m_iMonitorID, workspaceName);
}
if (PWORKSPACE) {
PWINDOW->m_iWorkspaceID = PWORKSPACE->m_iID;
PWINDOW->m_iMonitorID = PWORKSPACE->m_iMonitorID;
}
} else
workspaceSilent = false;
}
if (PWINDOW->m_bIsFloating) {
@@ -307,13 +330,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
std::clamp(MAXSIZE.y, 20.0, PMONITOR->vecSize.y) :
(!SIZEYSTR.contains('%') ? std::stoi(SIZEYSTR) : std::stof(SIZEYSTR.substr(0, SIZEYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
Debug::log(LOG, "Rule size, applying to window %lx", PWINDOW);
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
PWINDOW->m_vRealSize = Vector2D(SIZEX, SIZEY);
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv());
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); }
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("minsize") == 0) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
@@ -327,7 +350,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv());
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule minsize failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); }
} catch (...) { Debug::log(LOG, "Rule minsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("maxsize") == 0) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
@@ -341,11 +364,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv());
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule maxsize failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); }
} catch (...) { Debug::log(LOG, "Rule maxsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("move") == 0) {
try {
auto value = r.szRule.substr(r.szRule.find(' ') + 1);
const bool ONSCREEN = value.find("onscreen") == 0;
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
const bool CURSOR = value.find("cursor") == 0;
if (CURSOR)
@@ -358,8 +386,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
int posY = 0;
if (POSXSTR.find("100%-") == 0) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
const auto POSXRAW = POSXSTR.substr(5);
const auto POSXRAW = POSXSTR.substr(5);
posX =
PMONITOR->vecSize.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
@@ -378,8 +405,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (POSYSTR.find("100%-") == 0) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
const auto POSYRAW = POSYSTR.substr(5);
const auto POSYRAW = POSYSTR.substr(5);
posY =
PMONITOR->vecSize.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
@@ -397,14 +423,29 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
}
Debug::log(LOG, "Rule move, applying to window %lx", PWINDOW);
if (ONSCREEN) {
int borderSize = PWINDOW->getRealBorderSize();
posX = std::clamp(posX, (int)(PMONITOR->vecReservedTopLeft.x + borderSize),
(int)(PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PWINDOW->m_vRealSize.goalv().x - borderSize));
posY = std::clamp(posY, (int)(PMONITOR->vecReservedTopLeft.y + borderSize),
(int)(PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PWINDOW->m_vRealSize.goalv().y - borderSize));
}
Debug::log(LOG, "Rule move, applying to {}", PWINDOW);
PWINDOW->m_vRealPosition = Vector2D(posX, posY) + PMONITOR->vecPosition;
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); }
} else if (r.szRule == "center") {
PWINDOW->m_vRealPosition = PMONITOR->vecPosition + PMONITOR->vecSize / 2.f - PWINDOW->m_vRealSize.goalv() / 2.f;
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("center") == 0) {
auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r.szRule, 2, ' ');
if (ARGS[1] == "1")
RESERVEDOFFSET = (PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight) / 2.f;
PWINDOW->m_vRealPosition = PMONITOR->middle() - PWINDOW->m_vRealSize.goalv() / 2.f + RESERVEDOFFSET;
}
}
@@ -412,7 +453,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
// because the windows are animated on RealSize
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goalv();
g_pCompositor->moveWindowToTop(PWINDOW);
g_pCompositor->changeWindowZOrder(PWINDOW, true);
} else {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
@@ -429,21 +470,23 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
// check LS focus grab
const auto PFORCEFOCUS = g_pCompositor->getForceFocus();
const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus);
if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.keyboard_interactive)
PWINDOW->m_bNoInitialFocus = true;
if (!PWINDOW->m_bNoFocus && !PWINDOW->m_bNoInitialFocus &&
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent) {
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent &&
(!PFORCEFOCUS || PFORCEFOCUS == PWINDOW)) {
g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA);
PWINDOW->m_fDimPercent.setValueAndWarp(*PDIMSTRENGTH);
PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sAdditionalConfigData.forceNoDim ? 0.f : *PDIMSTRENGTH);
} else {
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PINACTIVEALPHA);
PWINDOW->m_fDimPercent.setValueAndWarp(0);
}
Debug::log(LOG, "Window got assigned a surfaceTreeNode %lx", PWINDOW->m_pSurfaceTree);
Debug::log(LOG, "Window got assigned a surfaceTreeNode {:x}", (uintptr_t)PWINDOW->m_pSurfaceTree);
if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xdg->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XDG Window Late");
@@ -464,7 +507,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
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,
"Xwayland Window Late");
PWINDOW->hyprListener_requestMinimize.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,
"Xwayland Window Late");
if (PWINDOW->m_iX11Type == 2)
@@ -480,7 +523,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
if ((requestsFullscreen || requestsMaximize || requestsFakeFullscreen) && !PWINDOW->m_bNoFullscreenRequest) {
if ((requestsFullscreen && (!PWINDOW->m_bNoFullscreenRequest || overridingNoFullscreen)) || (requestsMaximize && (!PWINDOW->m_bNoMaximizeRequest || overridingNoMaximize)) ||
requestsFakeFullscreen) {
// fix fullscreen on requested (basically do a switcheroo)
if (PWORKSPACE->m_bHasFullscreenWindow) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
@@ -491,6 +535,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bFakeFullscreenState = !PWINDOW->m_bFakeFullscreenState;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, true);
} else {
overridingNoFullscreen = false;
overridingNoMaximize = false;
PWINDOW->m_vRealPosition.warp();
PWINDOW->m_vRealSize.warp();
g_pCompositor->setWindowFullscreen(PWINDOW, true, requestsFullscreen ? FULLSCREEN_FULL : FULLSCREEN_MAXIMIZED);
@@ -508,7 +554,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->updateToplevel();
if (!shouldFocus) {
if (workspaceSilent) {
if (g_pCompositor->windowValidMapped(PFOCUSEDWINDOWPREV)) {
g_pCompositor->focusWindow(PFOCUSEDWINDOWPREV);
PFOCUSEDWINDOWPREV->updateWindowDecos(); // need to for some reason i cba to find out why
@@ -578,18 +624,20 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(finalFound);
finalFound->setHidden(true);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
}
}
}
}
}
Debug::log(LOG, "Map request dispatched, monitor %s, xywh: %f %f %f %f", PMONITOR->szName.c_str(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y,
PWINDOW->m_vRealSize.goalv().x, PWINDOW->m_vRealSize.goalv().y);
PWINDOW->m_bFirstMap = false;
Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition.goalv(), PWINDOW->m_vRealSize.goalv());
auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName;
g_pEventManager->postEvent(
SHyprIPCEvent{"openwindow", getFormat("%lx,%s,%s,%s", PWINDOW, workspaceID.c_str(), g_pXWaylandManager->getAppIDClass(PWINDOW).c_str(), PWINDOW->m_szTitle.c_str())});
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)});
EMIT_HOOK_EVENT("openWindow", PWINDOW);
// recalc the values for this window
@@ -601,15 +649,15 @@ void Events::listener_mapWindow(void* owner, void* data) {
void Events::listener_unmapWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Window %lx unmapped (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
Debug::log(LOG, "{:c} unmapped", PWINDOW);
if (!PWINDOW->m_pWLSurface.exists() || !PWINDOW->m_bIsMapped) {
Debug::log(WARN, "Window %lx unmapped without being mapped??", PWINDOW);
Debug::log(WARN, "{} unmapped without being mapped??", PWINDOW);
PWINDOW->m_bFadingOut = false;
return;
}
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", getFormat("%lx", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW);
@@ -634,8 +682,14 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->hyprListener_requestMinimize.removeCallback();
}
if (PWINDOW->m_bIsFullscreen) {
if (PWINDOW->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
// Allow the renderer to catch the last frame.
@@ -675,21 +729,21 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (wasLastWindow) {
const auto PWINDOWCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW);
Debug::log(LOG, "On closed window, new focused candidate is %lx", PWINDOWCANDIDATE);
Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE);
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow) {
if (!PWINDOWCANDIDATE)
g_pInputManager->refocus();
g_pInputManager->simulateMouseMovement();
else
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
} else {
g_pInputManager->refocus();
g_pInputManager->simulateMouseMovement();
}
} else {
Debug::log(LOG, "Unmapped was not focused, ignoring a refocus.");
}
Debug::log(LOG, "Destroying the SubSurface tree of unmapped window %lx", PWINDOW);
Debug::log(LOG, "Destroying the SubSurface tree of unmapped window {}", PWINDOW);
SubsurfaceTree::destroySurfaceTree(PWINDOW->m_pSurfaceTree);
PWINDOW->m_pSurfaceTree = nullptr;
@@ -700,14 +754,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID));
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
// do the animation thing
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
}
if (!PWINDOW->m_bX11DoesntWantBorders) // don't animate out if they weren't animated in.
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.vec() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise onWindowPostCreateClose will ignore it
@@ -736,18 +782,38 @@ void Events::listener_commitWindow(void* owner, void* data) {
PWINDOW->updateSurfaceOutputs();
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y);
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
// Debug::log(LOG, "Window %lx committed", PWINDOW); // SPAM!
if (PWINDOW->m_bIsX11 || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
return;
const auto ISRIGID = PWINDOW->m_uSurface.xdg->toplevel->current.max_height == PWINDOW->m_uSurface.xdg->toplevel->current.min_height &&
PWINDOW->m_uSurface.xdg->toplevel->current.max_width == PWINDOW->m_uSurface.xdg->toplevel->current.min_width;
if (!ISRIGID)
return;
const Vector2D REQUESTEDSIZE = {PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
if (REQUESTEDSIZE == PWINDOW->m_vReportedSize || REQUESTEDSIZE.x < 5 || REQUESTEDSIZE.y < 5)
return;
const Vector2D DELTA = PWINDOW->m_vReportedSize - REQUESTEDSIZE;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0;
PWINDOW->m_vRealSize = REQUESTEDSIZE;
g_pXWaylandManager->setWindowSize(PWINDOW, REQUESTEDSIZE, true);
g_pHyprRenderer->damageWindow(PWINDOW);
}
void Events::listener_destroyWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Window %lx destroyed, queueing. (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
Debug::log(LOG, "{:c} destroyed, queueing.", PWINDOW);
if (PWINDOW->m_bIsX11)
Debug::log(LOG, "XWayland class raw: %s", PWINDOW->m_uSurface.xwayland->_class);
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;
@@ -759,11 +825,13 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->hyprListener_destroyWindow.removeCallback();
PWINDOW->hyprListener_configureX11.removeCallback();
PWINDOW->hyprListener_setOverrideRedirect.removeCallback();
PWINDOW->hyprListener_associateX11.removeCallback();
PWINDOW->hyprListener_dissociateX11.removeCallback();
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
if (PWINDOW->m_pSurfaceTree) {
Debug::log(LOG, "Destroying Subsurface tree of %lx in destroyWindow", PWINDOW);
Debug::log(LOG, "Destroying Subsurface tree of {} in destroyWindow", PWINDOW);
SubsurfaceTree::destroySurfaceTree(PWINDOW->m_pSurfaceTree);
PWINDOW->m_pSurfaceTree = nullptr;
}
@@ -772,7 +840,7 @@ void Events::listener_destroyWindow(void* owner, void* data) {
if (!PWINDOW->m_bFadingOut) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
Debug::log(LOG, "Unmapped window %lx removed instantly", PWINDOW);
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
}
}
@@ -783,10 +851,12 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
return;
PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW);
g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("windowTitle", PWINDOW);
if (PWINDOW == g_pCompositor->m_pLastWindow) { // 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", getFormat("%lx", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("activeWindow", PWINDOW);
}
@@ -794,7 +864,7 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
PWINDOW->updateToplevel();
Debug::log(LOG, "Window %lx set title to %s", PWINDOW, PWINDOW->m_szTitle.c_str());
Debug::log(LOG, "Window {:x} set title to {}", PWINDOW, PWINDOW->m_szTitle);
}
void Events::listener_fullscreenWindow(void* owner, void* data) {
@@ -837,7 +907,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
} else {
if (!PWINDOW->m_uSurface.xwayland->mapped)
if (!PWINDOW->m_uSurface.xwayland->surface->mapped)
return;
if (!PWINDOW->m_bFakeFullscreenState)
@@ -853,7 +923,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
PWINDOW->updateToplevel();
Debug::log(LOG, "Window %lx fullscreen to %i", PWINDOW, PWINDOW->m_bIsFullscreen);
Debug::log(LOG, "{} fullscreen to {}", PWINDOW, PWINDOW->m_bIsFullscreen);
}
void Events::listener_activateXDG(wl_listener* listener, void* data) {
@@ -861,7 +931,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) {
static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue;
Debug::log(LOG, "Activate request for surface at %lx", E->surface);
Debug::log(LOG, "Activate request for surface at {:x}", (uintptr_t)E->surface);
if (!wlr_xdg_surface_try_from_wlr_surface(E->surface))
return;
@@ -871,25 +941,19 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) {
if (!PWINDOW || PWINDOW == g_pCompositor->m_pLastWindow)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%lx", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
PWINDOW->m_bIsUrgent = true;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
if (PWORKSPACE->m_pWlrHandle) {
wlr_ext_workspace_handle_v1_set_urgent(PWORKSPACE->m_pWlrHandle, 1);
}
if (!*PFOCUSONACTIVATE)
return;
if (PWINDOW->m_bIsFloating)
g_pCompositor->moveWindowToTop(PWINDOW);
g_pCompositor->changeWindowZOrder(PWINDOW, true);
g_pCompositor->focusWindow(PWINDOW);
Vector2D middle = PWINDOW->m_vRealPosition.goalv() + PWINDOW->m_vRealSize.goalv() / 2.f;
g_pCompositor->warpCursorTo(middle);
g_pCompositor->warpCursorTo(PWINDOW->middle());
}
void Events::listener_activateX11(void* owner, void* data) {
@@ -897,11 +961,11 @@ void Events::listener_activateX11(void* owner, void* data) {
static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue;
Debug::log(LOG, "X11 Activate request for window %lx", PWINDOW);
Debug::log(LOG, "X11 Activate request for window {}", PWINDOW);
if (PWINDOW->m_iX11Type == 2) {
Debug::log(LOG, "Unmanaged X11 %lx requests activate", PWINDOW);
Debug::log(LOG, "Unmanaged X11 {} requests activate", PWINDOW);
if (g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID())
return;
@@ -913,18 +977,17 @@ void Events::listener_activateX11(void* owner, void* data) {
if (PWINDOW == g_pCompositor->m_pLastWindow)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%lx", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
if (!*PFOCUSONACTIVATE)
return;
if (PWINDOW->m_bIsFloating)
g_pCompositor->moveWindowToTop(PWINDOW);
g_pCompositor->changeWindowZOrder(PWINDOW, true);
g_pCompositor->focusWindow(PWINDOW);
Vector2D middle = PWINDOW->m_vRealPosition.goalv() + PWINDOW->m_vRealSize.goalv() / 2.f;
g_pCompositor->warpCursorTo(middle);
g_pCompositor->warpCursorTo(PWINDOW->middle());
}
void Events::listener_configureX11(void* owner, void* data) {
@@ -932,7 +995,7 @@ void Events::listener_configureX11(void* owner, void* data) {
const auto E = (wlr_xwayland_surface_configure_event*)data;
if (!PWINDOW->m_uSurface.xwayland->mapped || !PWINDOW->m_bMappedX11) {
if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bMappedX11) {
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
return;
}
@@ -951,8 +1014,17 @@ void Events::listener_configureX11(void* owner, void* data) {
else
PWINDOW->setHidden(true);
PWINDOW->m_vRealPosition.setValueAndWarp(Vector2D(E->x, E->y));
const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords({E->x, E->y});
PWINDOW->m_vRealPosition.setValueAndWarp(LOGICALPOS);
PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(E->width, E->height));
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR)
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale);
}
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec();
PWINDOW->m_vSize = PWINDOW->m_vRealSize.vec();
@@ -960,7 +1032,7 @@ void Events::listener_configureX11(void* owner, void* data) {
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f)->activeWorkspace;
g_pCompositor->moveWindowToTop(PWINDOW);
g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->m_bCreatedOverFullscreen = true;
@@ -992,20 +1064,32 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
return;
}
if (abs(std::floor(POS.x) - PWINDOW->m_uSurface.xwayland->x) > 2 || abs(std::floor(POS.y) - PWINDOW->m_uSurface.xwayland->y) > 2 ||
abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) {
Debug::log(LOG, "Unmanaged window %lx requests geometry update to %i %i %i %i", PWINDOW, (int)PWINDOW->m_uSurface.xwayland->x, (int)PWINDOW->m_uSurface.xwayland->y,
(int)PWINDOW->m_uSurface.xwayland->width, (int)PWINDOW->m_uSurface.xwayland->height);
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOW->m_uSurface.xwayland->x, PWINDOW->m_uSurface.xwayland->y});
if (abs(std::floor(POS.x) - LOGICALPOS.x) > 2 || abs(std::floor(POS.y) - LOGICALPOS.y) > 2 || abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 ||
abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) {
Debug::log(LOG, "Unmanaged window {} requests geometry update to {:j} {} {}", PWINDOW, LOGICALPOS, (int)PWINDOW->m_uSurface.xwayland->width,
(int)PWINDOW->m_uSurface.xwayland->height);
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vRealPosition.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->x, PWINDOW->m_uSurface.xwayland->y));
PWINDOW->m_vRealPosition.setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y));
if (abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2)
PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->width, PWINDOW->m_uSurface.xwayland->height));
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR)
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale);
}
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.goalv();
PWINDOW->m_vSize = PWINDOW->m_vRealSize.goalv();
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f)->activeWorkspace;
g_pCompositor->moveWindowToTop(PWINDOW);
g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->updateWindowDecos();
g_pHyprRenderer->damageWindow(PWINDOW);
}
@@ -1019,12 +1103,24 @@ void Events::listener_setOverrideRedirect(void* owner, void* data) {
//}
}
void Events::listener_associateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW, "XWayland Window");
}
void Events::listener_dissociateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.removeCallback();
}
void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
const auto XWSURFACE = (wlr_xwayland_surface*)data;
Debug::log(LOG, "New XWayland Surface created (class %s).", XWSURFACE->_class);
Debug::log(LOG, "New XWayland Surface created (class {}).", XWSURFACE->_class ? XWSURFACE->_class : "null");
if (XWSURFACE->parent)
Debug::log(LOG, "Window parent data: %s at %lx", XWSURFACE->parent->_class, 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();
@@ -1034,7 +1130,8 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->m_pX11Parent = g_pCompositor->getX11Parent(PNEWWINDOW);
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XWSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XWayland Window");
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");
@@ -1047,12 +1144,12 @@ void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
if (XDGSURFACE->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
return;
Debug::log(LOG, "New XDG Surface created. (class: %s)", XDGSURFACE->toplevel->app_id);
Debug::log(LOG, "New XDG Surface created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window");
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");
}
@@ -1064,10 +1161,10 @@ void Events::listener_NewXDGDeco(wl_listener* listener, void* data) {
void Events::listener_requestMaximize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
if (PWINDOW->m_bNoFullscreenRequest)
if (PWINDOW->m_bNoMaximizeRequest)
return;
Debug::log(LOG, "Maximize request for %lx", PWINDOW);
Debug::log(LOG, "Maximize request for {}", PWINDOW);
if (!PWINDOW->m_bIsX11) {
const auto EV = (wlr_foreign_toplevel_handle_v1_maximized_event*)data;
@@ -1086,7 +1183,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
void Events::listener_requestMinimize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Minimize request for %lx", PWINDOW);
Debug::log(LOG, "Minimize request for {}", PWINDOW);
if (PWINDOW->m_bIsX11) {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1)
@@ -1094,13 +1191,13 @@ void Events::listener_requestMinimize(void* owner, void* data) {
const auto E = (wlr_xwayland_minimize_event*)data;
g_pEventManager->postEvent({"minimize", getFormat("%lx,%i", PWINDOW, (int)E->minimize)});
g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW, (int)E->minimize)});
EMIT_HOOK_EVENT("minimize", (std::vector<void*>{PWINDOW, (void*)E->minimize}));
wlr_xwayland_surface_set_minimized(PWINDOW->m_uSurface.xwayland, E->minimize && g_pCompositor->m_pLastWindow != PWINDOW); // fucking DXVK
} else {
const auto E = (wlr_foreign_toplevel_handle_v1_minimized_event*)data;
g_pEventManager->postEvent({"minimize", getFormat("%lx,%i", PWINDOW, E ? (int)E->minimized : 1)});
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)}));
}
}

View File

@@ -41,8 +41,8 @@ void CAnimatedVariable::create(ANIMATEDVARTYPE type, std::any val, SAnimationPro
default: ASSERT(false); break;
}
} catch (std::exception& e) {
Debug::log(ERR, "CAnimatedVariable create error: %s", e.what());
RASSERT(false, "CAnimatedVariable create error: %s", e.what());
Debug::log(ERR, "CAnimatedVariable create error: {}", e.what());
RASSERT(false, "CAnimatedVariable create error: {}", e.what());
}
}
@@ -51,13 +51,16 @@ CAnimatedVariable::~CAnimatedVariable() {
}
void CAnimatedVariable::unregister() {
g_pAnimationManager->m_lAnimatedVariables.remove(this);
if (!g_pAnimationManager)
return;
std::erase_if(g_pAnimationManager->m_vAnimatedVariables, [&](const auto& other) { return other == this; });
m_bIsRegistered = false;
disconnectFromActive();
}
void CAnimatedVariable::registerVar() {
if (!m_bIsRegistered)
g_pAnimationManager->m_lAnimatedVariables.push_back(this);
g_pAnimationManager->m_vAnimatedVariables.push_back(this);
m_bIsRegistered = true;
}
@@ -78,4 +81,18 @@ float CAnimatedVariable::getCurveValue() {
return 1.f;
return g_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT);
}
void CAnimatedVariable::connectToActive() {
g_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up
if (!m_bIsConnectedToActive)
g_pAnimationManager->m_vActiveAnimatedVariables.push_back(this);
m_bIsConnectedToActive = true;
}
void CAnimatedVariable::disconnectFromActive() {
std::erase_if(g_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == this; });
m_bIsConnectedToActive = false;
}

View File

@@ -1,16 +1,22 @@
#pragma once
#include "../defines.hpp"
#include <functional>
#include <any>
#include <chrono>
#include "Vector2D.hpp"
#include "Color.hpp"
#include "../macros.hpp"
enum ANIMATEDVARTYPE {
enum ANIMATEDVARTYPE
{
AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT,
AVARTYPE_VECTOR,
AVARTYPE_COLOR
};
enum AVARDAMAGEPOLICY {
enum AVARDAMAGEPOLICY
{
AVARDAMAGE_NONE = -1,
AVARDAMAGE_ENTIRE = 0,
AVARDAMAGE_BORDER,
@@ -30,6 +36,11 @@ class CAnimatedVariable {
void create(ANIMATEDVARTYPE, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
void create(ANIMATEDVARTYPE, std::any val, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
CAnimatedVariable(const CAnimatedVariable&) = delete;
CAnimatedVariable(CAnimatedVariable&&) = delete;
CAnimatedVariable& operator=(const CAnimatedVariable&) = delete;
CAnimatedVariable& operator=(CAnimatedVariable&&) = delete;
~CAnimatedVariable();
void unregister();
@@ -66,6 +77,9 @@ class CAnimatedVariable {
}
CAnimatedVariable& operator=(const Vector2D& v) {
if (v == m_vGoal)
return *this;
m_vGoal = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
@@ -76,6 +90,9 @@ class CAnimatedVariable {
}
CAnimatedVariable& operator=(const float& v) {
if (v == m_fGoal)
return *this;
m_fGoal = v;
animationBegin = std::chrono::system_clock::now();
m_fBegun = m_fValue;
@@ -86,6 +103,9 @@ class CAnimatedVariable {
}
CAnimatedVariable& operator=(const CColor& v) {
if (v == m_cGoal)
return *this;
m_cGoal = v;
animationBegin = std::chrono::system_clock::now();
m_cBegun = m_cValue;
@@ -97,6 +117,9 @@ class CAnimatedVariable {
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const Vector2D& v) {
if (v == m_vValue)
return;
m_vValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
@@ -106,6 +129,9 @@ class CAnimatedVariable {
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const float& v) {
if (v == m_fValue)
return;
m_fValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
@@ -115,6 +141,9 @@ class CAnimatedVariable {
// Sets the actual stored value, without affecting the goal, but resets the timer
void setValue(const CColor& v) {
if (v == m_cValue)
return;
m_cValue = v;
animationBegin = std::chrono::system_clock::now();
m_vBegun = m_vValue;
@@ -141,17 +170,8 @@ class CAnimatedVariable {
}
// checks if an animation is in progress
bool isBeingAnimated() {
switch (m_eVarType) {
case AVARTYPE_FLOAT: return m_fValue != m_fGoal;
case AVARTYPE_VECTOR: return m_vValue != m_vGoal;
case AVARTYPE_COLOR: return m_cValue != m_cGoal;
default: UNREACHABLE();
}
UNREACHABLE();
return false; // just so that the warning is suppressed
inline bool isBeingAnimated() {
return m_bIsBeingAnimated;
}
void warp(bool endCallback = true) {
@@ -171,6 +191,8 @@ class CAnimatedVariable {
default: UNREACHABLE();
}
m_bIsBeingAnimated = false;
if (endCallback)
onAnimationEnd();
}
@@ -209,10 +231,17 @@ class CAnimatedVariable {
m_bRemoveBeginAfterRan = remove;
}
/* Sets the update callback, called every time the value is animated and a step is done
Warning: calling unregisterVar/registerVar in this handler will cause UB */
void setUpdateCallback(std::function<void(void* thisptr)> func) {
m_fUpdateCallback = func;
}
/* resets all callbacks. Does not call any. */
void resetAllCallbacks() {
m_fBeginCallback = nullptr;
m_fEndCallback = nullptr;
m_fUpdateCallback = nullptr;
m_bRemoveBeginAfterRan = false;
m_bRemoveEndAfterRan = false;
}
@@ -237,8 +266,9 @@ class CAnimatedVariable {
SAnimationPropertyConfig* m_pConfig = nullptr;
bool m_bDummy = true;
bool m_bIsRegistered = false;
bool m_bDummy = true;
bool m_bIsRegistered = false;
bool m_bIsBeingAnimated = false;
std::chrono::system_clock::time_point animationBegin;
@@ -249,17 +279,30 @@ class CAnimatedVariable {
bool m_bRemoveBeginAfterRan = true;
std::function<void(void* thisptr)> m_fEndCallback;
std::function<void(void* thisptr)> m_fBeginCallback;
std::function<void(void* thisptr)> m_fUpdateCallback;
bool m_bIsConnectedToActive = false;
void connectToActive();
void disconnectFromActive();
// methods
void onAnimationEnd() {
m_bIsBeingAnimated = false;
disconnectFromActive();
if (m_fEndCallback) {
// loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false.
auto removeEndCallback = m_bRemoveEndAfterRan;
m_fEndCallback(this);
if (m_bRemoveEndAfterRan)
if (removeEndCallback)
m_fEndCallback = nullptr; // reset
}
}
void onAnimationBegin() {
m_bIsBeingAnimated = true;
connectToActive();
if (m_fBeginCallback) {
m_fBeginCallback(this);
if (m_bRemoveBeginAfterRan)

View File

@@ -1,5 +1,8 @@
#include "BezierCurve.hpp"
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include <chrono>
#include <algorithm>
void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
@@ -15,7 +18,7 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
m_dPoints.emplace_back(Vector2D(1, 1));
RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: %i)", m_dPoints.size());
RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: {})", m_dPoints.size());
// bake BAKEDPOINTS points for faster lookups
// T -> X ( / BAKEDPOINTS )
@@ -31,8 +34,8 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
getYForPoint(i);
const auto ELAPSEDCALCAVG = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGINCALC).count() / 1000.f / 10.f;
Debug::log(LOG, "Created a bezier curve, baked %i points, mem usage: %.2fkB, time to bake: %.2fµs. Estimated average calc time: %.2fµs.", BAKEDPOINTS, POINTSSIZE, ELAPSEDUS,
ELAPSEDCALCAVG);
Debug::log(LOG, "Created a bezier curve, baked {} points, mem usage: {:.2f}kB, time to bake: {:.2f}µs. Estimated average calc time: {:.2f}µs.", BAKEDPOINTS, POINTSSIZE,
ELAPSEDUS, ELAPSEDCALCAVG);
}
float CBezierCurve::getYForT(float t) {
@@ -45,27 +48,25 @@ float CBezierCurve::getXForT(float t) {
// Todo: this probably can be done better and faster
float CBezierCurve::getYForPoint(float x) {
if (x >= 1.0)
return 1.0;
if (x >= 1.f)
return 1.f;
// binary search for the range UPDOWN X
int upperT = BAKEDPOINTS - 1;
int lowerT = 0;
int mid = upperT / 2;
int index = 0;
bool below = true;
for (int step = (BAKEDPOINTS + 1) / 2; step > 0; step /= 2) {
if (below)
index += step;
else
index -= step;
while (std::abs(upperT - lowerT) > 1) {
if (m_aPointsBaked[mid].x > x) {
upperT = mid;
} else {
lowerT = mid;
}
mid = (upperT + lowerT) / 2;
below = m_aPointsBaked[index].x < x;
}
int lowerIndex = index - (!below || index == BAKEDPOINTS - 1);
// in the name of performance i shall make a hack
const auto LOWERPOINT = &m_aPointsBaked[std::clamp(lowerT, 0, BAKEDPOINTS - 1)];
const auto UPPERPOINT = &m_aPointsBaked[std::clamp(upperT, 0, BAKEDPOINTS - 1)];
const auto LOWERPOINT = &m_aPointsBaked[lowerIndex];
const auto UPPERPOINT = &m_aPointsBaked[lowerIndex + 1];
const auto PERCINDELTA = (x - LOWERPOINT->x) / (UPPERPOINT->x - LOWERPOINT->x);
@@ -73,4 +74,4 @@ float CBezierCurve::getYForPoint(float x) {
return 0.f;
return LOWERPOINT->y + (UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA;
}
}

View File

@@ -1,14 +1,15 @@
#pragma once
#include "../defines.hpp"
#include <deque>
#include <array>
#include <vector>
#include "Vector2D.hpp"
constexpr int BAKEDPOINTS = 255;
constexpr float INVBAKEDPOINTS = 1.f / BAKEDPOINTS;
// an implementation of a cubic bezier curve
// might do better later
// TODO: n-point curves
class CBezierCurve {
public:
// sets up the bezier curve.
@@ -24,4 +25,4 @@ class CBezierCurve {
std::deque<Vector2D> m_dPoints;
std::array<Vector2D, BAKEDPOINTS> m_aPointsBaked;
};
};

View File

@@ -1,5 +1,9 @@
#include "Color.hpp"
#include "../defines.hpp"
#define ALPHA(c) ((double)(((c) >> 24) & 0xff) / 255.0)
#define RED(c) ((double)(((c) >> 16) & 0xff) / 255.0)
#define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0)
#define BLUE(c) ((double)(((c)) & 0xff) / 255.0)
CColor::CColor() {}

View File

@@ -2,9 +2,11 @@
#include "../defines.hpp"
#include <algorithm>
#include "../Compositor.hpp"
#include <set>
#include <sys/utsname.h>
#include <iomanip>
#include <sstream>
#include <execinfo.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
@@ -124,20 +126,22 @@ static const float transforms[][9] = {
std::string absolutePath(const std::string& rawpath, const std::string& currentPath) {
auto value = rawpath;
if (value[0] == '.') {
auto currentDir = currentPath.substr(0, currentPath.find_last_of('/'));
if (value[1] == '.') {
auto parentDir = currentDir.substr(0, currentDir.find_last_of('/'));
value.replace(0, 2, parentDir);
} else {
value.replace(0, 1, currentDir);
}
}
if (value[0] == '~') {
static const char* const ENVHOME = getenv("HOME");
value.replace(0, 1, std::string(ENVHOME));
} else if (value[0] != '/') {
auto currentDir = currentPath.substr(0, currentPath.find_last_of('/'));
if (value[0] == '.') {
if (value[1] == '.' && value[2] == '/') {
auto parentDir = currentDir.substr(0, currentDir.find_last_of('/'));
value.replace(0, 2 + currentPath.empty(), parentDir);
} else if (value[1] == '/')
value.replace(0, 1 + currentPath.empty(), currentDir);
else
value = currentDir + '/' + value;
} else
value = currentDir + '/' + value;
}
return value;
@@ -149,27 +153,13 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const
wl_signal_add(pSignal, pListener);
Debug::log(LOG, "Registered signal for owner %lx: %lx -> %lx (owner: %s)", pOwner, pSignal, pListener, ownerString.c_str());
Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString);
}
void handleNoop(struct wl_listener* listener, void* data) {
// Do nothing
}
std::string getFormat(const char* fmt, ...) {
char* outputStr = nullptr;
va_list args;
va_start(args, fmt);
vasprintf(&outputStr, fmt, args);
va_end(args);
std::string output = std::string(outputStr);
free(outputStr);
return output;
}
std::string escapeJSONStrings(const std::string& str) {
std::ostringstream oss;
for (auto& c : str) {
@@ -222,7 +212,7 @@ float getPlusMinusKeywordResult(std::string source, float relative) {
try {
return relative + stof(source);
} catch (...) {
Debug::log(ERR, "Invalid arg \"%s\" in getPlusMinusKeywordResult!", source.c_str());
Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source);
return INT_MAX;
}
}
@@ -242,7 +232,7 @@ bool isNumber(const std::string& str, bool allowfloat) {
if (point)
return false;
point = true;
break;
continue;
}
if (!std::isdigit(c))
@@ -305,7 +295,140 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
outName = PLASTWORKSPACE->m_szName;
return PLASTWORKSPACE->m_iID;
} else {
if ((in[0] == 'm' || in[0] == 'e') && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
if (in[0] == 'r' && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Relative monitor workspace on monitor null!");
result = INT_MAX;
return result;
}
result = (int)getPlusMinusKeywordResult(in.substr(1), 0);
int remains = (int)result;
std::set<int> invalidWSes;
// Collect all the workspaces we can't jump to.
for (auto& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID)) {
// Can't jump to this workspace
invalidWSes.insert(ws->m_iID);
}
}
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) {
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) {
// Can't be invalid
continue;
}
// WS is bound to another monitor, can't jump to this
invalidWSes.insert(rule.workspaceId);
}
// Prepare all named workspaces in case when we need them
std::vector<int> namedWSes;
for (auto& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0)
continue;
namedWSes.push_back(ws->m_iID);
}
std::sort(namedWSes.begin(), namedWSes.end());
// Just take a blind guess at where we'll probably end up
int predictedWSID = g_pCompositor->m_pLastMonitor->activeWorkspace + remains;
int remainingWSes = 0;
char walkDir = in[1];
// sanitize. 0 means invalid oob in -
predictedWSID = std::max(predictedWSID, 0);
// Count how many invalidWSes are in between (how bad the prediction was)
int beginID = in[1] == '+' ? g_pCompositor->m_pLastMonitor->activeWorkspace + 1 : predictedWSID;
int endID = in[1] == '+' ? predictedWSID : g_pCompositor->m_pLastMonitor->activeWorkspace;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
remainingWSes++;
}
// Handle named workspaces. They are treated like always before other workspaces
if (g_pCompositor->m_pLastMonitor->activeWorkspace < 0) {
// Behaviour similar to 'm'
// Find current
int currentItem = -1;
for (size_t i = 0; i < namedWSes.size(); i++) {
if (namedWSes[i] == g_pCompositor->m_pLastMonitor->activeWorkspace) {
currentItem = i;
break;
}
}
currentItem += remains;
currentItem = std::max(currentItem, 0);
if (currentItem >= (int)namedWSes.size()) {
// At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0
int diff = currentItem - (namedWSes.size() - 1);
predictedWSID = diff;
int beginID = 1;
int endID = predictedWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
remainingWSes++;
}
walkDir = '+';
} else {
// We found our final ws.
remainingWSes = 0;
predictedWSID = namedWSes[currentItem];
}
}
// Go in the search direction for remainingWSes
// The performance impact is directly proportional to the number of open and bound workspaces
int finalWSID = predictedWSID;
if (walkDir == '-') {
int beginID = finalWSID;
int curID = finalWSID;
while (--curID > 0 && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) {
remainingWSes--;
}
finalWSID = curID;
}
if (finalWSID <= 0 || invalidWSes.find(finalWSID) != invalidWSes.end()) {
if (namedWSes.size()) {
// Go to the named workspaces
// Need remainingWSes more
int namedWSIdx = namedWSes.size() - remainingWSes;
// Sanitze
namedWSIdx = std::clamp(namedWSIdx, 0, (int)namedWSes.size() - 1);
finalWSID = namedWSes[namedWSIdx];
} else {
// Couldn't find valid workspace in negative direction, search last first one back up positive direction
walkDir = '+';
// We know, that everything less than beginID is invalid, so don't bother with that
finalWSID = beginID;
remainingWSes = 1;
}
}
}
if (walkDir == '+') {
int curID = finalWSID;
while (++curID < INT32_MAX && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) {
remainingWSes--;
}
finalWSID = curID;
}
}
result = finalWSID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(result);
if (PWORKSPACE)
outName = g_pCompositor->getWorkspaceByID(result)->m_szName;
else
outName = std::to_string(finalWSID);
} else if ((in[0] == 'm' || in[0] == 'e') && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
bool onAllMonitors = in[0] == 'e';
if (!g_pCompositor->m_pLastMonitor) {
@@ -354,7 +477,6 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
result = validWSes[currentItem];
outName = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName;
} else {
if (in[0] == '+' || in[0] == '-') {
if (g_pCompositor->m_pLastMonitor)
@@ -405,10 +527,10 @@ void logSystemInfo() {
uname(&unameInfo);
Debug::log(LOG, "System name: %s", unameInfo.sysname);
Debug::log(LOG, "Node name: %s", unameInfo.nodename);
Debug::log(LOG, "Release: %s", unameInfo.release);
Debug::log(LOG, "Version: %s", unameInfo.version);
Debug::log(LOG, "System name: {}", unameInfo.sysname);
Debug::log(LOG, "Node name: {}", unameInfo.nodename);
Debug::log(LOG, "Release: {}", unameInfo.release);
Debug::log(LOG, "Version: {}", unameInfo.version);
Debug::log(NONE, "\n");
@@ -417,7 +539,7 @@ void logSystemInfo() {
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
#endif
Debug::log(LOG, "GPU information:\n%s\n", GPUINFO.c_str());
Debug::log(LOG, "GPU information:\n{}\n", GPUINFO);
if (GPUINFO.contains("NVIDIA")) {
Debug::log(WARN, "Warning: you're using an NVIDIA GPU. Make sure you follow the instructions on the wiki if anything is amiss.\n");
@@ -426,7 +548,7 @@ void logSystemInfo() {
// log etc
Debug::log(LOG, "os-release:");
Debug::log(NONE, "%s", execAndGet("cat /etc/os-release").c_str());
Debug::log(NONE, "{}", execAndGet("cat /etc/os-release"));
}
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr) {
@@ -512,7 +634,7 @@ int64_t configStringToInt(const std::string& VALUE) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) {
Debug::log(WARN, "invalid length %i for rgba", VALUEWITHOUTFUNC.length());
Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)");
}
@@ -524,7 +646,7 @@ int64_t configStringToInt(const std::string& VALUE) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) {
Debug::log(WARN, "invalid length %i for rgb", VALUEWITHOUTFUNC.length());
Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
}
@@ -563,3 +685,25 @@ std::string replaceInString(std::string subject, const std::string& search, cons
}
return subject;
}
std::vector<SCallstackFrameInfo> getBacktrace() {
std::vector<SCallstackFrameInfo> callstack;
void* bt[1024];
size_t btSize;
char** btSymbols;
btSize = backtrace(bt, 1024);
btSymbols = backtrace_symbols(bt, btSize);
for (size_t i = 0; i < btSize; ++i) {
callstack.emplace_back(SCallstackFrameInfo{bt[i], std::string{btSymbols[i]}});
}
return callstack;
}
void throwError(const std::string& err) {
Debug::log(CRIT, "Critical error thrown: {}", err);
throw std::runtime_error(err);
}

View File

@@ -1,22 +1,41 @@
#pragma once
#include "../includes.hpp"
#include <string>
#include <wayland-server.h>
#include <wlr/util/box.h>
#include "Vector2D.hpp"
#include <vector>
#include <format>
std::string absolutePath(const std::string&, const std::string&);
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
std::string getFormat(const char* fmt, ...); // Basically Debug::log to a string
std::string escapeJSONStrings(const std::string& str);
void scaleBox(wlr_box*, float);
std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false);
bool isDirection(const std::string&);
int getWorkspaceIDFromString(const std::string&, std::string&);
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
void logSystemInfo();
std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid);
int64_t configStringToInt(const std::string&);
float getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
struct SCallstackFrameInfo {
void* adr = nullptr;
std::string desc;
};
std::string absolutePath(const std::string&, const std::string&);
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
std::string escapeJSONStrings(const std::string& str);
void scaleBox(wlr_box*, float);
std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false);
bool isDirection(const std::string&);
int getWorkspaceIDFromString(const std::string&, std::string&);
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
void logSystemInfo();
std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid);
int64_t configStringToInt(const std::string&);
float getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {
// no need for try {} catch {} because std::format_string<Args...> ensures that vformat never throw std::format_error
// because any suck format specifier will cause a compilation error
// this is actually what std::format in stdlib does
return std::vformat(fmt.get(), std::make_format_args(args...));
}

View File

@@ -14,6 +14,14 @@ CMonitor::CMonitor() {
CMonitor::~CMonitor() {
wlr_damage_ring_finish(&damage);
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
}
void CMonitor::onConnect(bool noRule) {
@@ -23,12 +31,14 @@ void CMonitor::onConnect(bool noRule) {
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this);
hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this);
hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this);
hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this);
hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this);
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this);
hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this);
if (m_bEnabled) {
wlr_output_enable(output, 1);
@@ -69,15 +79,12 @@ void CMonitor::onConnect(bool noRule) {
if (PREFSTATE)
wlr_output_set_mode(output, PREFSTATE);
else
Debug::log(WARN, "No mode found for disabled output %s", output->name);
Debug::log(WARN, "No mode found for disabled output {}", output->name);
wlr_output_enable(output, 0);
if (!wlr_output_commit(output)) {
Debug::log(ERR, "Couldn't commit disabled state on output %s", output->name);
}
Events::listener_change(nullptr, nullptr);
if (!wlr_output_commit(output))
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false;
@@ -117,11 +124,6 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true;
// create it in the arr
vecPosition = monitorRule.offset;
vecSize = monitorRule.resolution;
refreshRate = monitorRule.refreshRate;
wlr_output_enable(output, 1);
// set mode, also applies
@@ -132,20 +134,12 @@ void CMonitor::onConnect(bool noRule) {
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
Debug::log(LOG, "Added new monitor with name %s at %i,%i with size %ix%i, pointer %lx", output->name, (int)vecPosition.x, (int)vecPosition.y, (int)vecPixelSize.x,
(int)vecPixelSize.y, output);
// add a WLR workspace group
if (!pWLRWorkspaceGroupHandle) {
pWLRWorkspaceGroupHandle = wlr_ext_workspace_group_handle_v1_create(g_pCompositor->m_sWLREXTWorkspaceMgr);
}
wlr_ext_workspace_group_handle_v1_output_enter(pWLRWorkspaceGroupHandle, output);
Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output);
setupDefaultWS(monitorRule);
for (auto& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_szLastMonitor == szName) {
if (ws->m_szLastMonitor == szName || g_pCompositor->m_vMonitors.size() == 1 /* avoid lost workspaces on recover */) {
g_pCompositor->moveWorkspaceToMonitor(ws.get(), this);
ws->startAnim(true, true, true);
ws->m_szLastMonitor = "";
@@ -200,7 +194,7 @@ void CMonitor::onDisconnect() {
if (!m_bEnabled || g_pCompositor->m_bIsShuttingDown)
return;
Debug::log(LOG, "onDisconnect called for %s", output->name);
Debug::log(LOG, "onDisconnect called for {}", output->name);
// Cleanup everything. Move windows back, snap cursor, shit.
CMonitor* BACKUPMON = nullptr;
@@ -232,6 +226,7 @@ void CMonitor::onDisconnect() {
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) {
@@ -241,40 +236,38 @@ void CMonitor::onDisconnect() {
m_aLayerSurfaceLayers[i].clear();
}
Debug::log(LOG, "Removed monitor %s!", szName.c_str());
Debug::log(LOG, "Removed monitor {}!", szName);
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
if (!BACKUPMON) {
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDestroy.removeCallback();
g_pCompositor->m_bUnsafeState = true;
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
return;
}
// snap cursor
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f,
BACKUPMON->vecPosition.y + BACKUPMON->vecTransformedSize.y / 2.f);
if (BACKUPMON) {
// snap cursor
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f,
BACKUPMON->vecPosition.y + BACKUPMON->vecTransformedSize.y / 2.f);
// move workspaces
std::deque<CWorkspace*> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
wspToMove.push_back(w.get());
// move workspaces
std::deque<CWorkspace*> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
wspToMove.push_back(w.get());
}
}
}
for (auto& w : wspToMove) {
w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
for (auto& w : wspToMove) {
w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
}
} else {
g_pCompositor->m_pLastFocus = nullptr;
g_pCompositor->m_pLastWindow = nullptr;
g_pCompositor->m_pLastMonitor = nullptr;
}
activeWorkspace = -1;
@@ -285,8 +278,6 @@ void CMonitor::onDisconnect() {
wlr_output_commit(output);
std::erase_if(g_pCompositor->m_vWorkspaces, [&](std::unique_ptr<CWorkspace>& el) { return el->m_iMonitorID == ID; });
if (g_pCompositor->m_pLastMonitor == this)
g_pCompositor->setActiveMonitor(BACKUPMON);
@@ -318,6 +309,10 @@ void CMonitor::addDamage(const pixman_region32_t* rg) {
g_pCompositor->scheduleFrameForMonitor(this);
}
void CMonitor::addDamage(const CRegion* rg) {
addDamage(const_cast<CRegion*>(rg)->pixman());
}
void CMonitor::addDamage(const wlr_box* box) {
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
@@ -358,12 +353,12 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1;
newDefaultWorkspaceName = std::to_string(WORKSPACEID);
Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(szName).c_str());
Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"{}\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(szName));
}
auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID);
Debug::log(LOG, "New monitor: WORKSPACEID %d, exists: %d", WORKSPACEID, (int)(PNEWWORKSPACE != nullptr));
Debug::log(LOG, "New monitor: WORKSPACEID {}, exists: {}", WORKSPACEID, (int)(PNEWWORKSPACE != nullptr));
if (PNEWWORKSPACE) {
// workspace exists, move it to the newly connected monitor
@@ -377,15 +372,11 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(std::make_unique<CWorkspace>(ID, newDefaultWorkspaceName)).get();
// We are required to set the name here immediately
wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, newDefaultWorkspaceName.c_str());
PNEWWORKSPACE->m_iID = WORKSPACEID;
}
activeWorkspace = PNEWWORKSPACE->m_iID;
g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle);
PNEWWORKSPACE->setActive(true);
PNEWWORKSPACE->m_szLastMonitor = "";
}
@@ -474,6 +465,8 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
// remove from mvmonitors
std::erase_if(g_pCompositor->m_vMonitors, [&](const auto& other) { return other.get() == this; });
g_pCompositor->arrangeMonitors();
g_pCompositor->setActiveMonitor(g_pCompositor->m_vMonitors.front().get());
g_pCompositor->sanityCheckWorkspaces();
@@ -498,22 +491,20 @@ float CMonitor::getDefaultScale() {
return 1;
}
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal) {
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove) {
if (!pWorkspace)
return;
if (pWorkspace->m_bIsSpecialWorkspace) {
Debug::log(ERR, "BUG THIS: Attempted to changeWorkspace to special!");
if (specialWorkspaceID != pWorkspace->m_iID) {
Debug::log(LOG, "changeworkspace on special, togglespecialworkspace to id {}", pWorkspace->m_iID);
g_pKeybindManager->m_mDispatchers["togglespecialworkspace"](pWorkspace->m_szName == "special" ? "" : pWorkspace->m_szName);
}
return;
}
if (pWorkspace->m_iID == activeWorkspace) {
// in some cases (e.g. workspace from one monitor to another)
// we need to send this
g_pCompositor->deactivateAllWLRWorkspaces(pWorkspace->m_pWlrHandle);
pWorkspace->setActive(true);
if (pWorkspace->m_iID == activeWorkspace)
return;
}
const auto POLDWORKSPACE = g_pCompositor->getWorkspaceByID(activeWorkspace);
@@ -531,18 +522,29 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal) {
}
}
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
if (const auto PLASTWINDOW = pWorkspace->getLastFocusedWindow(); PLASTWINDOW)
g_pCompositor->focusWindow(PLASTWINDOW);
else {
g_pCompositor->focusWindow(nullptr);
g_pInputManager->refocus();
CWindow* pWindow = nullptr;
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal());
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
g_pCompositor->focusWindow(pWindow);
}
if (!noMouseMove)
g_pInputManager->simulateMouseMovement();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
// set some flags and fire event
g_pCompositor->deactivateAllWLRWorkspaces(pWorkspace->m_pWlrHandle);
pWorkspace->setActive(true);
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_szName});
EMIT_HOOK_EVENT("workspace", pWorkspace);
}
@@ -561,8 +563,10 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
if (!pWorkspace) {
// remove special if exists
if (const auto EXISTINGSPECIAL = g_pCompositor->getWorkspaceByID(specialWorkspaceID); EXISTINGSPECIAL)
if (const auto EXISTINGSPECIAL = g_pCompositor->getWorkspaceByID(specialWorkspaceID); EXISTINGSPECIAL) {
EXISTINGSPECIAL->startAnim(false, false);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + szName});
}
specialWorkspaceID = 0;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
@@ -576,10 +580,33 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
return;
}
if (specialWorkspaceID) {
if (const auto EXISTINGSPECIAL = g_pCompositor->getWorkspaceByID(specialWorkspaceID); EXISTINGSPECIAL)
EXISTINGSPECIAL->startAnim(false, false);
}
bool animate = true;
//close if open elsewhere
const auto PMONITORWORKSPACEOWNER = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
if (PMONITORWORKSPACEOWNER->specialWorkspaceID == pWorkspace->m_iID) {
PMONITORWORKSPACEOWNER->specialWorkspaceID = 0;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITORWORKSPACEOWNER->ID);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMONITORWORKSPACEOWNER->szName});
animate = false;
}
// open special
pWorkspace->m_iMonitorID = ID;
specialWorkspaceID = pWorkspace->m_iID;
pWorkspace->startAnim(true, true);
if (animate)
pWorkspace->startAnim(true, true);
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
w->m_iMonitorID = ID;
w->updateSurfaceOutputs();
}
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
@@ -587,8 +614,23 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
g_pCompositor->focusWindow(PLAST);
else
g_pInputManager->refocus();
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(this);
}
void CMonitor::setSpecialWorkspace(const int& id) {
setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id));
}
void CMonitor::moveTo(const Vector2D& pos) {
vecPosition = pos;
if (!isMirror())
wlr_output_layout_add(g_pCompositor->m_sWLROutputLayout, output, (int)vecPosition.x, (int)vecPosition.y);
}
Vector2D CMonitor::middle() {
return vecPosition + vecSize / 2.f;
}

View File

@@ -6,30 +6,48 @@
#include <vector>
#include <array>
#include <memory>
#include <xf86drmMode.h>
#include "Timer.hpp"
#include "Region.hpp"
#include <optional>
struct SMonitorRule;
struct SMonitorRule {
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60;
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
drmModeModeInfo drmMode = {};
std::optional<int> vrr;
};
class CMonitor {
public:
CMonitor();
~CMonitor();
Vector2D vecPosition = Vector2D(-1, -1); // means unset
Vector2D vecSize = Vector2D(0, 0);
Vector2D vecPixelSize = Vector2D(0, 0);
Vector2D vecTransformedSize = Vector2D(0, 0);
Vector2D vecPosition = Vector2D(-1, -1); // means unset
Vector2D vecXWaylandPosition = Vector2D(-1, -1); // means unset
Vector2D vecSize = Vector2D(0, 0);
Vector2D vecPixelSize = Vector2D(0, 0);
Vector2D vecTransformedSize = Vector2D(0, 0);
bool primary = false;
bool primary = false;
uint64_t ID = -1;
int activeWorkspace = -1;
float scale = 1;
uint64_t ID = -1;
int activeWorkspace = -1;
float scale = 1;
std::string szName = "";
std::string szName = "";
Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0);
Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0);
drmModeModeInfo customDrmMode = {};
// WLR stuff
wlr_damage_ring damage;
@@ -40,6 +58,8 @@ class CMonitor {
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
bool gammaChanged = false;
float xwaylandScale = 1.f;
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
@@ -53,10 +73,14 @@ class CMonitor {
bool RATScheduled = false;
CTimer lastPresentationTimer;
SMonitorRule activeMonitorRule;
// mirroring
CMonitor* pMirrorOf = nullptr;
std::vector<CMonitor*> mirrors;
CRegion lastFrameDamage; // stores last frame damage
// for the special workspace. 0 means not open.
int specialWorkspaceID = 0;
@@ -68,23 +92,23 @@ class CMonitor {
DYNLISTENER(monitorDamage);
DYNLISTENER(monitorNeedsFrame);
DYNLISTENER(monitorCommit);
// hack: a group = workspaces on a monitor.
// I don't really care lol :P
wlr_ext_workspace_group_handle_v1* pWLRWorkspaceGroupHandle = nullptr;
DYNLISTENER(monitorBind);
// methods
void onConnect(bool noRule);
void onDisconnect();
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg);
void addDamage(const wlr_box* box);
void setMirror(const std::string&);
bool isMirror();
float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false);
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false);
void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
std::shared_ptr<CMonitor>* m_pThisWrap = nullptr;
bool m_bEnabled = false;

143
src/helpers/Region.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include "Region.hpp"
#include <wlr/util/box.h>
#include <wlr/util/region.h>
CRegion::CRegion() {
pixman_region32_init(&m_rRegion);
}
CRegion::CRegion(pixman_region32_t* ref) {
pixman_region32_init(&m_rRegion);
pixman_region32_copy(&m_rRegion, ref);
}
CRegion::CRegion(double x, double y, double w, double h) {
pixman_region32_init_rect(&m_rRegion, x, y, w, h);
}
CRegion::CRegion(wlr_box* box) {
pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->width, box->height);
}
CRegion::CRegion(pixman_box32_t* box) {
pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1);
}
CRegion::CRegion(const CRegion& other) {
pixman_region32_init(&m_rRegion);
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
}
CRegion::CRegion(CRegion&& other) {
pixman_region32_init(&m_rRegion);
pixman_region32_copy(&m_rRegion, other.pixman());
}
CRegion::~CRegion() {
pixman_region32_fini(&m_rRegion);
}
CRegion& CRegion::clear() {
pixman_region32_clear(&m_rRegion);
return *this;
}
CRegion& CRegion::set(const CRegion& other) {
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this;
}
CRegion& CRegion::add(const CRegion& other) {
pixman_region32_union(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this;
}
CRegion& CRegion::add(double x, double y, double w, double h) {
pixman_region32_union_rect(&m_rRegion, &m_rRegion, x, y, w, h);
return *this;
}
CRegion& CRegion::subtract(const CRegion& other) {
pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this;
}
CRegion& CRegion::intersect(const CRegion& other) {
pixman_region32_intersect(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this;
}
CRegion& CRegion::intersect(double x, double y, double w, double h) {
pixman_region32_intersect_rect(&m_rRegion, &m_rRegion, x, y, w, h);
return *this;
}
CRegion& CRegion::invert(pixman_box32_t* box) {
pixman_region32_inverse(&m_rRegion, &m_rRegion, box);
return *this;
}
CRegion& CRegion::translate(const Vector2D& vec) {
pixman_region32_translate(&m_rRegion, vec.x, vec.y);
return *this;
}
CRegion& CRegion::scale(float scale) {
wlr_region_scale(&m_rRegion, &m_rRegion, scale);
return *this;
}
std::vector<pixman_box32_t> CRegion::getRects() const {
std::vector<pixman_box32_t> result;
int rectsNum = 0;
const auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum);
result.assign(RECTSARR, RECTSARR + rectsNum);
return result;
}
wlr_box CRegion::getExtents() {
pixman_box32_t* box = pixman_region32_extents(&m_rRegion);
return {box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1};
}
bool CRegion::containsPoint(const Vector2D& vec) const {
return pixman_region32_contains_point(&m_rRegion, vec.x, vec.y, nullptr);
}
bool CRegion::empty() const {
return !pixman_region32_not_empty(&m_rRegion);
}
Vector2D CRegion::closestPoint(const Vector2D& vec) const {
double bestDist = __FLT_MAX__;
Vector2D leader = vec;
for (auto& box : getRects()) {
double x = 0, y = 0;
if (vec.x >= box.x2)
x = box.x2 - 1;
else if (vec.x < box.x1)
x = box.x1;
else
x = vec.x;
if (vec.y >= box.y2)
y = box.y2 - 1;
else if (vec.y < box.y1)
y = box.y1;
else
y = vec.y;
double distance = sqrt(pow(x, 2) + pow(y, 2));
if (distance < bestDist) {
bestDist = distance;
leader = {x, y};
}
}
return leader;
}

59
src/helpers/Region.hpp Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <pixman.h>
#include <vector>
#include "Vector2D.hpp"
struct wlr_box;
class CRegion {
public:
/* Create an empty region */
CRegion();
/* Create from a reference. Copies, does not own. */
CRegion(pixman_region32_t* ref);
/* Create from a box */
CRegion(double x, double y, double w, double h);
/* Create from a wlr_box */
CRegion(wlr_box* box);
/* Create from a pixman_box32_t */
CRegion(pixman_box32_t* box);
CRegion(const CRegion&);
CRegion(CRegion&&);
~CRegion();
CRegion& operator=(CRegion&& other) {
pixman_region32_copy(&m_rRegion, other.pixman());
return *this;
}
CRegion& operator=(CRegion& other) {
pixman_region32_copy(&m_rRegion, other.pixman());
return *this;
}
CRegion& clear();
CRegion& set(const CRegion& other);
CRegion& add(const CRegion& other);
CRegion& add(double x, double y, double w, double h);
CRegion& subtract(const CRegion& other);
CRegion& intersect(const CRegion& other);
CRegion& intersect(double x, double y, double w, double h);
CRegion& translate(const Vector2D& vec);
CRegion& invert(pixman_box32_t* box);
CRegion& scale(float scale);
wlr_box getExtents();
bool containsPoint(const Vector2D& vec) const;
bool empty() const;
Vector2D closestPoint(const Vector2D& vec) const;
std::vector<pixman_box32_t> getRects() const;
pixman_region32_t* pixman() {
return &m_rRegion;
}
private:
pixman_region32_t m_rRegion;
};

View File

@@ -1,9 +1,10 @@
#pragma once
#include <array>
#include <vector>
#include <string>
inline const std::vector<std::string> SPLASHES = {
// clang-format off
"Woo, animations!",
"It's like Hypr, but better.",
"Release 1.0 when?",
@@ -60,4 +61,5 @@ inline const std::vector<std::string> SPLASHES = {
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo"
// clang-format on
};

View File

@@ -55,7 +55,7 @@ SSurfaceTreeNode* createSubsurfaceNode(SSurfaceTreeNode* pParent, SSubsurface* p
PNODE->pParent = pParent;
PNODE->pSubsurface = pSubsurface;
Debug::log(LOG, "Creating a subsurface Node! (pWindow: %lx)", pWindow);
Debug::log(LOG, "Creating a subsurface Node! {}", pWindow);
return PNODE;
}
@@ -63,7 +63,7 @@ SSurfaceTreeNode* createSubsurfaceNode(SSurfaceTreeNode* pParent, SSubsurface* p
SSurfaceTreeNode* SubsurfaceTree::createTreeRoot(wlr_surface* pSurface, applyGlobalOffsetFn fn, void* data, CWindow* pWindow) {
const auto PNODE = createTree(pSurface, pWindow);
Debug::log(LOG, "Creating a surfaceTree Root! (pWindow: %lx)", pWindow);
Debug::log(LOG, "Creating a surfaceTree Root! {}", pWindow);
PNODE->offsetfn = fn;
PNODE->globalOffsetData = data;
@@ -83,7 +83,7 @@ void SubsurfaceTree::destroySurfaceTree(SSurfaceTreeNode* pNode) {
}
if (!exists) {
Debug::log(ERR, "Tried to remove a SurfaceTreeNode that doesn't exist?? (Node %lx)", pNode);
Debug::log(ERR, "Tried to remove a SurfaceTreeNode that doesn't exist?? (Node {:x})", (uintptr_t)pNode);
return;
}
@@ -145,18 +145,18 @@ void Events::listener_newSubsurfaceNode(void* owner, void* data) {
const auto PNEWSUBSURFACE = &pNode->childSubsurfaces.emplace_back();
Debug::log(LOG, "Added a new subsurface %lx", PSUBSURFACE);
Debug::log(LOG, "Added a new subsurface {:x}", (uintptr_t)PSUBSURFACE);
PNEWSUBSURFACE->pSubsurface = PSUBSURFACE;
PNEWSUBSURFACE->pParent = pNode;
PNEWSUBSURFACE->hyprListener_map.initCallback(&PSUBSURFACE->events.map, &Events::listener_mapSubsurface, PNEWSUBSURFACE, "Subsurface");
PNEWSUBSURFACE->hyprListener_unmap.initCallback(&PSUBSURFACE->events.unmap, &Events::listener_unmapSubsurface, PNEWSUBSURFACE, "Subsurface");
PNEWSUBSURFACE->hyprListener_map.initCallback(&PSUBSURFACE->surface->events.map, &Events::listener_mapSubsurface, PNEWSUBSURFACE, "Subsurface");
PNEWSUBSURFACE->hyprListener_unmap.initCallback(&PSUBSURFACE->surface->events.unmap, &Events::listener_unmapSubsurface, PNEWSUBSURFACE, "Subsurface");
PNEWSUBSURFACE->hyprListener_destroy.initCallback(&PSUBSURFACE->events.destroy, &Events::listener_destroySubsurface, PNEWSUBSURFACE, "Subsurface");
PNEWSUBSURFACE->pWindowOwner = pNode->pWindowOwner;
if (PSUBSURFACE->mapped)
if (PSUBSURFACE->surface->mapped)
listener_mapSubsurface(PNEWSUBSURFACE, nullptr);
wlr_subsurface* existingWlrSubsurface;
@@ -174,7 +174,7 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
if (subsurface->pChild)
return;
Debug::log(LOG, "Subsurface %lx mapped", subsurface->pSubsurface);
Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface);
subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner);
}
@@ -182,7 +182,7 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
void Events::listener_unmapSubsurface(void* owner, void* data) {
SSubsurface* subsurface = (SSubsurface*)owner;
Debug::log(LOG, "Subsurface %lx unmapped", subsurface);
Debug::log(LOG, "Subsurface {:x} unmapped", (uintptr_t)subsurface);
if (subsurface->pSubsurface->surface == g_pCompositor->m_pLastFocus)
g_pInputManager->releaseAllMouseButtons();
@@ -221,7 +221,7 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
if (!g_pHyprRenderer->shouldRenderWindow(pNode->pWindowOwner)) {
static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue;
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from %lx because it's invisible.", pNode->pWindowOwner);
Debug::log(LOG, "Refusing to commit damage from {} because it's invisible.", pNode->pWindowOwner);
return;
}
@@ -229,6 +229,8 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
addSurfaceGlobalOffset(pNode, &lx, &ly);
const double SCALE = pNode->pWindowOwner && pNode->pWindowOwner->m_bIsX11 ? 1.0 / pNode->pWindowOwner->m_fX11SurfaceScaledBy : 1.0;
// I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox)
// What this does is that basically, if the pNode is a child of some other node, on commit,
// it will also damage (check & damage if needed) all its siblings.
@@ -237,12 +239,12 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
const auto NODECOORDS = pNode->pSubsurface ? Vector2D(pNode->pSubsurface->pSubsurface->current.x, pNode->pSubsurface->pSubsurface->current.y) : Vector2D();
if (&cs != pNode->pSubsurface && cs.pSubsurface) {
g_pHyprRenderer->damageSurface(cs.pSubsurface->surface, lx - NODECOORDS.x + cs.pSubsurface->current.x, ly - NODECOORDS.y + cs.pSubsurface->current.y);
g_pHyprRenderer->damageSurface(cs.pSubsurface->surface, lx - NODECOORDS.x + cs.pSubsurface->current.x, ly - NODECOORDS.y + cs.pSubsurface->current.y, SCALE);
}
}
if (pNode->pSurface && pNode->pSurface->exists())
g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly);
g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly, SCALE);
}
void Events::listener_destroySubsurface(void* owner, void* data) {
@@ -252,7 +254,7 @@ void Events::listener_destroySubsurface(void* owner, void* data) {
SubsurfaceTree::destroySurfaceTree(subsurface->pChild);
}
Debug::log(LOG, "Subsurface %lx destroyed", subsurface);
Debug::log(LOG, "Subsurface {:x} destroyed", (uintptr_t)subsurface);
subsurface->hyprListener_destroy.removeCallback();
subsurface->hyprListener_map.removeCallback();
@@ -264,7 +266,7 @@ void Events::listener_destroySubsurface(void* owner, void* data) {
void Events::listener_destroySubsurfaceNode(void* owner, void* data) {
SSurfaceTreeNode* pNode = (SSurfaceTreeNode*)owner;
Debug::log(LOG, "Subsurface Node %lx destroyed", pNode);
Debug::log(LOG, "Subsurface Node {:x} destroyed", (uintptr_t)pNode);
for (auto& c : pNode->childSubsurfaces)
destroySubsurface(&c);

View File

@@ -26,7 +26,8 @@ struct SSurfaceTreeNode {
void* globalOffsetData;
CWindow* pWindowOwner = nullptr;
bool operator==(const SSurfaceTreeNode& rhs) const {
//
bool operator==(const SSurfaceTreeNode& rhs) const {
return pSurface == rhs.pSurface;
}
};
@@ -43,7 +44,8 @@ struct SSubsurface {
CWindow* pWindowOwner = nullptr;
bool operator==(const SSubsurface& rhs) const {
//
bool operator==(const SSubsurface& rhs) const {
return pSubsurface == rhs.pSubsurface;
}
};

View File

@@ -14,4 +14,8 @@ int CTimer::getMillis() {
float CTimer::getSeconds() {
return std::chrono::duration_cast<std::chrono::milliseconds>(getDuration()).count() / 1000.f;
}
const std::chrono::system_clock::time_point& CTimer::chrono() const {
return m_tpLastReset;
}

View File

@@ -1,12 +1,13 @@
#pragma once
#include "../defines.hpp"
#include <chrono>
class CTimer {
public:
void reset();
float getSeconds();
int getMillis();
void reset();
float getSeconds();
int getMillis();
const std::chrono::system_clock::time_point& chrono() const;
private:
std::chrono::system_clock::time_point m_tpLastReset;

36
src/helpers/VarList.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "VarList.hpp"
#include <ranges>
#include <algorithm>
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
if (in.empty())
m_vArgs.emplace_back("");
std::string args{in};
size_t idx = 0;
size_t pos = 0;
std::ranges::replace_if(
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
for (const auto& s : args | std::views::split(0)) {
if (removeEmpty && s.empty())
continue;
if (++idx == lastArgNo) {
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
break;
}
pos += s.size() + 1;
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
}
}
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
size_t last = to == 0 ? size() : to;
std::string rolling;
for (size_t i = from; i < last; ++i) {
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
}
return rolling;
}

45
src/helpers/VarList.hpp Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include <vector>
#include <string>
#include "../macros.hpp"
class CVarList {
public:
/** Split string into arg list
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
@param delim if delimiter is 's', use std::isspace
@param removeEmpty remove empty args from argv
*/
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
~CVarList() = default;
size_t size() const {
return m_vArgs.size();
}
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
std::string operator[](const size_t& idx) const {
if (idx >= m_vArgs.size())
return "";
return m_vArgs[idx];
}
// for range-based loops
std::vector<std::string>::iterator begin() {
return m_vArgs.begin();
}
std::vector<std::string>::const_iterator begin() const {
return m_vArgs.begin();
}
std::vector<std::string>::iterator end() {
return m_vArgs.end();
}
std::vector<std::string>::const_iterator end() const {
return m_vArgs.end();
}
private:
std::vector<std::string> m_vArgs;
};

View File

@@ -16,7 +16,7 @@ Vector2D::~Vector2D() {}
double Vector2D::normalize() {
// get max abs
const auto max = abs(x) > abs(y) ? abs(x) : abs(y);
const auto max = std::abs(x) > std::abs(y) ? std::abs(x) : std::abs(y);
x /= max;
y /= max;
@@ -24,16 +24,24 @@ double Vector2D::normalize() {
return max;
}
Vector2D Vector2D::floor() {
return Vector2D((int)x, (int)y);
Vector2D Vector2D::floor() const {
return Vector2D(std::floor(x), std::floor(y));
}
Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) {
Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
}
double Vector2D::distance(const Vector2D& other) {
double Vector2D::distance(const Vector2D& other) const {
double dx = x - other.x;
double dy = y - other.y;
return std::sqrt(dx * dx + dy * dy);
}
bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const {
const auto a = ((p2.y - p3.y) * (x - p3.x) + (p3.x - p2.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));
const auto b = ((p3.y - p1.y) * (x - p3.x) + (p1.x - p3.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));
const auto c = 1 - a - b;
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1;
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <math.h>
#include <cmath>
#include <format>
#include "../macros.hpp"
class Vector2D {
public:
@@ -42,10 +44,53 @@ class Vector2D {
Vector2D operator/(const Vector2D& a) const {
return Vector2D(this->x / a.x, this->y / a.y);
}
double distance(const Vector2D& other);
Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D());
bool operator>(const Vector2D& a) const {
return this->x > a.x && this->y > a.y;
}
Vector2D floor();
bool operator<(const Vector2D& a) const {
return this->x < a.x && this->y < a.y;
}
double distance(const Vector2D& other) const;
Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D()) const;
Vector2D floor() const;
bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const;
};
/**
format specification
- 'j', as json array
- 'X', same as std::format("{}x{}", vec.x, vec.y)
- number, floating point precision, use `0` to format as integer
*/
template <typename CharT>
struct std::formatter<Vector2D, CharT> : std::formatter<CharT> {
bool formatJson = false;
bool formatX = false;
std::string precision = "";
FORMAT_PARSE(FORMAT_FLAG('j', formatJson) //
FORMAT_FLAG('X', formatX) //
FORMAT_NUMBER(precision),
Vector2D)
template <typename FormatContext>
auto format(const Vector2D& vec, FormatContext& ctx) const {
std::string formatString = precision.empty() ? "{}" : std::format("{{:.{}f}}", precision);
if (formatJson)
formatString = std::format("[{0}, {0}]", formatString);
else if (formatX)
formatString = std::format("{0}x{0}", formatString);
else
formatString = std::format("[Vector2D: x: {0}, y: {0}]", formatString);
try {
string buf = std::vformat(formatString, std::make_format_args(vec.x, vec.y));
return std::format_to(ctx.out(), "{}", buf);
} catch (std::format_error& e) { return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); }
}
};

View File

@@ -1,5 +1,6 @@
#include "WLClasses.hpp"
#include "../config/ConfigManager.hpp"
#include "../Compositor.hpp"
SLayerSurface::SLayerSurface() {
alpha.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_ENTIRE);
@@ -8,16 +9,109 @@ SLayerSurface::SLayerSurface() {
}
void SLayerSurface::applyRules() {
noAnimations = false;
forceBlur = false;
ignoreZero = false;
noAnimations = false;
forceBlur = false;
ignoreAlpha = false;
ignoreAlphaValue = 0.f;
xray = -1;
for (auto& rule : g_pConfigManager->getMatchingRules(this)) {
if (rule.rule == "noanim")
noAnimations = true;
else if (rule.rule == "blur")
forceBlur = true;
else if (rule.rule == "ignorezero")
ignoreZero = true;
else if (rule.rule.find("ignorealpha") == 0 || rule.rule.find("ignorezero") == 0) {
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.find("xray") == 0) {
CVarList vars{rule.rule, 0, ' '};
try {
xray = configStringToInt(vars[1]);
} catch (...) {}
}
}
}
CRegion SConstraint::getLogicCoordsRegion() {
CRegion result;
if (!constraint)
return result;
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return result;
result.add(&constraint->region); // surface-local coords
if (!PWINDOWOWNER->m_bIsX11) {
result.translate(PWINDOWOWNER->m_vRealPosition.goalv());
return result;
}
const auto COORDS = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealPosition.goalv() :
g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y});
const auto PMONITOR = PWINDOWOWNER->m_bIsMapped ? g_pCompositor->getMonitorFromID(PWINDOWOWNER->m_iMonitorID) : g_pCompositor->getMonitorFromVector(COORDS);
if (!PMONITOR)
return CRegion{};
result.scale(PMONITOR->xwaylandScale);
result.translate(COORDS);
return result;
}
Vector2D SConstraint::getLogicConstraintPos() {
if (!constraint)
return {};
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return {};
if (!PWINDOWOWNER->m_bIsX11)
return PWINDOWOWNER->m_vRealPosition.goalv();
const auto COORDS = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealPosition.goalv() :
g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y});
return COORDS;
}
Vector2D SConstraint::getLogicConstraintSize() {
if (!constraint)
return {};
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return {};
if (!PWINDOWOWNER->m_bIsX11)
return PWINDOWOWNER->m_vRealSize.goalv();
const auto PMONITOR = PWINDOWOWNER->m_bIsMapped ?
g_pCompositor->getMonitorFromID(PWINDOWOWNER->m_iMonitorID) :
g_pCompositor->getMonitorFromVector(g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y}));
if (!PMONITOR)
return {};
const auto SIZE = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealSize.goalv() :
Vector2D{PWINDOWOWNER->m_uSurface.xwayland->width, PWINDOWOWNER->m_uSurface.xwayland->height} * PMONITOR->xwaylandScale;
return SIZE;
}

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