Compare commits

...

196 Commits

Author SHA1 Message Date
vaxerski
9a3bec5d0a [gha] Nix: update inputs 2025-03-28 16:23:50 +00:00
Vaxry
29e2e59fdb version: bump to v0.48.1 2025-03-28 16:16:07 +00:00
Lee Bousfield
1fdb5ba09e xwayland: Cleanup server startup and FDs (#9769) 2025-03-28 16:14:46 +00:00
Lee Bousfield
aa421c2e95 core: Don't damage the entire surface every frame (#9763)
* core: Don't damage the entire surface every frame

* core: Damage buffer on dims or transform change

* core: Use guards for scale and tr equality checks
2025-03-28 16:14:46 +00:00
Vaxry
3c36e083f1 surfacestate: reset buffer bit before applying to current
fixes #9759
2025-03-28 16:14:46 +00:00
Vaxry
3fc0abcb56 workspaces: minor fixes to persistence
fixes #9741
2025-03-28 16:14:46 +00:00
Tom Englund
273f43bda6 internal: fix minor ubsan errors (#9743)
* opengl: check if g_pHyprOpengl exist

on compositor destruction we can hit a race where a CEGLSync destructs
and tries to call eglDestroySyncKHR on a null g_pHyprOpengl.

/src/render/OpenGL.cpp:3019:32: runtime error: member access within null pointer of type 'struct CHyprOpenGLImpl'
     #0 0x555565eed979 in CEGLSync::~CEGLSync() /src/render/OpenGL.cpp:3019
     #1 0x555565f6271e in std::default_delete<CEGLSync>::operator()(CEGLSync*)
     const /usr/lib/gcc/x86_64-pc-linux-gnu/14/include/g++-v14/bits/unique_ptr.h:93

* xdgshell: dont apply state on empty states

setsize can be called before a state has been added to pending,
resulting in calling ApplyState with a empty state.

/src/protocols/XDGShell.cpp:323:11: runtime error: null pointer passed as argument 2, which is declared to never be null
     #0 0x5555659bf67e in CXDGToplevelResource::applyState() /src/protocols/XDGShell.cpp:323
     #1 0x5555659bcedc in CXDGToplevelResource::setSize(Hyprutils::Math::Vector2D const&) /src/protocols/XDGShell.cpp:  256
     #2 0x555563eed0ef in Events::listener_commitWindow(void*, void*) /src/events/Windows.cpp:841
2025-03-28 16:14:46 +00:00
Vaxry
0a3948107a surfacestate: track and apply updated state
fixes #9729
2025-03-28 16:14:46 +00:00
Vaxry
189e18394e opengl: don't attempt to compile cm on gles3.0
also disable the error for the cm shader

fixes #9738
2025-03-28 16:14:46 +00:00
vaxerski
3eb859bb4e pass/rect: include clipBox in opaque calculations
ref #9730 ref #9709
2025-03-28 16:14:46 +00:00
vaxerski
eaa9663057 groupbar: round boxes 2025-03-28 16:14:46 +00:00
Tom Englund
5da8281d68 pass: remove unusued timeline in texpass (#9734)
remove unused timeline and waitpoint in texpass and especially remove
the passing it to renderTextureInternalWithDamage that implicitly
converted it to bool. setting discardActive and allowCustomUV
2025-03-28 16:14:46 +00:00
Arkady Buryakov
d2031ba3e0 Groupbar: apply scaling factor to text (#9731) 2025-03-28 16:14:46 +00:00
Tom Englund
c22f46768c xwl: dont close the fd to early (#9715)
dont close the fd until the wl_event_source is removed, so we dont get
another event triggered with an already closed fd.
2025-03-28 16:14:46 +00:00
Vaxry
66470020a7 seat: avoid sending null surfaces in leave/enter events
ref #9699
2025-03-28 16:14:46 +00:00
UjinT34
ed2f50d5ad renderer: Simplify and fix hdr metadata setting (#9706)
* simplify and fix hdr metadata setting

* keep incorrect(?) cm skip till #9600
2025-03-28 16:14:46 +00:00
Vaxry
5ee35f914f version: bump to 0.48.0 2025-03-23 14:56:13 +00:00
entailz
aa1bd647b1 core/Compositor.hpp: fix non-relative Texture import (#9703) 2025-03-23 14:49:10 +02:00
Lee Bousfield
fdb7ca6c8f core/compositor: Fix dropping cursor buffer data early (#9700) 2025-03-22 23:06:02 +01:00
UjinT34
6ab5a0befb renderer: fix cm_fs_passthrough (#9698) 2025-03-22 18:34:01 +01:00
Lee Bousfield
6384f4acf4 core/compositor: Correctly track SHM buffer damage (#9678) 2025-03-22 17:13:44 +01:00
Aaron Blasko
4600043a49 hyprpm: return 1 when plugins are outdated (#9694)
* hyprpm: return 1 when plugins are outdated

* clang-formatted
2025-03-22 17:01:35 +01:00
Lee Bousfield
279b06044c screencopy, render: Use explicit sync for screencopy (#9697)
* screencopy, render: Use explicit sync for screencopy

* screencopy: Check if explicit sync is enabled

* screencopy: Don't require explicit KMS enabled
2025-03-22 17:01:14 +01:00
Tom Englund
ccbdba7ee2 syncobj: refactor point timelines (#9689)
no need to store the resource, just store the csynctimeline as a shared
pointer and make the timeline own the syncobj fd.
2025-03-21 20:19:53 +01:00
UjinT34
c7f0519faf core: fix DS and VRR automation (#9334) 2025-03-21 14:33:07 +01:00
Lee Bousfield
7ea4fbf0ba types: Upgrade buffer ref from WP to SP (#9677) 2025-03-20 11:08:47 +00:00
Tom Englund
f6ca4bac51 syncobj: restore SHM buffer reset (#9675)
reset shm buffers early to mitigate stuttering animations, also reuse
the monitors eglSync and store the eglsync per monitor. this however
reintroduces flickering in dbeaver nonsyncobj application.
2025-03-20 11:39:55 +01:00
phonetic112
155eba57d8 groupbar: remove 2 pixel gap above groupbar (#9664) 2025-03-19 23:09:36 +01:00
Andrei V
7b10530a0d XWayland: restore the abstract socket, and make it optional (#9615)
* Revert "xwayland: don't create an abstract unix domain socket on linux (#8874)" (#9574)

This reverts commit 2b01a5bcf6.

* xwayland: make the abstract Unix domain socket optional (#9574)

* xwayland: extend the default permissions for a regular Unix domain socket (#9574)

* xwayland: a little refactoring for `createSocket`
2025-03-19 23:06:30 +01:00
Tom Englund
a25a214523 dmabuf: pop buffer on failure (#9620)
ensure it doesnt permanently gets stuck in the container on failure, pop
it from the container.
2025-03-19 17:49:20 +01:00
Lee Bousfield
c8d80a2920 ci: Fail on warnings (#9668)
* ci: Fail on warnings

* misc: Fix compiler warnings
2025-03-19 00:46:28 +01:00
Vaxry
03385fc07f seatmgr: avoid crash on null surfs 2025-03-18 19:43:05 +00:00
vaxerski
cca0f48b74 renderer: add an option to disable cm and auto-skip cm if not necessary
fixes #9641

adds render:cm_enabled default true
2025-03-18 11:30:08 +00:00
Kamikadze
60edb376f2 config/defaultConfig.hpp: windowrulev2 -> windowrule (#9663) 2025-03-18 13:13:03 +02:00
tachyglossues
6f74d8d7e9 example/hyprland.conf: windowrulev2 -> windowrule (#9662) 2025-03-18 12:24:43 +02:00
Vaxry
ec4bea7901 config: nuke windowrule v1 syntax 2025-03-18 01:37:00 +00:00
Honkazel
9171db1984 renderer: delete now redundant ifdefs (#9651)
Hyprland for now requires aquamarine =>0.8.0 anyway
2025-03-18 02:29:08 +01:00
Ikalco
5f60fc7d00 renderer: only commit hw cursor stuff if needed (#9654) 2025-03-17 22:06:41 +01:00
Lee Bousfield
c4f46473df monitor: Optimize direct scanout damage (#9653) 2025-03-17 22:05:44 +01:00
Lee Bousfield
011d7ccb91 internal: Fix compiler warnings (#9646) 2025-03-17 15:52:40 +01:00
Lee Bousfield
efc51eb7d1 managers: Use primary backend for cursor swapchain (#9645) 2025-03-17 15:51:18 +01:00
nyx
c2835b6b0f groupbar: remove recursive window recalc (#9561) 2025-03-17 15:50:49 +01:00
Mihai Fufezan
d5d7f69d1e flake.lock: update
aquamarine: bump to 0.8.0
hyprcursor: bump to 0.1.12
2025-03-17 14:50:35 +02:00
Ikalco
5cef2f44fe renderer: allow commits when buffer is unchanged but cursor changed (#9648) 2025-03-17 13:06:58 +01:00
Vaxry
22154fa272 opengl: simplify cm pipeline
fixes a few mistakes, and skips the CM shader in cpu instead of adding a costly branch

ref #9641
2025-03-16 21:50:20 +00:00
Mihai Fufezan
2ddd16ef28 CMake: install frag files (for real this time) 2025-03-16 19:35:37 +02:00
Mihai Fufezan
d7382aa8a1 CMake: install frag files 2025-03-16 18:27:27 +02:00
Mihai Fufezan
90306bdae6 Meson: include frags in globber 2025-03-16 16:30:50 +02:00
Vaxry
b1ab0f7539 splashes: update for 3ya 2025-03-16 03:03:37 +00:00
Tom Englund
bf5e4bf116 syncobj: dont crash compositor on protocol errors (#9627)
dont call a member on null pointer if client misbehaves.
as in the weak pointer being expired.
2025-03-15 19:57:52 +01:00
Blackilykat
4c471218c9 renderer: fix window offset for dragged windows (#9629) 2025-03-15 19:15:09 +01:00
phonetic112
e59680481d input: Fix clicking through groupbar tabs (#9606) 2025-03-15 01:22:39 +01:00
André Silva
f4315db50f nix: mesa -> libgbm (#9612)
d209d800b7
2025-03-14 20:24:55 +02:00
Vaxry
3cc8e8c6be renderer: don't crash if cm fails to compile 2025-03-14 15:20:08 +00:00
Vaxry
b37944605f protocols: fix include 2025-03-14 14:35:01 +00:00
Vaxry
f4995c1837 descriptions: remove allow_early_buffer_release 2025-03-14 14:14:10 +00:00
Tom Englund
6ffde36466 syncobj: use eventfd instead of stalling fd checks (#9437)
* syncobj: cleanup and use uniqueptrs

cleanup a bit missing removals if resource not good, erasing from
containers etc. make use of unique ptrs instead. and add default
destructors.

* syncobj: rework syncobj entirerly

remove early buffer release that was breaking explicit sync, the buffer
needs to exist until the surface commit event has been emitted and draw
calls added egl sync points, move to eventfd signaling instead of
stalling sync point checks, and recommit pending commits if waiting on a
signal. add a CDRMSyncPointState helper class. move a few weak pointers
to shared pointers so they dont destruct before we need to use them.

* syncobj: queue pending states for eventfd

eventfd requires us to queue pending stats until ready and then apply to
current, and also when no ready state exist commit the client commit on
the current existing buffer, if there is one.

* syncobj: clear current buffer damage

clear current buffer damage on current buffer commits.

* syncobj: cleanup code and fix hyprlock

remove unused code, and ensure we dont commit a empty texture causing
locksession protocol and gtk4-layer-shell misbehaving.

* syncobj: ensure buffers are cleaned up

ensure the containers having the various buffers actually gets cleaned
up from their containers, incase the CSignal isnt signaled because of
expired smart pointers or just wrong order destruction because mishaps.
also move the acquire/point setting to buffer attaching. instead of on
precommit.

* syncobj: remove unused code, optimize

remove unused code and merge sync fds if fence is valid, remove manual
directscanout buffer dropping that signals release point on pageflip, it
can cause us to signal the release point while still keeping the current
buffer and rendering it yet again causing wrong things.

* syncobj: delay buffer release on non syncobj

delay buffer releases on non syncobj surfaces until next commit, and
check on async buffers if syncobj and drop and signal the release point
on backend buffer release.

* syncobj: ensure we follow protocol

ensure we follow protocol by replacing acquire/release points if they
arrive late and replace already existing ones. also remove unneded
brackets, and dont try to manual lock/release buffers when it comes to
explicit protocol. it doesnt care about buffer releases only about
acquire and release points and signaling them.

* syncobj: lets not complicate things

set points in precommit, before checking protocol errors and we catch
any pending acquire/release points arriving late.

* syncobj: move SSurfaceState to types

remove destructor resource destroying, let resources destroys them on
their events, and move SSurfaceStates to types/SurfaceState.hpp

* syncobj: actually store the merged fd

have to actually store the mergedfd to use it.

* syncobj: cleanup a bit around fences

ensure the current asynchronous buffer is actually released on pageflip
not the previous. cleanup a bit FD handling in
commitPendingAndDoExplicitSync, and reuse the in fence when syncing
surfaces.

* syncobjs: ensure fence FD doesnt leak

calling resetexplicitfence without properly ensuring the FD is closed
before will leak it, store it per monitor and let it close itself with
the CFileDescriptor class.

* syncobj: ensure buffers are actually released

buffers were never being sent released properly.

* types: Defer buffer sync releaser until unlock

* syncobj: store directscanout fence in monitor

ensure the infence fd survives the scope of attemptdirectscanout so it
doesnt close before it should have.

* syncobj: check if if acquire is expired

we might hit a race to finish on exit where the timeline just has
destructed but the buffer waiter is still pending. and such we
removeAllWaiters null dereferences.

* syncobj: code style changes

remove quack comment, change to m_foo and use a std::vector and
weakpointer in the waiter for removal instead of a std::list.

* syncobj: remove unused async buffer drop

remove unused async buffer drop, only related to directscanout and is
handled elsewhere.

---------

Co-authored-by: Lee Bousfield <ljbousfield@gmail.com>
2025-03-14 15:08:20 +01:00
André Silva
c754d7963f nix: remove wayland-protocols overlay and bump flake (#9613)
* flake.lock: update nixpkgs

* nix: remove wayland-protocols bump

* flake.lock: update

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2025-03-14 09:14:59 +02:00
UjinT34
8c97cb7858 renderer: add simple color management (#9506)
Adds proper color management and transformations for CM surfaces.
2025-03-14 00:15:18 +01:00
phonetic112
e86d3a14e4 groupbar: add an option to adjust gap sizes (#9578) 2025-03-12 15:09:09 +01:00
nyx
4b25fbe5fd windows: respect noinitialfocus with workspace changes (#9586)
If a window is specifically placed on another workspace then we should
not "focus" that workspace even if the window itself never gets focused
when noinitialfocus is enabled for the window.
2025-03-11 18:32:01 +01:00
Vaxry
81e93acba4 groupbar: pass alpha to title tex render pass
ref #9598
2025-03-11 17:30:12 +00:00
Khalid J.
b21edb1a97 input: fix touch calibration matrix overriding
make the default behavior non-overriding to avoid overriding user-set
values elswehere.
2025-03-11 00:51:42 +00:00
MightyPlaza
e4af4b5e2e core: update decorations on lockgroups (#9573)
modified:   src/managers/KeybindManager.cpp
2025-03-10 18:40:46 +01:00
Ikalco
d30cc19d25 renderer: skip ds commits if buffer didn't change (#9556)
this fixes direct scanout glitches by ensuring that attemptDirectScanout doesn't try to recommit the same buffer to AQ
which would cause a pageflip event and the backendRelease to release the same buffer too early
2025-03-08 20:24:22 +01:00
nyx
f15b49e0fd core: prevent crash when monitor list is empty (#9572) 2025-03-08 20:14:03 +01:00
nyx
c544c5115c windowrules: store floating size on close instead of resize (#9569) 2025-03-08 14:20:27 +01:00
nyx
b80b64cd6c windowrules: add option to allow size persistence between app launches (#9422) 2025-03-08 02:12:02 +01:00
vaxerski
4082e876d5 [gha] Nix: update inputs 2025-03-07 22:56:23 +00:00
Lee Bousfield
8ce1665fdb protocols: Fix blocked color management get_information (#9563) 2025-03-07 23:54:56 +01:00
nyx
7753e8ea68 keybinds): prevent crash when executing movewindoworgroup (#9551) 2025-03-06 23:08:57 +01:00
Vaxry
cb4230e1c2 touch: reject swipe on locked sessions
fixes #9542
2025-03-06 22:08:23 +00:00
nyx
7055d0c138 master: add option to keep master window position (#9537) 2025-03-06 23:06:05 +01:00
nyx
4435f5c546 input: fixup mouse check for flipping x / y (#9529) 2025-03-06 17:33:01 +01:00
Lee Bousfield
7a84317f33 protocols: Fix cursor shape protocol valid shape check (#9547) 2025-03-06 16:54:47 +01:00
Abdalrahman Shaban
2433760786 hyprctl: Error handling improvements, minor cleanups (#9536) 2025-03-06 00:50:28 +01:00
Matt White
b51ab182ae socket2: add activespecialv2 (#9530) 2025-03-05 20:33:33 +01:00
nyx
d7e7a29261 input: add flip_x and flip_y for touchpad (#9481) 2025-03-03 21:56:01 +01:00
Ikalco
f1ef724a87 protocols: Ensure protocol dtors are called in ProtocolManager dtor (#9521) 2025-03-03 01:57:49 +01:00
Vaxry
474bfcbccd core: fix clang-format 2025-03-02 16:24:37 +00:00
Mihai Fufezan
905ca39bc9 nix/module: toHyprconf -> toHyprlang (#9221)
* nix/module: toHyprconf -> toHyprlang

Updated generator that will end up living in Nixpkgs' `lib/generators`.

* nix/module: use xdph package directly

The downstream module already applies hyprland's
finalPackage to the portalPackage.

* new lib

* lib: add flattenAttrs, remove category processing

Flattening attributes means we no longer need to process categories
separately. For all intents and purposes, they do not exist.

Simplify the codebase once again, while introducing an easy to grasp
recursive function.

Add a bit of documentation for toHyprlang, though I doubt it's clear
enough even now. Still needs proper NixDoc.

* lib: add proper NixDoc

* nix/lib: inherit from lib
2025-03-02 15:41:49 +02:00
Vaxry
3a21dd84b3 core: cleanup some smart pointer usage 2025-03-02 02:19:35 +00:00
Vaxry
e6be4af21f core: add default initializers for ccompositor ptrs 2025-03-02 02:16:24 +00:00
Vaxry
2c78867a98 core: cleanup window properties
they don't need to be in CConfigManager, and belong to window instead
2025-03-02 02:13:37 +00:00
phonetic112
2acbb59bf2 core: ignore internal move workspace calls if the workspace doesn't change (#9516) 2025-03-01 19:34:38 +01:00
Vaxry
11943f761e monitor: avoid crash on expired special workspace monitor owner
fixes #9477
2025-03-01 15:56:44 +00:00
Indy Ray
f148b96bea input: Provide a non-zero time for button release triggered during window unmap. (#9513)
It doesn't seem that this is actually required, but it seems more consistant with other areas, such as in CVirtualKeyboardV1Resource::releasePressed.
2025-03-01 16:53:30 +01:00
Lee Bousfield
445337d03d protocols: Fix IWaylandProtocol onDisplayDestroy m_pGlobal double-free (#9507) 2025-02-28 02:52:31 +01:00
Lee Bousfield
34f2a4713e core: Add null activeWorkspace check in moveWorkspaceToMonitor (#9503) 2025-02-28 02:52:23 +01:00
Tuur Vanhoutte
d5920bc5da internal: fix include paths (#9500)
* Fix include path in Monitor.hpp

* fix more includes
2025-02-27 00:56:11 +01:00
Vaxry
aee9b8ac19 protocols: fix relative include 2025-02-26 22:13:06 +00:00
Vaxry
32a3d84d74 protocols: fix wrong iface for xdg_dialog
fixes #9498
2025-02-26 20:49:31 +00:00
Soliprem
002cd91fbf nix: wp-protocol version bump (#9494) 2025-02-26 19:36:24 +02:00
littleblack111
01f4074421 hyprctl/layers: print pid for layershell clients (#9468)
Adds a `getPID()` fn to layershell to print it in `hyprctl layers`
2025-02-26 16:03:06 +01:00
UjinT34
6787fe8933 protocols: Support wp color management proto (#9444)
Adds support for the recently merged w-p CM protocol alongside the (now deprecated) old CM WIP protocols
2025-02-26 15:56:37 +01:00
牧羊犬真Q
f0850905f0 cmake: add CXX_STANDARD_REQUIRED flag to ensure c++ version must be c++26 (#9476)
As the [cmake documentation](https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD_REQUIRED.html) said, when we use the CMAKE_CXX_STANDARD without CXX_STANDARD_REQUIRED, then the standard version we set will be an optional value, and may decay back to older c++ version. So I add this configuration into cmakelist to ensure c++ version is c++26.
2025-02-25 14:03:09 +00:00
Vaxry
d1ea18b492 hyprctl/config: fixup config descriptions, fill out missing ones, provide more data
adds current and explicit, better parsing, and fixes missing / invalid ones
2025-02-25 14:03:04 +00:00
Vaxry
73ae9790f9 hyprpm: add --hl-url for custom forks 2025-02-24 13:11:30 +00:00
nyx
3458d7ac93 xwayland: improve dnd and cleanup (#9405)
Minor changes to xwayland dnd / regular dnd to fix various issues

---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
2025-02-24 03:52:10 +01:00
cyanbun96
e59464629f config: fix a possible crash in the monitor config parser (#9460)
* Less crash-prone monitor config parser

* clang-format
2025-02-23 23:43:24 +01:00
littleblack111
2e81648980 internal: use isNumber from hyprutils (#9467) 2025-02-23 18:35:29 +01:00
Tom Englund
8f50460bfe xcursor: move config check to sync gsettings (#9473)
move the check down into the function itself, 39df1f4 began syncing on
session change, but isnt checking for the setting itself, so instead of
duplicating checks all over. do it here.
2025-02-23 18:32:38 +01:00
Vaxry
f4b148df1e anr: add xwayland support (#9456)
Adds XWayland support to ANR dialogs
2025-02-21 21:26:53 +01:00
Vaxry
0e24f9c0d5 anr: add a note for no pid dialogs 2025-02-21 16:58:22 +00:00
Aaron Tulino
2cfa5d2408 hyprctl: Add IPC support for Hyprsunset (#9315)
* Add IPC support for Hyprsunset

* clang-format

* Add documentation
2025-02-21 15:20:11 +01:00
Mihai Fufezan
fa246cb6ed flake.lock: update hyprutils and aquamarine 2025-02-20 14:56:17 +02:00
nyx
6d25ef09cd xwayland: add INCR support for clipboard transfers (#9434)
add INCR protocol support for large transfers
fix write handling for partial transfers
fix an issue where wayland windows could die from a paste from an
xwayland window
2025-02-19 16:29:39 +01:00
Vaxry
0137a5f6cd anr: capitalize options 2025-02-18 15:20:00 +00:00
Vaxry
3c1a2e9fca config/descriptions: add enable_anr_dialog 2025-02-18 15:18:34 +00:00
Vaxry
fb8eaba83f core: add an ANR dialog
for xdg-shell, we can ping the wm_base, and thus render an ANR dialog if an app dies

for XWayland, there probably is a similar method, but I don't know about it and don't care.
2025-02-18 15:13:56 +00:00
nyx
3352317ca8 scripts/generateVersion.sh: convert to posix (#9433) 2025-02-18 16:18:22 +02:00
Vaxry
e59623d1d5 hyprctl: don't return empty str if there are no global shortcuts
fixes #6043
2025-02-18 00:33:36 +00:00
nyx
d01f9943e1 subsurfaces: dont try to access popup surfaces when handling subsurface updates (#9421) 2025-02-17 19:02:32 +01:00
andrewandreii
59c615c321 input: add follow_mouse_threshold (#9392) 2025-02-17 03:03:27 +01:00
Vaxry
e2a9271150 animations: add the gnomed animation style for windows 2025-02-16 20:53:49 +00:00
Maximilian Seidler
897ee276dc xwayland: configure on a configure request and cleanup geometry conversion (#9375)
* xwayland: configure the window on a configure request

* xwayland: move coordinate conversion handling to their own functions

* xwayland: rename configure to configureRequest
2025-02-16 01:20:42 +01:00
andrewandreii
94a30889a7 keybinds: fix some errors not returning a failure (#9416) 2025-02-15 20:04:02 +01:00
nyx
9228116c9a xwayland: fix a possible clipboard race condition (#9394) 2025-02-15 20:03:37 +01:00
Roberto Previdi
410da2e46f workspaces: update persistence on workspace rename (#9368) 2025-02-15 15:01:52 +01:00
nyx
7a6fde8414 internal: redirect exec'd app output to /dev/null (#9411) 2025-02-15 14:51:17 +01:00
Tom Englund
3eb6cb1875 syncobj: ensure we only add waiters on succesful checks (#9412)
timeline check only returns nullopt on ETIME_ERR , meaning the if check
later on returns true if drmSyncobjTimelineWait returns anything else
like EINVAL/EPERM/EACCESS etc, so actually check the returned .value()
of the std::optional. also move the fd to rvalue references.
2025-02-15 14:48:52 +01:00
Vaxry
2f967037aa config: set no_hw_cursors to auto by default and disable on tearing
when tearing, updates to the overlay plane may be ignored by the kernel. To avoid the cursor being a slideshow, disable hw cursors
2025-02-15 00:29:27 +00:00
Vaxry
1309b59f2c monitor: report a scheduled frame when tearing on cursor move 2025-02-15 00:18:43 +00:00
Vaxry
fb36815b01 renderer: remove spammy log 2025-02-14 22:51:41 +00:00
Honkazel
df3fba1572 internal: remove unused variable (#9402) 2025-02-14 16:31:03 +01:00
Maximilian Seidler
40adb3dfb4 config: actually set initial beziers (#9400) 2025-02-13 11:37:59 +00:00
Anthony Ruhier
5d2b008294 renderer: disable explicit if aquamarine output doesn't support it (#9396) (#9398)
The explicit settings ignore the aquamarine output.supportsExplicit
attribute, which creates glitches on drivers not supporting explicit
sync (example: freedreno).

If the output has been set as not supporting explicit, disable the
explicit settings.
2025-02-13 11:09:25 +00:00
Maximilian Seidler
208f94fe12 animations: sync inactive/active border angles when using borderangle animations (#9401) 2025-02-13 11:08:03 +00:00
Maximilian Seidler
1789405163 session-lock: send locked when in unsafe state (#9399) 2025-02-13 11:06:36 +00:00
nyx
68bb3e7f0a env: move XDG_SESSION_TYPE to before backend init (#9390) 2025-02-12 13:54:42 +00:00
J. J. Ramsey
f83fe9986b protocols: add version 2 of ext-idle-notify-v1 protocol (#8959)
Signed-off-by: James Ramsey <james.jehiel.ramsey@gmail.com>
Co-authored-by: James Ramsey <james.jehiel.ramsey@gmail.com>
2025-02-11 15:58:43 +01:00
Mihai Fufezan
f2d43e5f21 nix/overlays: add wayland-protocols overlay 2025-02-10 20:17:29 +02:00
Mihai Fufezan
e1179b665b flake.lock: update 2025-02-10 20:11:35 +02:00
Vaxry
f261fb6fe0 groupbar: fix groupbar missing when indicator_height is <= 0
ref #9291 #9372
2025-02-09 17:58:09 +00:00
Vaxry
3a43e7bb9a config: default movefocus_cycles_fullscreen to false
less confusing
2025-02-09 17:50:54 +00:00
Vaxry
1f97643e83 core: add mallopt to modify trim threshold 2025-02-09 17:38:20 +00:00
clamydo
56f6f61596 tablet: take active_area_size into account when sending tip event (#9325)
* fixes #9322, take `active_area_size` into account when sending tip event

* check if `relative_input` is set

As suggested by @y47s5s68tq870r7tc1xpp755pabopg

* refactoring active area in own function to keep it DRY

* coding style

* making transformation static

---------

Co-authored-by: clamydo <clamydo@users.noreply.github.com>
2025-02-09 15:30:30 +01:00
Honkazel
8e10ddb592 datadevice: fix wrong param (#9370)
Reason: look at the setReceive parameters and also at the next line(tries to narrow)
2025-02-08 22:00:55 +01:00
nyx
feb8ad48f0 groups: deactivate unfocused windows in groups (#9354) 2025-02-08 15:05:44 +01:00
Tom Englund
f7fcbe32c9 renderer: various fixes towards improving gpu calls robustness (#9188)
ensure framebuffer textures are detached and deleted, avoid leaving framebuffers bound when not needed

* render: avoid calling glDeleteProgram on no program

its safe to do so but it adds a bunch of unnecessery lines in apitrace
when tracing. if guard it and return early.

* opengl: ensure texture and buffers are unbound

ensure bound buffers are unbound after use, also detach textures from
framebuffer before deleting it otherwise it will become dangling and
essentially leak.
2025-02-08 01:46:26 +01:00
Paul Cross
a724332eb8 desktop: add ability to target pinned windows in workspace rules (#9344)
* desktop: add ability to target pinned windows in workspace rules

* desktop: add ability to target pinned windows in workspace rules

* fix formating
2025-02-08 01:45:13 +01:00
Vaxry
54441e0c4e renderer: fix fade out on silent moves to special 2025-02-06 17:45:28 +00:00
WhySoBad
acbcf0cf11 toplevelexport: fix transformed origin for shm buffers (#9343)
* toplevelexport: fix transformed origin for shm buffers

* toplevelexport: fix style nits
2025-02-06 12:42:20 +00:00
vaxerski
30b49c75bf popup: improve ::at() behavior 2025-02-06 12:15:41 +00:00
Maximilian Seidler
ff9e059de6 window: move size reporting to animation begin callback (#9298)
* window: fix resizes with an update callback

* window: fixup sendWindowSize

Remove the size argument from sendWindowSize, since it is now a member of the Window class
and we don't want any mismatches between m_vRealSize and what we report.

Remove sendWindowSize from mapWindow, since we shouldn't need it.

* window: sendWindowSize on animation begin

* window: move most calls to sendWindowSize to the animation begin
callback

* window: remove sendWindowSize in unmanaged if not fullscreen
2025-02-06 11:21:04 +00:00
Tom Englund
f1e32cd122 core: avoid .at() and use [] operator (#9347)
avoid .at() where it makes sense and use [] operator in loops.
2025-02-06 11:18:04 +00:00
nyx
868b2b544a window: fix missing surface null checks to prevent crashes (#9350) 2025-02-06 11:17:12 +00:00
Vaxry
8a6778f0a0 scripts: don't overwrite generated version if we're not in a git repo 2025-02-05 15:42:01 +00:00
raf
64591c85aa nix: add hydraJobs output for aggregating Hyprland build jobs 2025-02-05 16:55:33 +02:00
nyx
873bff390e renderer: fix missing null checks to prevent crashes (#9332) 2025-02-05 15:53:09 +01:00
Tom Benham
84c9baecc6 keybinds: Added toggleswallow dispatcher (#5548)
* Added `toggleswallow` dispatcher

* clang-format

* Removed brackets for 1-line if
2025-02-05 09:56:41 +00:00
vaxerski
3b99e906df compositor: don't iterate over unmapped ls-es in vectorToLS
fixes #9312
2025-02-04 10:18:08 +00:00
Vaxry
5e7292434a compositor: guard null ws in updating fade 2025-02-03 22:36:10 +00:00
Vaxry
70cfc7cc9c cmonitor: guard old workspace 2025-02-03 19:53:14 +00:00
Vaxry
1da0b2c02e subprojects: update h-p
fixes #9309
2025-02-03 19:45:26 +00:00
Alexander
708d166360 dispatchers: add cyclenext hist option (#9055) 2025-02-03 02:34:30 +01:00
Vaxry
44004abc01 config: fix includes 2025-02-02 22:20:28 +00:00
UjinT34
31431a9271 protocols: Support content-type-v1 proto (#9226) 2025-02-02 20:25:29 +01:00
Alexander
70d94fec13 refactor: clang-tidy in compositor (#9241)
Co-authored-by: Alexandr Krylov <t@ruenya.nix>
2025-02-02 18:34:26 +01:00
outfoxxed
373108102c protocols: implement hyprland-ctm-control rev 2 (#9267)
* protocols: implement hyprland-ctm-control v2

* bump h-p and nix
2025-02-02 18:31:04 +01:00
micha4w
97a24ec6f3 Nix: change meson buildtype from debugoptimized to debug 2025-02-02 14:32:03 +02:00
Vaxry
64fefa3749 desktop: move popups to UPs and fix missing subsurface resource
fixes #9283
2025-02-01 19:10:19 +00:00
Vaxry
e380b6ed66 popup: take xdg geometry into account in input calcs
fixes #9023
2025-02-01 15:49:10 +00:00
Vaxry
88adae73ba pass: add input region debug 2025-02-01 15:31:31 +00:00
Vaxry
c6f672257b desktop: move popup and subsurface ctors to factories
makes sure m_pSelf is set before we do anything like possibly adding children

fixes #9275

supersedes #9276
2025-02-01 15:08:30 +00:00
Maximilian Seidler
5b43c106bd animation: don't immediately disconnect active vars during tick (#9272) 2025-02-01 15:44:20 +01:00
Mihai Fufezan
d11d069715 CI/Nix: remove deprecated magic-nix-cache-action 2025-02-01 09:29:06 +02:00
nyx
ddf180fa30 render: enforce framebuffer offloading and remove introspection toggle (#9217) 2025-01-31 17:08:43 +01:00
nyx
a4b7d1c2d7 xwayland: correct pointer coordinate mismatch in X11 windows (#9259)
refactor(xwayland): add back comments
2025-01-31 14:36:56 +01:00
Mihai Fufezan
12b5034c99 configWatcher: watch both symlinks and canonical paths (#9219) 2025-01-31 14:36:22 +01:00
Vaxry
9c38287410 groupbar: various visual improvements
added rounding, round at edges, and indicator height
2025-01-31 13:32:36 +00:00
Brayden Zee
ac5668192e desktop: fix segfault when destroying a partially create layer surface (#9247) 2025-01-31 13:33:51 +01:00
Aaron Tulino
7d51dee103 hyprctl: Extract IPC code to separate method (#9223)
This makes it possible to use the same IPC code for more projects in the future
2025-01-31 13:33:36 +01:00
nyx
ea16b64ec1 xwayland: prevent invalid window configurations for X11 apps (#9253)
* fix(xwayland): prevent invalid window configurations for X11 apps

* refact(xwayland): remove unneeded line
2025-01-31 13:26:08 +01:00
Ikalco
e6a9cfab91 monitor: preferred mode now tries first 3 modes if preferred fails before erroring (#9246) 2025-01-31 13:23:32 +01:00
nyx
ef03f69116 xwayland: handle window coords correctly (#9238) 2025-01-30 22:36:42 +01:00
Tom Englund
32c0fa2f2f core: begin using CFileDescriptor from hyprutils (#9122)
* config: make fd use CFileDescriptor

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

* hyprctl: make fd use CFileDescriptor

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

* ikeyboard: make fd use CFileDescriptor

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

* core: make SHMFile functions use CFileDescriptor

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

* core: make explicit sync use CFileDescriptor

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

* eventmgr: make fd and sockets use CFileDescriptor

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

* eventloopmgr: make timerfd use CFileDescriptor

make the timerfd use CFileDescriptor instead of manual fd handling

* opengl: make gbm fd use CFileDescriptor

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

* core: make selection source/offer use CFileDescriptor

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

* protocols: convert protocols fd to CFileDescriptor

make most fd handling use CFileDescriptor in protocols

* shm: make SHMPool use CfileDescriptor

make SHMPool use CFileDescriptor instead of manual fd handling.

* opengl: duplicate fd with CFileDescriptor

duplicate fenceFD with CFileDescriptor duplicate instead.

* xwayland: make sockets and fds use CFileDescriptor

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

* keybindmgr: make sockets and fds use CFileDescriptor

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

* dup include + clang moment

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

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

* remove
2025-01-27 17:41:26 +01:00
Vaxry
2a478c30ca core: fix clang-format 2025-01-27 13:41:38 +00:00
195 changed files with 7163 additions and 3456 deletions

View File

@@ -35,6 +35,7 @@ runs:
libinput \
libjxl \
libliftoff \
libspng \
libwebp \
libxcursor \
libxcvt \

View File

@@ -21,7 +21,7 @@ jobs:
- name: Build Hyprland
run: |
make all
CFLAGS=-Werror CXXFLAGS=-Werror make all
- name: Compress and package artifacts
run: |
@@ -127,27 +127,3 @@ jobs:
- name: clang-format check
run: ninja -C build clang-format-check
- name: clang-format apply
if: ${{ failure() && github.event_name == 'pull_request' }}
run: ninja -C build clang-format
- name: Create patch
if: ${{ failure() && github.event_name == 'pull_request' }}
run: |
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch
echo '<details>' >> clang-format.patch
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
echo >> clang-format.patch
echo '```diff' >> clang-format.patch
git diff >> clang-format.patch
echo '```' >> clang-format.patch
echo >> clang-format.patch
echo '</details>' >> clang-format.patch
- name: Comment patch
if: ${{ failure() && github.event_name == 'pull_request' }}
uses: mshick/add-pr-comment@v2
with:
message-path: |
clang-format.patch

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

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

View File

@@ -19,7 +19,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: cachix/cachix-action@v15
with:

View File

@@ -77,6 +77,7 @@ add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
include_directories(. "src/" "protocols/")
set(CMAKE_CXX_STANDARD 26)
set(CXX_STANDARD_REQUIRED ON)
add_compile_options(
-Wall
-Wextra
@@ -101,13 +102,21 @@ else()
endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5)
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.4.0)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR)
list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH)
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR})
add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR})
add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH})
add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
@@ -120,7 +129,7 @@ pkg_check_modules(
xkbcommon
uuid
wayland-server>=1.22.90
wayland-protocols
wayland-protocols>=1.41
cairo
pango
pangocairo
@@ -197,6 +206,12 @@ if(NOT HAS_TIMERFD AND epoll_FOUND)
target_link_libraries(Hyprland PkgConfig::epoll)
endif()
check_include_file("sys/inotify.h" HAS_INOTIFY)
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
if(NOT HAS_INOTIFY AND inotify_FOUND)
target_link_libraries(Hyprland PkgConfig::inotify)
endif()
if(LEGACY_RENDERER)
message(STATUS "Using the legacy GLES2 renderer!")
add_compile_definitions(LEGACY_RENDERER)
@@ -298,7 +313,7 @@ endfunction()
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.0)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.2)
if(hyprland_protocols_dep_FOUND)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
@@ -362,6 +377,8 @@ protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)
protocolnew("staging/content-type" "content-type-v1" false)
protocolnew("staging/color-management" "color-management-v1" false)
protocolwayland()
@@ -427,4 +444,5 @@ install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
PATTERN "*.h*")
PATTERN "*.h*"
PATTERN "*.frag")

View File

@@ -1 +1 @@
0.47.0
0.48.1

View File

@@ -139,10 +139,10 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
@@ -276,14 +276,11 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule
# windowrule = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0

137
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1737636397,
"narHash": "sha256-F5MbBj3QVorycVSFE9qjuOTLtIQBqt2VWbXa0uwzm98=",
"lastModified": 1742213273,
"narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "7fe006981fae53e931f513026fc754e322f13145",
"rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
]
},
"locked": {
"lastModified": 1737634937,
"narHash": "sha256-Ffw4ujFpi++6pPHe+gCBOfDgAoNlzVPZN6MReC1beu8=",
"lastModified": 1742215578,
"narHash": "sha256-zfs71PXVVPEe56WEyNi2TJQPs0wabU4WAlq0XV7GcdE=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "9c5dd1f7c825ee47f72727ad0a4e16ca46a2688e",
"rev": "2fd36421c21aa87e2fe3bee11067540ae612f719",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
]
},
"locked": {
"lastModified": 1737634889,
"narHash": "sha256-9JZE3KxcXOqZH9zs3UeadngDiK/yIACTiAR8HSA/TNI=",
"lastModified": 1739049071,
"narHash": "sha256-3+7TpXMrbsUXSwgr5VAKAnmkzMb6JO+Rvc9XRb5NMg4=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "0d77b4895ad5f1bb3b0ee43103a5246c58b65591",
"rev": "175c6b29b6ff82100539e7c4363a35a02c74dd73",
"type": "github"
},
"original": {
@@ -128,11 +128,11 @@
]
},
"locked": {
"lastModified": 1737556638,
"narHash": "sha256-laKgI3mr2qz6tas/q3tuGPxMdsGhBi/w+HO+hO2f1AY=",
"lastModified": 1738422629,
"narHash": "sha256-5v+bv75wJWvahyM2xcMTSNNxmV8a7hb01Eey5zYnBJw=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "4c75dd5c015c8a0e5a34c6d02a018a650f57feb5",
"rev": "755aef8dab49d0fc4663c715fa4ad221b2aedaed",
"type": "github"
},
"original": {
@@ -143,7 +143,10 @@
},
"hyprland-qt-support": {
"inputs": {
"hyprlang": "hyprlang",
"hyprlang": [
"hyprland-qtutils",
"hyprlang"
],
"nixpkgs": [
"hyprland-qtutils",
"nixpkgs"
@@ -170,7 +173,12 @@
"hyprland-qtutils": {
"inputs": {
"hyprland-qt-support": "hyprland-qt-support",
"hyprlang": [
"hyprlang"
],
"hyprutils": [
"hyprland-qtutils",
"hyprlang",
"hyprutils"
],
"nixpkgs": [
@@ -181,11 +189,11 @@
]
},
"locked": {
"lastModified": 1737811848,
"narHash": "sha256-WZ7LeiKHk5Y94MU5gHIWn0r8asWxYOvie4LqfCjVIZU=",
"lastModified": 1739048983,
"narHash": "sha256-REhTcXq4qs3B3cCDtLlYDz0GZvmsBSh947Ub6pQWGTQ=",
"owner": "hyprwm",
"repo": "hyprland-qtutils",
"rev": "9c0831ff98856c0f312fcb8b57553fbe3dd34d5b",
"rev": "3504a293c8f8db4127cb0f7cfc1a318ffb4316f8",
"type": "github"
},
"original": {
@@ -195,34 +203,6 @@
}
},
"hyprlang": {
"inputs": {
"hyprutils": "hyprutils",
"nixpkgs": [
"hyprland-qtutils",
"hyprland-qt-support",
"nixpkgs"
],
"systems": [
"hyprland-qtutils",
"hyprland-qt-support",
"systems"
]
},
"locked": {
"lastModified": 1737634606,
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "f41271d35cc0f370d300413d756c2677f386af9d",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"hyprlang_2": {
"inputs": {
"hyprutils": [
"hyprutils"
@@ -235,11 +215,11 @@
]
},
"locked": {
"lastModified": 1737634606,
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=",
"lastModified": 1741191527,
"narHash": "sha256-kM+11Nch47Xwfgtw2EpRitJuORy4miwoMuRi5tyMBDY=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "f41271d35cc0f370d300413d756c2677f386af9d",
"rev": "72df3861f1197e41b078faa3e38eedd60e00018d",
"type": "github"
},
"original": {
@@ -249,35 +229,6 @@
}
},
"hyprutils": {
"inputs": {
"nixpkgs": [
"hyprland-qtutils",
"hyprland-qt-support",
"hyprlang",
"nixpkgs"
],
"systems": [
"hyprland-qtutils",
"hyprland-qt-support",
"hyprlang",
"systems"
]
},
"locked": {
"lastModified": 1737632363,
"narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "006620eb29d54ea9086538891404c78563d1bae1",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprutils",
"type": "github"
}
},
"hyprutils_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@@ -287,11 +238,11 @@
]
},
"locked": {
"lastModified": 1737725508,
"narHash": "sha256-jGmcPc6y/prg/4A8KGYqJ27nSPaProCMiFadaxNAKvA=",
"lastModified": 1742984269,
"narHash": "sha256-uz9FaCIbga/gQ5ZG1Hb4HVVjTWT1qjjCAFlCXiaefxg=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "fb0c2d1de3d1ef7396d19c18ac09e12bd956929e",
"rev": "7248194a2ce0106ae647b70d0526a96dc9d6ad60",
"type": "github"
},
"original": {
@@ -310,11 +261,11 @@
]
},
"locked": {
"lastModified": 1735493474,
"narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=",
"lastModified": 1739870480,
"narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b",
"rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
"type": "github"
},
"original": {
@@ -325,11 +276,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1737632463,
"narHash": "sha256-38J9QfeGSej341ouwzqf77WIHAScihAKCt8PQJ+NH28=",
"lastModified": 1742889210,
"narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0aa475546ed21629c4f5bbf90e38c846a99ec9e9",
"rev": "698214a32beb4f4c8e3942372c694f40848b360d",
"type": "github"
},
"original": {
@@ -348,11 +299,11 @@
]
},
"locked": {
"lastModified": 1737465171,
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
@@ -368,8 +319,8 @@
"hyprgraphics": "hyprgraphics",
"hyprland-protocols": "hyprland-protocols",
"hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang_2",
"hyprutils": "hyprutils_2",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
@@ -414,11 +365,11 @@
]
},
"locked": {
"lastModified": 1737634991,
"narHash": "sha256-dBAnb7Kbnier30cA7AgxVSxxARmxKZ1vHZT33THSIr8=",
"lastModified": 1741934139,
"narHash": "sha256-ZhTcTH9FoeAtbPfWGrhkH7RjLJZ7GeF18nygLAMR+WE=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "e09dfe2726c8008f983e45a0aa1a3b7416aaeb8a",
"rev": "150b0b6f52bb422a1b232a53698606fe0320dde0",
"type": "github"
},
"original": {

View File

@@ -39,7 +39,7 @@
url = "github:hyprwm/hyprland-qtutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprutils.follows = "hyprutils";
inputs.hyprlang.follows = "hyprlang";
};
hyprlang = {
@@ -157,5 +157,11 @@
nixosModules.default = import ./nix/module.nix inputs;
homeManagerModules.default = import ./nix/hm-module.nix self;
# Hydra build jobs
# Recent versions of Hydra can aggregate jobsets from 'hydraJobs' intead of a release.nix
# or similar. Remember to filter large or incompatible attributes here. More eval jobs can
# be added by merging, e.g., self.packages // self.devShells.
hydraJobs = self.packages;
};
}

View File

@@ -22,6 +22,7 @@ commands:
getoption <option> Gets the config option status (values)
globalshortcuts Lists all global shortcuts
hyprpaper ... Issue a hyprpaper request
hyprsunset ... Issue a hyprsunset request
instances Lists all running instances of Hyprland with
their info
keyword <name> <value> Issue a keyword to call a config keyword
@@ -81,6 +82,16 @@ requests:
flags:
See 'hyprctl --help')#";
const std::string_view HYPRSUNSET_HELP = R"#(usage: hyprctl [flags] hyprsunset <request>
requests:
temperature <temp> Enable blue-light filter
identity Disable blue-light filter
gamma <gamma> Enable gamma filter
flags:
See 'hyprctl --help')#";
const std::string_view NOTIFY_HELP = R"#(usage: hyprctl [flags] notify <icon> <time_ms> <color> <message...>
icon:

View File

@@ -12,22 +12,17 @@
#include <sys/un.h>
#include <pwd.h>
#include <unistd.h>
#include <ranges>
#include <algorithm>
#include <csignal>
#include <format>
#include <iostream>
#include <string>
#include <print>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
#include <cstdarg>
#include <sys/socket.h>
#include <hyprutils/string/String.hpp>
#include <cstring>
using namespace Hyprutils::String;
#include "Strings.hpp"
@@ -154,8 +149,16 @@ int rollingRead(const int socket) {
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
if (SERVERSOCKET < 0) {
log("Couldn't open a socket (1)");
return 1;
}
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
if (setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)) < 0) {
log("Couldn't set socket timeout (2)");
return 2;
}
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
@@ -164,14 +167,9 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
return -1;
}
if (SERVERSOCKET < 0) {
log("Couldn't open a socket (1)");
return 1;
}
if (instanceSignature.empty()) {
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
return 2;
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)");
return 3;
}
const std::string USERID = std::to_string(getUID());
@@ -184,39 +182,40 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
log("Couldn't connect to " + socketPath + ". (3)");
return 3;
log("Couldn't connect to " + socketPath + ". (4)");
return 4;
}
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
if (sizeWritten < 0) {
log("Couldn't write (4)");
return 4;
log("Couldn't write (5)");
return 5;
}
if (needRoll)
return rollingRead(SERVERSOCKET);
std::string reply = "";
char buffer[8192] = {0};
std::string reply = "";
constexpr size_t BUFFER_SIZE = 8192;
char buffer[BUFFER_SIZE] = {0};
sizeWritten = read(SERVERSOCKET, buffer, 8192);
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) {
if (errno == EWOULDBLOCK)
log("Hyprland IPC didn't respond in time\n");
log("Couldn't read (5)");
return 5;
log("Couldn't read (6)");
return 6;
}
reply += std::string(buffer, sizeWritten);
while (sizeWritten == 8192) {
sizeWritten = read(SERVERSOCKET, buffer, 8192);
while (sizeWritten == BUFFER_SIZE) {
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) {
log("Couldn't read (5)");
return 5;
log("Couldn't read (6)");
return 6;
}
reply += std::string(buffer, sizeWritten);
}
@@ -228,7 +227,7 @@ int request(std::string arg, int minArgs = 0, bool needRoll = false) {
return 0;
}
int requestHyprpaper(std::string arg) {
int requestIPC(std::string filename, std::string arg) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
if (SERVERSOCKET < 0) {
@@ -246,7 +245,7 @@ int requestHyprpaper(std::string arg) {
const std::string USERID = std::to_string(getUID());
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.hyprpaper.sock";
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename;
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
@@ -264,10 +263,10 @@ int requestHyprpaper(std::string arg) {
log("Couldn't write (4)");
return 4;
}
constexpr size_t BUFFER_SIZE = 8192;
char buffer[BUFFER_SIZE] = {0};
char buffer[8192] = {0};
sizeWritten = read(SERVERSOCKET, buffer, 8192);
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) {
log("Couldn't read (5)");
@@ -281,6 +280,14 @@ int requestHyprpaper(std::string arg) {
return 0;
}
int requestHyprpaper(std::string arg) {
return requestIPC(".hyprpaper.sock", arg);
}
int requestHyprsunset(std::string arg) {
return requestIPC(".hyprsunset.sock", arg);
}
void batchRequest(std::string arg, bool json) {
std::string commands = arg.substr(arg.find_first_of(' ') + 1);
@@ -385,6 +392,8 @@ int main(int argc, char** argv) {
if (cmd == "hyprpaper") {
std::println("{}", HYPRPAPER_HELP);
} else if (cmd == "hyprsunset") {
std::println("{}", HYPRSUNSET_HELP);
} else if (cmd == "notify") {
std::println("{}", NOTIFY_HELP);
} else if (cmd == "output") {
@@ -466,6 +475,8 @@ int main(int argc, char** argv) {
batchRequest(fullRequest, json);
else if (fullRequest.contains("/hyprpaper"))
exitStatus = requestHyprpaper(fullRequest);
else if (fullRequest.contains("/hyprsunset"))
exitStatus = requestHyprsunset(fullRequest);
else if (fullRequest.contains("/switchxkblayout"))
exitStatus = request(fullRequest, 2);
else if (fullRequest.contains("/seterror"))

View File

@@ -463,7 +463,9 @@ bool CPluginManager::updateHeaders(bool force) {
return false;
}
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning https://github.com/hyprwm/Hyprland, this might take a moment."));
const auto& HL_URL = m_szCustomHlUrl.empty() ? "https://github.com/hyprwm/Hyprland" : m_szCustomHlUrl;
progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning {}, this might take a moment.", HL_URL));
const bool bShallow = (HLVER.branch == "main") && !m_bNoShallow;
@@ -474,12 +476,12 @@ bool CPluginManager::updateHeaders(bool force) {
if (m_bVerbose && bShallow)
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
std::string ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-{}{}", getTempRoot(), USERNAME,
(bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
std::string ret =
execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
if (!std::filesystem::exists(WORKINGDIR)) {
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
ret = execAndGet(std::format("cd {} && git clone --recursive https://github.com/hyprwm/hyprland hyprland-{}", getTempRoot(), USERNAME));
ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME));
}
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {

View File

@@ -62,6 +62,7 @@ class CPluginManager {
bool m_bVerbose = false;
bool m_bNoShallow = false;
std::string m_szCustomHlUrl;
// will delete recursively if exists!!
bool createSafeDirectory(const std::string& path);

View File

@@ -29,6 +29,7 @@ constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
--verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f)
--no-shallow | -s Disable shallow cloning of Hyprland sources
--hl-url | Pass a custom hyprland source url
)#";
@@ -45,6 +46,7 @@ int main(int argc, char** argv, char** envp) {
std::vector<std::string> command;
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
std::string customHlUrl;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
@@ -59,6 +61,13 @@ int main(int argc, char** argv, char** envp) {
verbose = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
noShallow = true;
} else if (ARGS[i] == "--hl-url") {
if (i + 1 >= argc) {
std::println(stderr, "Missing argument for --hl-url");
return 1;
}
customHlUrl = ARGS[i + 1];
i++;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
@@ -66,9 +75,8 @@ int main(int argc, char** argv, char** envp) {
std::println(stderr, "Unrecognized option {}", ARGS[i]);
return 1;
}
} else {
} else
command.push_back(ARGS[i]);
}
}
if (command.empty()) {
@@ -76,9 +84,10 @@ int main(int argc, char** argv, char** envp) {
return 1;
}
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager->m_bNoShallow = noShallow;
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager->m_bNoShallow = noShallow;
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
if (command[0] == "add") {
if (command.size() < 2) {
@@ -156,15 +165,18 @@ int main(int argc, char** argv, char** envp) {
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK && notify) {
switch (ret) {
case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
case LOADSTATE_HEADERS_OUTDATED:
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
break;
default: break;
if (ret != LOADSTATE_OK) {
if (notify) {
switch (ret) {
case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
case LOADSTATE_HEADERS_OUTDATED:
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
break;
default: break;
}
}
return 1;
} else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");

View File

@@ -31,12 +31,16 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
aquamarine = dependency('aquamarine', version: '>=0.4.5')
aquamarine = dependency('aquamarine', version: '>=0.8.0')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
aquamarine_version_list = aquamarine.version().split('.')
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
add_project_arguments(['-DAQUAMARINE_VERSION_MINOR=@0@'.format(aquamarine_version_list.get(1))], language: 'cpp')
add_project_arguments(['-DAQUAMARINE_VERSION_PATCH=@0@'.format(aquamarine_version_list.get(2))], language: 'cpp')
add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp')
add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp')
add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp')
@@ -58,6 +62,7 @@ endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
inotify_dep = dependency('libinotify', required: false) # inotify on BSDs
re2 = dependency('re2', required: true)
@@ -84,7 +89,7 @@ endif
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
# Install headers
globber = run_command('find', 'src', '-name', '*.h*', check: true)
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.frag', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)

View File

@@ -26,7 +26,7 @@
libinput,
libxkbcommon,
libuuid,
mesa,
libgbm,
pango,
pciutils,
re2,
@@ -130,7 +130,7 @@ in
libinput
libuuid
libxkbcommon
mesa
libgbm
pango
pciutils
re2
@@ -155,7 +155,7 @@ in
mesonBuildType =
if debug
then "debugoptimized"
then "debug"
else "release";
mesonFlags = flatten [

201
nix/lib.nix Normal file
View File

@@ -0,0 +1,201 @@
lib: let
inherit (lib)
attrNames
filterAttrs
foldl
generators
partition
;
inherit (lib.strings)
concatMapStrings
hasPrefix
;
/**
Convert a structured Nix attribute set into Hyprland's configuration format.
This function takes a nested attribute set and converts it into Hyprland-compatible
configuration syntax, supporting top, bottom, and regular command sections.
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
`key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format.
Configuration:
* `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `["$"]`).
* `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`).
Attention:
- The function ensures top commands appear **first** and bottom commands **last**.
- The generated configuration is a **single string**, suitable for writing to a config file.
- Lists are converted into multiple entries, ensuring compatibility with Hyprland.
# Inputs
Structured function argument:
: topCommandsPrefixes (optional, default: `["$"]`)
: A list of prefixes that define **top** commands. Any key starting with one of these
prefixes will be placed at the beginning of the configuration.
: bottomCommandsPrefixes (optional, default: `[]`)
: A list of prefixes that define **bottom** commands. Any key starting with one of these
prefixes will be placed at the end of the configuration.
Value:
: The attribute set to be converted to Hyprland configuration format.
# Type
```
toHyprlang :: AttrSet -> AttrSet -> String
```
# Examples
:::{.example}
```nix
let
config = {
"$mod" = "SUPER";
monitor = {
"HDMI-A-1" = "1920x1080@60,0x0,1";
};
exec = [
"waybar"
"dunst"
];
};
in lib.toHyprlang {} config
```
**Output:**
```nix
"$mod = SUPER"
"monitor:HDMI-A-1 = 1920x1080@60,0x0,1"
"exec = waybar"
"exec = dunst"
```
:::
*/
toHyprlang = {
topCommandsPrefixes ? ["$"],
bottomCommandsPrefixes ? [],
}: attrs: let
toHyprlang' = attrs: let
# Specially configured `toKeyValue` generator with support for duplicate keys
# and a legible key-value separator.
mkCommands = generators.toKeyValue {
mkKeyValue = generators.mkKeyValueDefault {} " = ";
listsAsDuplicateKeys = true;
indent = ""; # No indent, since we don't have nesting
};
# Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`.
# Uses `flattenAttrs` with a colon separator.
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
# General filtering function to check if a key starts with any prefix in a given list.
filterCommands = list: n:
foldl (acc: prefix: acc || hasPrefix prefix n) false list;
# Partition keys into top commands and the rest
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
remainingCommands = removeAttrs commands result.right;
# Partition remaining commands into bottom commands and regular commands
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
regularCommands = removeAttrs remainingCommands result2.right;
in
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
concatMapStrings mkCommands [
topCommands
regularCommands
bottomCommands
];
in
toHyprlang' attrs;
/**
Flatten a nested attribute set into a flat attribute set, using a custom key separator function.
This function recursively traverses a nested attribute set and produces a flat attribute set
where keys are joined using a user-defined function (`pred`). It allows transforming deeply
nested structures into a single-level attribute set while preserving key-value relationships.
Configuration:
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
# Inputs
Structured function argument:
: pred (required)
: A function that determines how parent and child keys should be combined into a single key.
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
Value:
: The nested attribute set to be flattened.
# Type
```
flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet
```
# Examples
:::{.example}
```nix
let
nested = {
a = "3";
b = { c = "4"; d = "5"; };
};
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
in lib.flattenAttrs separator nested
```
**Output:**
```nix
{
"a" = "3";
"b.c" = "4";
"b.d" = "5";
}
```
:::
*/
flattenAttrs = pred: attrs: let
flattenAttrs' = prefix: attrs:
builtins.foldl' (
acc: key: let
value = attrs.${key};
newKey =
if prefix == ""
then key
else pred prefix key;
in
acc
// (
if builtins.isAttrs value
then flattenAttrs' newKey value
else {"${newKey}" = value;}
)
) {} (builtins.attrNames attrs);
in
flattenAttrs' "" attrs;
in
{
inherit flattenAttrs toHyprlang;
}

View File

@@ -5,72 +5,8 @@ inputs: {
...
}: let
inherit (pkgs.stdenv.hostPlatform) system;
selflib = import ./lib.nix lib;
cfg = config.programs.hyprland;
# basically 1:1 taken from https://github.com/nix-community/home-manager/blob/master/modules/services/window-managers/hyprland.nix
toHyprconf = {
attrs,
indentLevel ? 0,
importantPrefixes ? ["$"],
}: let
inherit
(lib)
all
concatMapStringsSep
concatStrings
concatStringsSep
filterAttrs
foldl
generators
hasPrefix
isAttrs
isList
mapAttrsToList
replicate
;
initialIndent = concatStrings (replicate indentLevel " ");
toHyprconf' = indent: attrs: let
sections =
filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs;
mkSection = n: attrs:
if lib.isList attrs
then (concatMapStringsSep "\n" (a: mkSection n a) attrs)
else ''
${indent}${n} {
${toHyprconf' " ${indent}" attrs}${indent}}
'';
mkFields = generators.toKeyValue {
listsAsDuplicateKeys = true;
inherit indent;
};
allFields =
filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v)))
attrs;
isImportantField = n: _:
foldl (acc: prev:
if hasPrefix prev n
then true
else acc)
false
importantPrefixes;
importantFields = filterAttrs isImportantField allFields;
fields =
builtins.removeAttrs allFields
(mapAttrsToList (n: _: n) importantFields);
in
mkFields importantFields
+ concatStringsSep "\n" (mapAttrsToList mkSection sections)
+ mkFields fields;
in
toHyprconf' initialIndent attrs;
in {
options = {
programs.hyprland = {
@@ -106,6 +42,9 @@ in {
should be written as lists. Variables' and colors' names should be
quoted. See <https://wiki.hyprland.org> for more examples.
Special categories (e.g `devices`) should be written as
`"devices[device-name]"`.
::: {.note}
Use the [](#programs.hyprland.plugins) option to
declare plugins.
@@ -151,20 +90,21 @@ in {
'';
};
sourceFirst =
lib.mkEnableOption ''
putting source entries at the top of the configuration
''
// {
default = true;
};
importantPrefixes = lib.mkOption {
topPrefixes = lib.mkOption {
type = with lib.types; listOf str;
default = ["$" "bezier" "name"] ++ lib.optionals cfg.sourceFirst ["source"];
example = ["$" "bezier"];
default = ["$" "bezier"];
example = ["$" "bezier" "source"];
description = ''
List of prefix of attributes to source at the top of the config.
List of prefix of attributes to put at the top of the config.
'';
};
bottomPrefixes = lib.mkOption {
type = with lib.types; listOf str;
default = [];
example = ["source"];
description = ''
List of prefix of attributes to put at the bottom of the config.
'';
};
};
@@ -173,38 +113,38 @@ in {
{
programs.hyprland = {
package = lib.mkDefault inputs.self.packages.${system}.hyprland;
portalPackage = lib.mkDefault (inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
hyprland = cfg.finalPackage;
});
portalPackage = lib.mkDefault inputs.self.packages.${system}.xdg-desktop-portal-hyprland;
};
}
(lib.mkIf cfg.enable {
environment.etc."xdg/hypr/hyprland.conf" = let
shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != [];
pluginsToHyprconf = plugins:
toHyprconf {
attrs = {
plugin = let
mkEntry = entry:
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in
map mkEntry cfg.plugins;
};
inherit (cfg) importantPrefixes;
pluginsToHyprlang = plugins:
selflib.toHyprlang {
topCommandsPrefixes = cfg.topPrefixes;
bottomCommandsPrefixes = cfg.bottomPrefixes;
}
{
plugin = let
mkEntry = entry:
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in
map mkEntry cfg.plugins;
};
in
lib.mkIf shouldGenerate {
text =
lib.optionalString (cfg.plugins != [])
(pluginsToHyprconf cfg.plugins)
(pluginsToHyprlang cfg.plugins)
+ lib.optionalString (cfg.settings != {})
(toHyprconf {
attrs = cfg.settings;
inherit (cfg) importantPrefixes;
})
(selflib.toHyprlang {
topCommandsPrefixes = cfg.topPrefixes;
bottomCommandsPrefixes = cfg.bottomPrefixes;
}
cfg.settings)
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
};
})

View File

@@ -1,13 +1,13 @@
wayland_protos = dependency(
'wayland-protocols',
version: '>=1.32',
version: '>=1.41',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)
hyprland_protos = dependency(
'hyprland-protocols',
version: '>=0.6',
version: '>=0.6.2',
fallback: 'hyprland-protocols',
)
@@ -70,6 +70,8 @@ protocols = [
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
wayland_protocol_dir / 'staging/content-type/content-type-v1.xml',
wayland_protocol_dir / 'staging/color-management/color-management-v1.xml',
]
wl_protocols = []

View File

@@ -1,4 +1,13 @@
#!/bin/sh
# if the git directory doesn't exist, don't gather data to avoid overwriting, unless
# the version file is missing altogether (otherwise compiling will fail)
if [ ! -d ./.git ]; then
if [ -f ./src/version.h ]; then
exit 0
fi
fi
cp -fr ./src/version.h.in ./src/version.h
HASH=${HASH-$(git rev-parse HEAD)}

View File

@@ -1,3 +1,4 @@
#include <ranges>
#include <re2/re2.h>
#include "Compositor.hpp"
@@ -12,6 +13,7 @@
#include "managers/SeatManager.hpp"
#include "managers/VersionKeeperManager.hpp"
#include "managers/DonationNagManager.hpp"
#include "managers/ANRManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include <algorithm>
#include <aquamarine/output/Output.hpp>
@@ -21,15 +23,12 @@
#include <print>
#include <cstring>
#include <filesystem>
#include <ranges>
#include <print>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp"
#ifdef USES_SYSTEMD
#include <helpers/SdDaemon.hpp> // for SdNotify
#endif
#include "helpers/varlist/VarList.hpp"
#include "helpers/fs/FsUtils.hpp"
#include "protocols/FractionalScale.hpp"
#include "protocols/PointerConstraints.hpp"
@@ -71,9 +70,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <malloc.h>
#include <unistd.h>
using namespace Hyprutils::String;
using namespace Aquamarine;
using enum NContentType::eContentType;
using namespace NColorManagement;
static int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
@@ -163,10 +166,22 @@ void CCompositor::restoreNofile() {
Debug::log(ERR, "Failed restoring NOFILE limits");
}
void CCompositor::setMallocThreshold() {
#ifdef M_TRIM_THRESHOLD
// The default is 128 pages,
// which is very large and can lead to a lot of memory used for no reason
// because trimming hasn't happened
static const int PAGESIZE = sysconf(_SC_PAGESIZE);
mallopt(M_TRIM_THRESHOLD, 6 * PAGESIZE);
#endif
}
CCompositor::CCompositor(bool onlyConfig) : m_bOnlyConfigVerification(onlyConfig), m_iHyprlandPID(getpid()) {
if (onlyConfig)
return;
setMallocThreshold();
m_szHyprTempDataRoot = std::string{getenv("XDG_RUNTIME_DIR")} + "/hypr";
if (m_szHyprTempDataRoot.starts_with("/hypr")) {
@@ -265,7 +280,6 @@ static bool filterGlobals(const wl_client* client, const wl_global* global, void
//
void CCompositor::initServer(std::string socketName, int socketFd) {
if (m_bOnlyConfigVerification) {
g_pHookSystem = makeUnique<CHookSystemManager>();
g_pKeybindManager = makeUnique<CKeybindManager>();
@@ -374,7 +388,6 @@ void CCompositor::initServer(std::string socketName, int socketFd) {
}
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
if (!getenv("XDG_CURRENT_DESKTOP")) {
setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1);
m_bDesktopEnvSet = true;
@@ -501,7 +514,7 @@ void CCompositor::cleanEnvironment() {
"dbus-update-activation-environment 2>/dev/null && "
#endif
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS";
g_pKeybindManager->spawn(CMD);
CKeybindManager::spawn(CMD);
}
}
@@ -581,6 +594,7 @@ void CCompositor::cleanup() {
g_pEventLoopManager.reset();
g_pVersionKeeperMgr.reset();
g_pDonationNagManager.reset();
g_pANRManager.reset();
g_pConfigWatcher.reset();
if (m_pAqBackend)
@@ -683,6 +697,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the DonationNag!");
g_pDonationNagManager = makeUnique<CDonationNagManager>();
Debug::log(LOG, "Creating the ANRManager!");
g_pANRManager = makeUnique<CANRManager>();
Debug::log(LOG, "Starting XWayland");
g_pXWayland = makeUnique<CXWayland>(g_pCompositor->m_bWantsXwayland);
} break;
@@ -739,7 +756,7 @@ void CCompositor::startCompositor() {
"dbus-update-activation-environment 2>/dev/null && "
#endif
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS";
g_pKeybindManager->spawn(CMD);
CKeybindManager::spawn(CMD);
}
Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket);
@@ -798,6 +815,11 @@ PHLMONITOR CCompositor::getMonitorFromCursor() {
}
PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) {
if (m_vMonitors.empty()) {
Debug::log(WARN, "getMonitorFromVector called with empty monitor list");
return nullptr;
}
PHLMONITOR mon;
for (auto const& m : m_vMonitors) {
if (CBox{m->vecPosition, m->vecSize}.containsPoint(point)) {
@@ -840,12 +862,7 @@ void CCompositor::removeWindowFromVectorSafe(PHLWINDOW pWindow) {
}
bool CCompositor::monitorExists(PHLMONITOR pMonitor) {
for (auto const& m : m_vRealMonitors) {
if (m == pMonitor)
return true;
}
return false;
return std::ranges::any_of(m_vRealMonitors, [&](const PHLMONITOR& m) { return m == pMonitor; });
}
PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, PHLWINDOW pIgnoreWindow) {
@@ -1065,7 +1082,7 @@ PHLMONITOR CCompositor::getRealMonitorFromOutput(SP<Aquamarine::IOutput> out) {
return nullptr;
}
void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface) {
void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, bool preserveFocusHistory) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PSPECIALFALLTHROUGH = CConfigValue<Hyprlang::INT>("input:special_fallthrough");
@@ -1176,8 +1193,8 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
pWindow->m_bIsUrgent = false;
// Send an event
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", pWindow->m_szClass + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)pWindow.get())});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = pWindow->m_szClass + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", (uintptr_t)pWindow.get())});
EMIT_HOOK_EVENT("activeWindow", pWindow);
@@ -1185,16 +1202,20 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
g_pInputManager->recheckIdleInhibitorStatus();
// move to front of the window history
const auto HISTORYPIVOT = std::find_if(m_vWindowFocusHistory.begin(), m_vWindowFocusHistory.end(), [&](const auto& other) { return other.lock() == pWindow; });
if (HISTORYPIVOT == m_vWindowFocusHistory.end()) {
Debug::log(ERR, "BUG THIS: {} has no pivot in history", pWindow);
} else {
std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1);
if (!preserveFocusHistory) {
// move to front of the window history
const auto HISTORYPIVOT = std::ranges::find_if(m_vWindowFocusHistory, [&](const auto& other) { return other.lock() == pWindow; });
if (HISTORYPIVOT == m_vWindowFocusHistory.end())
Debug::log(ERR, "BUG THIS: {} has no pivot in history", pWindow);
else
std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1);
}
if (*PFOLLOWMOUSE == 0)
g_pInputManager->sendMotionEventsToFocused();
if (pWindow->m_sGroupData.pNextWindow)
pWindow->deactivateGroupMembers();
}
void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindowOwner) {
@@ -1218,8 +1239,8 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
if (!pSurface) {
g_pSeatManager->setKeyboardFocus(nullptr);
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = ","});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = ""});
EMIT_HOOK_EVENT("keyboardFocus", (SP<CWLSurfaceResource>)nullptr);
m_pLastFocus.reset();
return;
@@ -1251,7 +1272,7 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
SP<CWLSurfaceResource> CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, PHLMONITOR monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) {
for (auto const& lsl : monitor->m_aLayerSurfaceLayers | std::views::reverse) {
for (auto const& ls : lsl | std::views::reverse) {
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha->value() == 0.f)
if (!ls->mapped || ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha->value() == 0.f)
continue;
auto SURFACEAT = ls->popupHead->at(pos, true);
@@ -1269,7 +1290,7 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerPopupSurface(const Vector2D& po
SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<PHLLSREF>* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) {
for (auto const& ls : *layerSurfaces | std::views::reverse) {
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha->value() == 0.f)
if (!ls->mapped || ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha->value() == 0.f)
continue;
auto [surf, local] = ls->layerSurface->surface->at(pos - ls->geometry.pos(), true);
@@ -1396,14 +1417,13 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) {
toMove.insert(toMove.begin(), pw);
for (auto const& w : m_vWindows) {
if (w->m_bIsMapped && !w->isHidden() && w->m_bIsX11 && w->x11TransientFor() == pw && w != pw && std::find(toMove.begin(), toMove.end(), w) == toMove.end()) {
if (w->m_bIsMapped && !w->isHidden() && w->m_bIsX11 && w->x11TransientFor() == pw && w != pw && std::ranges::find(toMove, w) == toMove.end()) {
x11Stack(w, top, x11Stack);
}
}
};
x11Stack(pWindow, top, x11Stack);
for (const auto& it : toMove) {
moveToZ(it, top);
}
@@ -1455,7 +1475,7 @@ void CCompositor::cleanupFadingOut(const MONITORID& monid) {
if (ls->fadingOut && ls->readyToDelete && ls->isFadedOut()) {
for (auto const& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
if (!lsl.empty() && std::find_if(lsl.begin(), lsl.end(), [&](auto& other) { return other == ls; }) != lsl.end()) {
if (!lsl.empty() && std::ranges::find_if(lsl, [&](auto& other) { return other == ls; }) != lsl.end()) {
std::erase_if(lsl, [&](auto& other) { return other == ls || !other; });
}
}
@@ -1478,7 +1498,7 @@ void CCompositor::cleanupFadingOut(const MONITORID& monid) {
}
void CCompositor::addToFadingOutSafe(PHLLS pLS) {
const auto FOUND = std::find_if(m_vSurfacesFadingOut.begin(), m_vSurfacesFadingOut.end(), [&](auto& other) { return other.lock() == pLS; });
const auto FOUND = std::ranges::find_if(m_vSurfacesFadingOut, [&](auto& other) { return other.lock() == pLS; });
if (FOUND != m_vSurfacesFadingOut.end())
return; // if it's already added, don't add it.
@@ -1491,7 +1511,7 @@ void CCompositor::removeFromFadingOutSafe(PHLLS ls) {
}
void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) {
const auto FOUND = std::find_if(m_vWindowsFadingOut.begin(), m_vWindowsFadingOut.end(), [&](PHLWINDOWREF& other) { return other.lock() == pWindow; });
const auto FOUND = std::ranges::find_if(m_vWindowsFadingOut, [&](PHLWINDOWREF& other) { return other.lock() == pWindow; });
if (FOUND != m_vWindowsFadingOut.end())
return; // if it's already added, don't add it.
@@ -1610,7 +1630,7 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
//
auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double {
double dot = a.x * b.x + a.y * b.y;
double dot = (a.x * b.x) + (a.y * b.y);
double ang = std::acos(dot / (a.size() * b.size()));
return ang;
};
@@ -1654,37 +1674,52 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
return nullptr;
}
PHLWINDOW CCompositor::getNextWindowOnWorkspace(PHLWINDOW pWindow, bool focusableOnly, std::optional<bool> floating, bool visible) {
auto it = std::ranges::find(m_vWindows, pWindow);
const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(pWindow, w, focusableOnly, floating, visible); };
const auto IN_RIGHT = std::find_if(it, m_vWindows.end(), FINDER);
if (IN_RIGHT != m_vWindows.end())
return *IN_RIGHT;
const auto IN_LEFT = std::find_if(m_vWindows.begin(), it, FINDER);
return *IN_LEFT;
}
PHLWINDOW CCompositor::getPrevWindowOnWorkspace(PHLWINDOW pWindow, bool focusableOnly, std::optional<bool> floating, bool visible) {
auto it = std::ranges::find(std::ranges::reverse_view(m_vWindows), pWindow);
const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(pWindow, w, focusableOnly, floating, visible); };
const auto IN_LEFT = std::find_if(it, m_vWindows.rend(), FINDER);
if (IN_LEFT != m_vWindows.rend())
return *IN_LEFT;
const auto IN_RIGHT = std::find_if(m_vWindows.rbegin(), it, FINDER);
return *IN_RIGHT;
}
inline static bool isWorkspaceMatches(PHLWINDOW pWindow, const PHLWINDOW w, bool anyWorkspace) {
template <typename WINDOWPTR>
static bool isWorkspaceMatches(WINDOWPTR pWindow, const WINDOWPTR w, bool anyWorkspace) {
return anyWorkspace ? w->m_pWorkspace && w->m_pWorkspace->isVisible() : w->m_pWorkspace == pWindow->m_pWorkspace;
}
inline static bool isFloatingMatches(PHLWINDOW w, std::optional<bool> floating) {
template <typename WINDOWPTR>
static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
return !floating.has_value() || w->m_bIsFloating == floating.value();
};
}
bool CCompositor::isWindowAvailableForCycle(PHLWINDOW pWindow, const PHLWINDOW w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace) {
return isFloatingMatches(w, floating) && w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_bIsMapped && !w->isHidden() &&
(!focusableOnly || !w->m_sWindowData.noFocus.valueOrDefault());
template <typename WINDOWPTR>
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false) {
return isFloatingMatches(w, floating) &&
(w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sWindowData.noFocus.valueOrDefault()));
}
template <typename Iterator>
static PHLWINDOW getWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOW&)> PRED) {
const auto IN_ONE_SIDE = std::find_if(cur, end, PRED);
if (IN_ONE_SIDE != end)
return *IN_ONE_SIDE;
const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED);
return *IN_OTHER_SIDE;
}
template <typename Iterator>
static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOWREF&)> PRED) {
const auto IN_ONE_SIDE = std::find_if(cur, end, PRED);
if (IN_ONE_SIDE != end)
return IN_ONE_SIDE->lock();
const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED);
return IN_OTHER_SIDE->lock();
}
PHLWINDOW CCompositor::getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly, std::optional<bool> floating, bool visible, bool next) {
const auto FINDER = [&](const PHLWINDOWREF& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible); };
// also m_vWindowFocusHistory has reverse order, so when it is next - we need to reverse again
return next ?
getWeakWindowPred(std::ranges::find(std::ranges::reverse_view(m_vWindowFocusHistory), cur), m_vWindowFocusHistory.rend(), m_vWindowFocusHistory.rbegin(), FINDER) :
getWeakWindowPred(std::ranges::find(m_vWindowFocusHistory, cur), m_vWindowFocusHistory.end(), m_vWindowFocusHistory.begin(), FINDER);
}
PHLWINDOW CCompositor::getWindowCycle(PHLWINDOW cur, bool focusableOnly, std::optional<bool> floating, bool visible, bool prev) {
const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible); };
return prev ? getWindowPred(std::ranges::find(std::ranges::reverse_view(m_vWindows), cur), m_vWindows.rend(), m_vWindows.rbegin(), FINDER) :
getWindowPred(std::ranges::find(m_vWindows, cur), m_vWindows.end(), m_vWindows.begin(), FINDER);
}
WORKSPACEID CCompositor::getNextAvailableNamedWorkspace() {
@@ -1729,7 +1764,7 @@ bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR
const auto XY1 = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
const auto XY2 = PMONITOR->vecPosition + PMONITOR->vecSize - PMONITOR->vecReservedBottomRight;
return !VECINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y);
return VECNOTINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y);
}
PHLMONITOR CCompositor::getMonitorInDirection(const char& dir) {
@@ -1903,7 +1938,7 @@ void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
MONITORID 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]; }))
if (m_mMonitorIDMap.contains(name) && !std::ranges::any_of(m_vRealMonitors, [&](auto m) { return m->ID == m_mMonitorIDMap[name]; }))
return m_mMonitorIDMap[name];
// otherwise, find minimum available ID that is not in the map
@@ -1913,7 +1948,7 @@ MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) {
}
MONITORID nextID = 0;
while (usedIDs.count(nextID) > 0) {
while (usedIDs.contains(nextID)) {
nextID++;
}
m_mMonitorIDMap[name] = nextID;
@@ -1921,7 +1956,6 @@ MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) {
}
void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitorB) {
const auto PWORKSPACEA = pMonitorA->activeWorkspace;
const auto PWORKSPACEB = pMonitorB->activeWorkspace;
@@ -1993,17 +2027,18 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
(g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING)));
const auto PNEWWORKSPACE = pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID ? PWORKSPACEB : PWORKSPACEA;
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", PNEWWORKSPACE->m_szName});
g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", PNEWWORKSPACE->m_iID, PNEWWORKSPACE->m_szName)});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspace", .data = PNEWWORKSPACE->m_szName});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspacev2", .data = std::format("{},{}", PNEWWORKSPACE->m_iID, PNEWWORKSPACE->m_szName)});
EMIT_HOOK_EVENT("workspace", PNEWWORKSPACE);
}
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEA->m_szName + "," + pMonitorB->szName});
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspacev2", std::format("{},{},{}", PWORKSPACEA->m_iID, PWORKSPACEA->m_szName, pMonitorB->szName)});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEA->m_szName + "," + pMonitorB->szName});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEA->m_iID, PWORKSPACEA->m_szName, pMonitorB->szName)});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<std::any>{PWORKSPACEA, pMonitorB}));
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEB->m_szName + "," + pMonitorA->szName});
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspacev2", std::format("{},{},{}", PWORKSPACEB->m_iID, PWORKSPACEB->m_szName, pMonitorA->szName)});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEB->m_szName + "," + pMonitorA->szName});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEB->m_iID, PWORKSPACEB->m_szName, pMonitorA->szName)});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<std::any>{PWORKSPACEB, pMonitorA}));
}
@@ -2082,7 +2117,6 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) {
}
void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMonitor, bool noWarpCursor) {
if (!pWorkspace || !pMonitor)
return;
@@ -2181,7 +2215,8 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
// finalize
if (POLDMON) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace);
if (valid(POLDMON->activeWorkspace))
updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace);
updateSuspendedStates();
}
@@ -2189,8 +2224,8 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
updateSuspendedStates();
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName});
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspacev2", std::format("{},{},{}", pWorkspace->m_iID, pWorkspace->m_szName, pMonitor->szName)});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = pWorkspace->m_szName + "," + pMonitor->szName});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", pWorkspace->m_iID, pWorkspace->m_szName, pMonitor->szName)});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<std::any>{pWorkspace, pMonitor}));
}
@@ -2201,12 +2236,8 @@ bool CCompositor::workspaceIDOutOfBounds(const WORKSPACEID& id) {
for (auto const& w : m_vWorkspaces) {
if (w->m_bIsSpecialWorkspace)
continue;
if (w->m_iID < lowestID)
lowestID = w->m_iID;
if (w->m_iID > highestID)
highestID = w->m_iID;
lowestID = std::min(w->m_iID, lowestID);
highestID = std::max(w->m_iID, highestID);
}
return std::clamp(id, lowestID, highestID) != id;
@@ -2214,6 +2245,9 @@ bool CCompositor::workspaceIDOutOfBounds(const WORKSPACEID& id) {
void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) {
if (!pWorkspace)
return;
const auto FULLSCREEN = pWorkspace->m_bHasFullscreenWindow;
for (auto const& w : g_pCompositor->m_vWindows) {
@@ -2282,7 +2316,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen())
setWindowFullscreenInternal(PWORKSPACE->getFullscreenWindow(), FSMODE_NONE);
const bool CHANGEINTERNAL = !(PWINDOW->m_bPinned || CURRENT_EFFECTIVE_MODE == EFFECTIVE_MODE);
const bool CHANGEINTERNAL = !PWINDOW->m_bPinned && CURRENT_EFFECTIVE_MODE != EFFECTIVE_MODE;
if (*PALLOWPINFULLSCREEN && PWINDOW->m_bPinFullscreened && PWINDOW->isFullscreen() && !PWINDOW->m_bPinned && state.internal == FSMODE_NONE) {
PWINDOW->m_bPinned = true;
@@ -2309,7 +2343,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
PWORKSPACE->m_efFullscreenMode = EFFECTIVE_MODE;
PWORKSPACE->m_bHasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE;
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)EFFECTIVE_MODE != FSMODE_NONE)});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string((int)EFFECTIVE_MODE != FSMODE_NONE)});
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
PWINDOW->updateDynamicRules();
@@ -2324,7 +2358,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
updateFullscreenFadeOnWorkspace(PWORKSPACE);
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true);
PWINDOW->sendWindowSize(true);
PWORKSPACE->forceReportSizesToWindows();
@@ -2336,7 +2370,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
// ignore if DS is disabled.
if (*PDIRECTSCANOUT)
if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME))
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
g_pConfigManager->ensureVRR(PMONITOR);
@@ -2576,8 +2610,8 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con
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;
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);
@@ -2593,7 +2627,13 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITO
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, getMonitorFromID(monID), NAME, SPECIAL, isEmpty));
const auto PMONITOR = getMonitorFromID(monID);
if (!PMONITOR) {
Debug::log(ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace");
return nullptr;
}
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty));
PWORKSPACE->m_fAlpha->setValueAndWarp(0);
@@ -2614,8 +2654,8 @@ void CCompositor::setActiveMonitor(PHLMONITOR pMonitor) {
const auto WORKSPACE_ID = PWORKSPACE ? std::to_string(PWORKSPACE->m_iID) : std::to_string(WORKSPACE_INVALID);
const auto WORKSPACE_NAME = PWORKSPACE ? PWORKSPACE->m_szName : "?";
g_pEventManager->postEvent(SHyprIPCEvent{"focusedmon", pMonitor->szName + "," + WORKSPACE_NAME});
g_pEventManager->postEvent(SHyprIPCEvent{"focusedmonv2", pMonitor->szName + "," + WORKSPACE_ID});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmon", .data = pMonitor->szName + "," + WORKSPACE_NAME});
g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmonv2", .data = pMonitor->szName + "," + WORKSPACE_ID});
EMIT_HOOK_EVENT("focusedMon", pMonitor);
m_pLastMonitor = pMonitor->self;
@@ -2671,6 +2711,9 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
if (pWindow->m_bPinned && pWorkspace->m_bIsSpecialWorkspace)
return;
if (pWindow->m_pWorkspace == pWorkspace)
return;
const bool FULLSCREEN = pWindow->isFullscreen();
const auto FULLSCREENMODE = pWindow->m_sFullscreenState.internal;
const bool WASVISIBLE = pWindow->m_pWorkspace && pWindow->m_pWorkspace->isVisible();
@@ -2805,14 +2848,10 @@ void CCompositor::arrangeMonitors() {
// Finds the max and min values of explicitely placed monitors.
for (auto const& m : arranged) {
if (m->vecPosition.x + m->vecSize.x > maxXOffsetRight)
maxXOffsetRight = m->vecPosition.x + m->vecSize.x;
if (m->vecPosition.x < maxXOffsetLeft)
maxXOffsetLeft = m->vecPosition.x;
if (m->vecPosition.y + m->vecSize.y > maxYOffsetDown)
maxYOffsetDown = m->vecPosition.y + m->vecSize.y;
if (m->vecPosition.y < maxYOffsetUp)
maxYOffsetUp = m->vecPosition.y;
maxXOffsetRight = std::max<double>(m->vecPosition.x + m->vecSize.x, maxXOffsetRight);
maxXOffsetLeft = std::min<double>(m->vecPosition.x, maxXOffsetLeft);
maxYOffsetDown = std::max<double>(m->vecPosition.y + m->vecSize.y, maxYOffsetDown);
maxYOffsetUp = std::min<double>(m->vecPosition.y, maxYOffsetUp);
}
};
@@ -3006,8 +3045,10 @@ void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
g_pHyprRenderer->damageMonitor(PNEWMONITOR);
PNEWMONITOR->onMonitorFrame();
if (PROTO::colorManagement && shouldChangePreferredImageDescription())
PROTO::colorManagement->onImagePreferredChanged();
if (PROTO::colorManagement && shouldChangePreferredImageDescription()) {
Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id");
PROTO::colorManagement->onImagePreferredChanged(0);
}
}
SImageDescription CCompositor::getPreferredImageDescription() {
@@ -3025,34 +3066,58 @@ bool CCompositor::shouldChangePreferredImageDescription() {
return false;
}
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules) {
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
if (!m_pLastMonitor)
return;
for (const auto& rule : rules) {
if (!rule.isPersistent)
continue;
PHLWORKSPACE PWORKSPACE = nullptr;
if (pWorkspace) {
if (pWorkspace->matchesStaticSelector(rule.workspaceString))
PWORKSPACE = pWorkspace;
else
continue;
}
const auto PMONITOR = getMonitorFromString(rule.monitor);
if (!rule.monitor.empty() && !PMONITOR)
continue; // don't do anything yet, as the monitor is not yet present.
if (!PWORKSPACE) {
WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName;
if (id == WORKSPACE_INVALID) {
const auto R = getWorkspaceIDNameFromString(rule.workspaceString);
id = R.id;
wsname = R.name;
}
if (id == WORKSPACE_INVALID) {
Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {}", rule.workspaceString);
continue;
}
PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
createNewWorkspace(id, PMONITOR ? PMONITOR->ID : m_pLastMonitor->ID, wsname, false);
}
if (PWORKSPACE)
PWORKSPACE->m_bPersistent = true;
if (!PMONITOR) {
Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping", rule.monitor);
continue;
}
WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName;
if (id == WORKSPACE_INVALID) {
const auto R = getWorkspaceIDNameFromString(rule.workspaceString);
id = R.id;
wsname = R.name;
}
if (id == WORKSPACE_INVALID) {
Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {}", rule.workspaceString);
continue;
}
if (const auto PWORKSPACE = getWorkspaceByID(id); PWORKSPACE) {
if (PWORKSPACE) {
if (PWORKSPACE->m_pMonitor == PMONITOR) {
Debug::log(LOG, "ensurePersistentWorkspacesPresent: workspace persistent {} already on {}", rule.workspaceString, PMONITOR->szName);
continue;
}
@@ -3060,8 +3125,6 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
moveWorkspaceToMonitor(PWORKSPACE, PMONITOR);
continue;
}
createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false);
}
// cleanup old

View File

@@ -1,15 +1,12 @@
#pragma once
#include <list>
#include <sys/resource.h>
#include "defines.hpp"
#include "managers/XWaylandManager.hpp"
#include "managers/KeybindManager.hpp"
#include "managers/SessionLockManager.hpp"
#include "desktop/Window.hpp"
#include "protocols/types/ColorManagement.hpp"
#include "helpers/memory/Memory.hpp"
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
@@ -28,8 +25,8 @@ class CCompositor {
CCompositor(bool onlyConfig = false);
~CCompositor();
wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop;
wl_display* m_sWLDisplay = nullptr;
wl_event_loop* m_sWLEventLoop = nullptr;
int m_iDRMFD = -1;
bool m_bInitialized = false;
SP<Aquamarine::CBackend> m_pAqBackend;
@@ -55,8 +52,6 @@ class CCompositor {
void startCompositor();
void stopCompositor();
void cleanup();
void createLockFile();
void removeLockFile();
void bumpNofile();
void restoreNofile();
@@ -86,7 +81,7 @@ class CCompositor {
PHLMONITOR getMonitorFromCursor();
PHLMONITOR getMonitorFromVector(const Vector2D&);
void removeWindowFromVectorSafe(PHLWINDOW);
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr);
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr, bool preserveFocusHistory = false);
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
bool monitorExists(PHLMONITOR);
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
@@ -108,8 +103,8 @@ class CCompositor {
void cleanupFadingOut(const MONITORID& monid);
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}, bool visible = false);
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}, bool visible = false);
PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool prev = false);
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false);
WORKSPACEID getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
@@ -152,12 +147,12 @@ class CCompositor {
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
void updateSuspendedStates();
void onNewMonitor(SP<Aquamarine::IOutput> output);
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules);
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
SImageDescription getPreferredImageDescription();
bool shouldChangePreferredImageDescription();
NColorManagement::SImageDescription getPreferredImageDescription();
bool shouldChangePreferredImageDescription();
std::string explicitConfigPath;
std::string explicitConfigPath;
private:
void initAllSignals();
@@ -166,11 +161,13 @@ class CCompositor {
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
bool isWindowAvailableForCycle(PHLWINDOW pWindow, PHLWINDOW w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false);
void createLockFile();
void removeLockFile();
void setMallocThreshold();
uint64_t m_iHyprlandPID = 0;
wl_event_source* m_critSigSource = nullptr;
rlimit m_sOriginalNofile = {0};
rlimit m_sOriginalNofile = {};
};
inline UP<CCompositor> g_pCompositor;

View File

@@ -11,7 +11,7 @@ enum eConfigValueDataTypes : int8_t {
class ICustomConfigValueData {
public:
virtual ~ICustomConfigValueData() = 0;
virtual ~ICustomConfigValueData() = default;
virtual eConfigValueDataTypes getDataType() = 0;

View File

@@ -253,98 +253,98 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
*/
SConfigOptionDescription{
.value = "blur:enabled",
.value = "decoration:blur:enabled",
.description = "enable kawase window background blur",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "blur:size",
.value = "decoration:blur:size",
.description = "blur size (distance)",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{8, 0, 100},
},
SConfigOptionDescription{
.value = "blur:passes",
.value = "decoration:blur:passes",
.description = "the amount of passes to perform",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 10},
},
SConfigOptionDescription{
.value = "blur:ignore_opacity",
.value = "decoration:blur:ignore_opacity",
.description = "make the blur layer ignore the opacity of the window",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "blur:new_optimizations",
.value = "decoration:blur:new_optimizations",
.description = "whether to enable further optimizations to the blur. Recommended to leave on, as it will massively improve performance.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "blur:xray",
.value = "decoration:blur:xray",
.description = "if enabled, floating windows will ignore tiled windows in their blur. Only available if blur_new_optimizations is true. Will reduce overhead on floating "
"blur significantly.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "blur:noise",
.value = "decoration:blur:noise",
.description = "how much noise to apply. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.0117, 0, 1},
},
SConfigOptionDescription{
.value = "blur:contrast",
.value = "decoration:blur:contrast",
.description = "contrast modulation for blur. [0.0 - 2.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.8916, 0, 2},
},
SConfigOptionDescription{
.value = "blur:brightness",
.value = "decoration:blur:brightness",
.description = "brightness modulation for blur. [0.0 - 2.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.8172, 0, 2},
},
SConfigOptionDescription{
.value = "blur:vibrancy",
.value = "decoration:blur:vibrancy",
.description = "Increase saturation of blurred colors. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.1696, 0, 1},
},
SConfigOptionDescription{
.value = "blur:vibrancy_darkness",
.value = "decoration:blur:vibrancy_darkness",
.description = "How strong the effect of vibrancy is on dark areas . [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0, 0, 1},
},
SConfigOptionDescription{
.value = "blur:special",
.value = "decoration:blur:special",
.description = "whether to blur behind the special workspace (note: expensive)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "blur:popups",
.value = "decoration:blur:popups",
.description = "whether to blur popups (e.g. right-click menus)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "blur:popups_ignorealpha",
.value = "decoration:blur:popups_ignorealpha",
.description = "works like ignorealpha in layer rules. If pixel opacity is below set value, will not blur. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
},
SConfigOptionDescription{
.value = "blur:input_methods",
.value = "decoration:blur:input_methods",
.description = "whether to blur input methods (e.g. fcitx5)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "blur:input_methods_ignorealpha",
.value = "decoration:blur:input_methods_ignorealpha",
.description = "works like ignorealpha in layer rules. If pixel opacity is below set value, will not blur. [0.0 - 1.0]",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{0.2, 0, 1},
@@ -501,6 +501,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 3},
},
SConfigOptionDescription{
.value = "input:follow_mouse_threshold",
.description = "The smallest distance in logical pixels the mouse needs to travel for the window under it to get focused. Works only with follow_mouse = 1.",
.type = CONFIG_OPTION_FLOAT,
.data = SConfigOptionDescription::SFloatData{},
},
SConfigOptionDescription{
.value = "input:focus_on_close",
.description = "Controls the window focus behavior when a window is closed. When set to 0, focus will shift to the next window candidate. When set to 1, focus will shift "
@@ -602,6 +608,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "input:touchpad:flip_x",
.description = "Inverts the horizontal movement of the touchpad",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "input:touchpad:flip_y",
.description = "Inverts the vertical movement of the touchpad",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* input:touchdevice:
@@ -809,25 +827,25 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "general:col.border_active",
.value = "group:col.border_active",
.description = "border color for inactive windows",
.type = CONFIG_OPTION_GRADIENT,
.data = SConfigOptionDescription::SGradientData{"0x66ffff00"},
},
SConfigOptionDescription{
.value = "general:col.border_inactive",
.value = "group:col.border_inactive",
.description = "border color for the active window",
.type = CONFIG_OPTION_GRADIENT,
.data = SConfigOptionDescription::SGradientData{"0x66777700"},
},
SConfigOptionDescription{
.value = "general:col.border_locked_active",
.value = "group:col.border_locked_inactive",
.description = "inactive border color for window that cannot be added to a group (see denywindowfromgroup dispatcher)",
.type = CONFIG_OPTION_GRADIENT,
.data = SConfigOptionDescription::SGradientData{"0x66ff5500"},
},
SConfigOptionDescription{
.value = "general:col.border_locked_inactive",
.value = "group:col.border_locked_active",
.description = "active border color for window that cannot be added to a group",
.type = CONFIG_OPTION_GRADIENT,
.data = SConfigOptionDescription::SGradientData{"0x66775500"},
@@ -883,7 +901,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.value = "group:groupbar:gradients",
.description = "enables gradients",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "group:groupbar:height",
@@ -891,6 +909,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{14, 1, 64},
},
SConfigOptionDescription{
.value = "group:groupbar:indicator_height",
.description = "height of the groupbar indicator",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{3, 1, 64},
},
SConfigOptionDescription{
.value = "group:groupbar:stacked",
.description = "render the groupbar as a vertical stack",
@@ -915,6 +939,30 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:groupbar:rounding",
.description = "how much to round the groupbar",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
},
SConfigOptionDescription{
.value = "group:groupbar:gradient_rounding",
.description = "how much to round the groupbar gradient",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
},
SConfigOptionDescription{
.value = "group:groupbar:round_only_edges",
.description = "if yes, will only round at the groupbar edges",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:groupbar:gradient_round_only_edges",
.description = "if yes, will only round at the groupbar gradient edges",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:groupbar:text_color",
.description = "controls the group bar text color",
@@ -945,6 +993,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_COLOR,
.data = SConfigOptionDescription::SColorData{0x66775500},
},
SConfigOptionDescription{
.value = "group:groupbar:gaps_out",
.description = "gap between gradients and window",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
},
SConfigOptionDescription{
.value = "group:groupbar:gaps_in",
.description = "gap between gradients",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
},
/*
* misc:
@@ -994,9 +1054,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "misc:vrr",
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only [0/1/2]",
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only, 3 - fullscreen with game or video content type [0/1/2/3]",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 3},
},
SConfigOptionDescription{
.value = "misc:mouse_move_enables_dpms",
@@ -1151,6 +1211,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1000, 0, 5000},
},
SConfigOptionDescription{
.value = "misc:enable_anr_dialog",
.description = "whether to enable the ANR (app not responding) dialog when your apps hang",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
/*
* binds:
@@ -1204,7 +1270,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.value = "binds:movefocus_cycles_fullscreen",
.description = "If enabled, when on a fullscreen window, movefocus will cycle fullscreen, if not, it will move the focus in a direction.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "binds:movefocus_cycles_groupfirst",
@@ -1253,6 +1319,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "xwayland:create_abstract_socket",
.description = "Create the abstract Unix domain socket for XWayland",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* opengl:
@@ -1264,13 +1336,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "opengl:force_introspection",
.description = "forces introspection at all times. Introspection is aimed at reducing GPU usage in certain cases, but might cause graphical glitches on nvidia. 0 - "
"nothing, 1 - force always on, 2 - force always on if nvidia",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
},
/*
* render:
@@ -1291,9 +1356,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
SConfigOptionDescription{
.value = "render:direct_scanout",
.description = "Enables direct scanout. Direct scanout attempts to reduce lag when there is only one fullscreen application on a screen (e.g. game). It is also "
"recommended to set this to false if the fullscreen application shows graphical glitches.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
"recommended to set this to false if the fullscreen application shows graphical glitches. 0 - off, 1 - on, 2 - auto (on with content type 'game')",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "render:expand_undersized_textures",
@@ -1314,8 +1379,14 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
},
SConfigOptionDescription{
.value = "render:allow_early_buffer_release",
.description = "Allow early buffer release event. Fixes stuttering and missing frames for some apps. May cause graphical glitches and memory leaks in others",
.value = "render:cm_fs_passthrough",
.description = "Passthrough color settings for fullscreen apps when possible",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "render:cm_enabled",
.description = "Enable Color Management pipelines (requires restart to fully take effect)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
@@ -1324,24 +1395,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
* cursor:
*/
SConfigOptionDescription{
.value = "cursor:use_nearest_neighbor",
.description = "sync xcursor theme with gsettings, it applies cursor-theme and cursor-size on theme load to gsettings making most CSD gtk based clients use same xcursor "
"theme and size.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "cursor:no_hardware_cursors",
.description = "disables hardware cursors",
.description = "disables hardware cursors. Auto = disable when tearing",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Auto"},
},
SConfigOptionDescription{
.value = "cursor:no_break_fs_vrr",
.description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (requires no_hardware_cursors = true)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
.description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (may require no_hardware_cursors = true) "
"0 - off, 1 - on, 2 - auto (on with content type 'game')",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "cursor:min_refresh_rate",
@@ -1421,6 +1486,35 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "cursor:sync_gsettings_theme",
.description = "sync xcursor theme with gsettings, it applies cursor-theme and cursor-size on theme load to gsettings making most CSD gtk based clients use same xcursor "
"theme and size.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "cursor:warp_back_after_non_mouse_input",
.description = "warp the cursor back to where it was after using a non-mouse input to move it, and then returning back to mouse.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* ecosystem:
*/
SConfigOptionDescription{
.value = "ecosystem:no_update_news",
.description = "disable the popup that shows up when you update hyprland to a new version.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "ecosystem:no_donation_nag",
.description = "disable the popup that shows up twice a year encouraging to donate.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* debug:
@@ -1504,6 +1598,24 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "debug:log_damage",
.description = "enables logging the damage.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "debug:pass",
.description = "enables render pass debugging.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "debug:full_cm_proto",
.description = "claims support for all cm proto features (requires restart)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* dwindle:
@@ -1664,22 +1776,16 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "experimental:wide_color_gamut",
.description = "force wide color gamut for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:hdr",
.description = "force static hdr for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:xx_color_management_v4",
.description = "enable color management protocol",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "master:always_keep_position",
.description = "whether to keep the master window in its configured position when there are no slave windows",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -138,6 +138,27 @@ struct SFirstExecRequest {
bool withRules = false;
};
struct SFloatCache {
size_t hash;
SFloatCache(PHLWINDOW window) {
hash = std::hash<std::string>{}(window->m_szClass) ^ (std::hash<std::string>{}(window->m_szTitle) << 1);
}
bool operator==(const SFloatCache& other) const {
return hash == other.hash;
}
};
namespace std {
template <>
struct hash<SFloatCache> {
size_t operator()(const SFloatCache& id) const {
return id.hash;
}
};
}
class CConfigManager {
public:
CConfigManager();
@@ -189,7 +210,8 @@ class CConfigManager {
void ensureMonitorStatus();
void ensureVRR(PHLMONITOR pMonitor = nullptr);
bool shouldUseSoftwareCursors();
bool shouldUseSoftwareCursors(PHLMONITOR pMonitor);
void updateWatcher();
std::string parseKeyword(const std::string&, const std::string&);
@@ -203,66 +225,35 @@ class CConfigManager {
std::string getErrors();
// keywords
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
std::optional<std::string> handleExec(const std::string&, const std::string&);
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecRawOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecShutdown(const std::string&, const std::string&);
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWindowRuleV2(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handleRawExec(const std::string&, const std::string&);
std::optional<std::string> handleExec(const std::string&, const std::string&);
std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecRawOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecShutdown(const std::string&, const std::string&);
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::string configCurrentPath;
std::string configCurrentPath;
std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> mbWindowProperties = {
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.allowsInput; }},
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.dimAround; }},
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.decorate; }},
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }},
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }},
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }},
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noAnim; }},
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBlur; }},
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBorder; }},
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noDim; }},
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noFocus; }},
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noRounding; }},
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShadow; }},
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.opaque; }},
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.RGBX; }},
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.tearing; }},
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.xray; }},
};
bool m_bWantsMonitorReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
bool m_bLastConfigVerificationWasSuccessful = true;
std::unordered_map<std::string, std::function<CWindowOverridableVar<int>*(const PHLWINDOW&)>> miWindowProperties = {
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.rounding; }},
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.borderSize; }},
};
std::unordered_map<std::string, std::function<CWindowOverridableVar<float>*(PHLWINDOW)>> mfWindowProperties = {
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.roundingPower; }},
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
bool m_bWantsMonitorReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
bool m_bLastConfigVerificationWasSuccessful = true;
void storeFloatingSize(PHLWINDOW window, const Vector2D& size);
std::optional<Vector2D> getStoredFloatingSize(PHLWINDOW window);
private:
UP<Hyprlang::CConfig> m_pConfig;
@@ -296,14 +287,26 @@ class CConfigManager {
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
std::string m_szConfigErrors = "";
uint32_t m_configValueNumber = 0;
// internal methods
void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
void registerConfigVar(const char* name, const Hyprlang::INT& val);
void registerConfigVar(const char* name, const Hyprlang::FLOAT& val);
void registerConfigVar(const char* name, const Hyprlang::VEC2& val);
void registerConfigVar(const char* name, const Hyprlang::STRING& val);
void registerConfigVar(const char* name, Hyprlang::CUSTOMTYPE&& val);
std::unordered_map<SFloatCache, Vector2D> m_mStoredFloatingSizes;
friend struct SConfigOptionDescription;
};
inline UP<CConfigManager> g_pConfigManager;

View File

@@ -4,28 +4,26 @@
#include <ranges>
#include <fcntl.h>
#include <unistd.h>
#include <filesystem>
using namespace Hyprutils::OS;
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
if (m_inotifyFd < 0) {
if (!m_inotifyFd.isValid()) {
Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
return;
}
const int FLAGS = fcntl(m_inotifyFd, F_GETFL, 0);
if (fcntl(m_inotifyFd, F_SETFL, FLAGS | O_NONBLOCK) < 0) {
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
close(m_inotifyFd);
m_inotifyFd = -1;
m_inotifyFd.reset();
return;
}
}
CConfigWatcher::~CConfigWatcher() {
if (m_inotifyFd >= 0)
close(m_inotifyFd);
}
int CConfigWatcher::getInotifyFD() {
CFileDescriptor& CConfigWatcher::getInotifyFD() {
return m_inotifyFd;
}
@@ -38,7 +36,7 @@ void CConfigWatcher::setWatchList(const std::vector<std::string>& paths) {
// cleanup old paths
for (auto& watch : m_watches) {
inotify_rm_watch(m_inotifyFd, watch.wd);
inotify_rm_watch(m_inotifyFd.get(), watch.wd);
}
m_watches.clear();
@@ -46,9 +44,19 @@ void CConfigWatcher::setWatchList(const std::vector<std::string>& paths) {
// add new paths
for (const auto& path : paths) {
m_watches.emplace_back(SInotifyWatch{
.wd = inotify_add_watch(m_inotifyFd, path.c_str(), IN_MODIFY),
.wd = inotify_add_watch(m_inotifyFd.get(), path.c_str(), IN_MODIFY | IN_DONT_FOLLOW),
.file = path,
});
std::error_code ec, ec2;
const auto CANONICAL = std::filesystem::canonical(path, ec);
const auto IS_SYMLINK = std::filesystem::is_symlink(path, ec2);
if (!ec && !ec2 && IS_SYMLINK) {
m_watches.emplace_back(SInotifyWatch{
.wd = inotify_add_watch(m_inotifyFd.get(), CANONICAL.c_str(), IN_MODIFY),
.file = path,
});
}
}
}
@@ -58,7 +66,7 @@ void CConfigWatcher::setOnChange(const std::function<void(const SConfigWatchEven
void CConfigWatcher::onInotifyEvent() {
inotify_event ev;
while (read(m_inotifyFd, &ev, sizeof(ev)) > 0) {
while (read(m_inotifyFd.get(), &ev, sizeof(ev)) > 0) {
const auto WD = std::ranges::find_if(m_watches.begin(), m_watches.end(), [wd = ev.wd](const auto& e) { return e.wd == wd; });
if (WD == m_watches.end()) {

View File

@@ -3,20 +3,21 @@
#include <vector>
#include <string>
#include <functional>
#include <hyprutils/os/FileDescriptor.hpp>
class CConfigWatcher {
public:
CConfigWatcher();
~CConfigWatcher();
~CConfigWatcher() = default;
struct SConfigWatchEvent {
std::string file;
};
int getInotifyFD();
void setWatchList(const std::vector<std::string>& paths);
void setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn);
void onInotifyEvent();
Hyprutils::OS::CFileDescriptor& getInotifyFD();
void setWatchList(const std::vector<std::string>& paths);
void setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn);
void onInotifyEvent();
private:
struct SInotifyWatch {
@@ -26,7 +27,7 @@ class CConfigWatcher {
std::function<void(const SConfigWatchEvent&)> m_watchCallback;
std::vector<SInotifyWatch> m_watches;
int m_inotifyFd = -1;
Hyprutils::OS::CFileDescriptor m_inotifyFd;
};
inline UP<CConfigWatcher> g_pConfigWatcher = makeUnique<CConfigWatcher>();

View File

@@ -152,10 +152,10 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
@@ -269,7 +269,7 @@ bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
@@ -289,15 +289,12 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule
# windowrule = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
)#";

View File

@@ -25,6 +25,7 @@
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
#include <aquamarine/input/Input.hpp>
#include "../config/ConfigDataValues.hpp"
@@ -263,8 +264,8 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"),
(int64_t)w->monitorID(), escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass),
escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
(uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"));
(uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(),
getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
@@ -275,7 +276,7 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
(int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID,
(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->monitorID(), w->m_szClass, w->m_szTitle,
w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal,
(uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w),
(uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(), getFocusHistoryID(w),
(int)g_pInputManager->isWindowInhibiting(w, false));
}
}
@@ -318,15 +319,17 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
"windows": {},
"hasfullscreen": {},
"lastwindow": "0x{:x}",
"lastwindowtitle": "{}"
"lastwindowtitle": "{}",
"ispersistent": {}
}})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"),
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), w->getWindows(), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"),
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), w->getWindows(), w->m_bHasFullscreenWindow ? "true" : "false",
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "", w->m_bPersistent ? "true" : "false");
} else {
return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID,
w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", w->getWindows(), (int)w->m_bHasFullscreenWindow,
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_szTitle : "");
return std::format(
"workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n",
w->m_iID, w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", w->getWindows(), (int)w->m_bHasFullscreenWindow,
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_szTitle : "", (int)w->m_bPersistent);
}
}
@@ -476,9 +479,11 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques
"y": {},
"w": {},
"h": {},
"namespace": "{}"
"namespace": "{}",
"pid": {}
}},)#",
(uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, layer->geometry.height, escapeJSONStrings(layer->szNamespace));
(uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, layer->geometry.height, escapeJSONStrings(layer->szNamespace),
layer->getPID());
}
trimTrailingComma(result);
@@ -509,8 +514,8 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques
result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]);
for (auto const& layer : level) {
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);
result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}, pid: {}\n", (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y,
layer->geometry.width, layer->geometry.height, layer->szNamespace, layer->getPID());
}
layerLevel++;
@@ -808,8 +813,11 @@ static std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::stri
std::string ret = "";
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto const& sh : SHORTCUTS)
for (auto const& sh : SHORTCUTS) {
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
}
if (ret.empty())
ret = "none";
} else {
ret += "[";
for (auto const& sh : SHORTCUTS) {
@@ -1100,6 +1108,9 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
}
}
if (COMMAND.contains("misc:disable_autoreload"))
g_pConfigManager->updateWatcher();
// decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
COMMAND.starts_with("windowrule")) {
@@ -1677,8 +1688,6 @@ CHyprCtl::CHyprCtl() {
CHyprCtl::~CHyprCtl() {
if (m_eventSource)
wl_event_source_remove(m_eventSource);
if (m_iSocketFD >= 0)
close(m_iSocketFD);
if (!m_socketPath.empty())
unlink(m_socketPath.c_str());
}
@@ -1837,10 +1846,13 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0;
if (!g_pHyprCtl->m_iSocketFD.isValid())
return 0;
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
std::array<char, 1024> readBuffer;
@@ -1897,9 +1909,9 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
}
void CHyprCtl::startHyprCtlSocket() {
m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
m_iSocketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
if (m_iSocketFD < 0) {
if (!m_iSocketFD.isValid()) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return;
}
@@ -1910,15 +1922,15 @@ void CHyprCtl::startHyprCtlSocket() {
strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str());
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
if (bind(m_iSocketFD.get(), (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
return;
}
// 10 max queued.
listen(m_iSocketFD, 10);
listen(m_iSocketFD.get(), 10);
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View File

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

View File

@@ -31,10 +31,9 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
pLS->szNamespace = resource->layerNamespace;
pLS->layer = resource->current.layer;
pLS->popupHead = makeUnique<CPopup>(pLS);
pLS->popupHead->m_pSelf = pLS->popupHead;
pLS->monitor = pMonitor;
pLS->layer = resource->current.layer;
pLS->popupHead = CPopup::create(pLS);
pLS->monitor = pMonitor;
pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS);
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
@@ -98,7 +97,8 @@ void CLayerSurface::onDestroy() {
onUnmap();
} else {
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
alpha->setValueAndWarp(0.f);
if (alpha)
alpha->setValueAndWarp(0.f);
fadingOut = true;
g_pCompositor->addToFadingOutSafe(self.lock());
}
@@ -584,3 +584,15 @@ int CLayerSurface::popupsCount() {
MONITORID CLayerSurface::monitorID() {
return monitor ? monitor->ID : MONITOR_INVALID;
}
pid_t CLayerSurface::getPID() {
pid_t PID = -1;
if (!layerSurface || !layerSurface->surface || !layerSurface->surface->getResource() || !layerSurface->surface->getResource()->resource() ||
!layerSurface->surface->getResource()->resource()->client)
return -1;
wl_client_get_credentials(layerSurface->surface->getResource()->resource()->client, &PID, nullptr, nullptr);
return PID;
}

View File

@@ -61,6 +61,8 @@ class CLayerSurface {
std::string szNamespace = "";
UP<CPopup> popupHead;
pid_t getPID();
void onDestroy();
void onMap();
void onUnmap();

View File

@@ -12,23 +12,37 @@
#include "../render/OpenGL.hpp"
#include <ranges>
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
initAllSignals();
UP<CPopup> CPopup::create(PHLWINDOW pOwner) {
auto popup = UP<CPopup>(new CPopup());
popup->m_pWindowOwner = pOwner;
popup->m_pSelf = popup;
popup->initAllSignals();
return popup;
}
CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) {
initAllSignals();
UP<CPopup> CPopup::create(PHLLS pOwner) {
auto popup = UP<CPopup>(new CPopup());
popup->m_pLayerOwner = pOwner;
popup->m_pSelf = popup;
popup->initAllSignals();
return popup;
}
CPopup::CPopup(SP<CXDGPopupResource> popup, WP<CPopup> pOwner) :
m_pWindowOwner(pOwner->m_pWindowOwner), m_pLayerOwner(pOwner->m_pLayerOwner), m_pParent(pOwner), m_pResource(popup) {
m_pWLSurface = CWLSurface::create();
m_pWLSurface->assign(popup->surface->surface.lock(), this);
UP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
auto popup = UP<CPopup>(new CPopup());
popup->m_pResource = resource;
popup->m_pWindowOwner = pOwner->m_pWindowOwner;
popup->m_pLayerOwner = pOwner->m_pLayerOwner;
popup->m_pParent = pOwner;
popup->m_pSelf = popup;
popup->m_pWLSurface = CWLSurface::create();
popup->m_pWLSurface->assign(resource->surface->surface.lock(), popup.get());
m_vLastSize = popup->surface->current.geometry.size();
reposition();
popup->m_vLastSize = resource->surface->current.geometry.size();
popup->reposition();
initAllSignals();
popup->initAllSignals();
return popup;
}
CPopup::~CPopup() {
@@ -59,7 +73,7 @@ void CPopup::initAllSignals() {
}
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
const auto& POPUP = m_vChildren.emplace_back(makeShared<CPopup>(popup, m_pSelf));
const auto& POPUP = m_vChildren.emplace_back(CPopup::create(popup, m_pSelf));
POPUP->m_pSelf = POPUP;
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
}
@@ -91,8 +105,7 @@ void CPopup::onMap() {
g_pInputManager->simulateMouseMovement();
m_pSubsurfaceHead = makeUnique<CSubsurface>(m_pSelf);
m_pSubsurfaceHead->m_pSelf = m_pSubsurfaceHead;
m_pSubsurfaceHead = CSubsurface::create(m_pSelf);
//unconstrain();
sendScale();
@@ -138,10 +151,11 @@ void CPopup::onUnmap() {
},
nullptr);
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
// TODO: probably refocus, but without a motion event?
// const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource();
if (WASLASTFOCUS)
g_pInputManager->simulateMouseMovement();
// if (WASLASTFOCUS)
// g_pInputManager->simulateMouseMovement();
}
void CPopup::onCommit(bool ignoreSiblings) {
@@ -268,7 +282,11 @@ void CPopup::recheckTree() {
}
void CPopup::recheckChildrenRecursive() {
auto cpy = m_vChildren;
if (m_bInert || !m_pWLSurface)
return;
std::vector<WP<CPopup>> cpy;
std::ranges::for_each(m_vChildren, [&cpy](const auto& el) { cpy.emplace_back(el); });
for (auto const& c : cpy) {
c->onCommit(true);
c->recheckChildrenRecursive();
@@ -325,23 +343,26 @@ void CPopup::breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data)
WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<WP<CPopup>> popups;
breadthfirst([](WP<CPopup> popup, void* data) { ((std::vector<WP<CPopup>>*)data)->push_back(popup); }, &popups);
breadthfirst([&popups](WP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
for (auto const& p : popups | std::views::reverse) {
if (!p->m_pResource || !p->m_bMapped)
continue;
if (!allowsInput) {
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
const Vector2D size = p->m_pResource ? p->m_pResource->geometry.size() : p->size();
const bool HASSURFACE = p->m_pResource && p->m_pResource->surface;
const auto BOX = CBox{p->coordsGlobal() + offset, size};
Vector2D offset = HASSURFACE ? p->m_pResource->surface->current.geometry.pos() : Vector2D{};
Vector2D size = HASSURFACE ? p->m_pResource->surface->current.geometry.size() : p->size();
if (size == Vector2D{})
size = p->size();
const auto BOX = CBox{p->coordsGlobal() + offset, size};
if (BOX.containsPoint(globalCoords))
return p;
} else {
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
const auto REGION =
CRegion{p->m_pWLSurface->resource()->current.input}.intersect(CBox{{}, p->m_pWLSurface->resource()->current.size}).translate(p->coordsGlobal() + offset);
const auto REGION = CRegion{p->m_pWLSurface->resource()->current.input}.intersect(CBox{{}, p->m_pWLSurface->resource()->current.size}).translate(p->coordsGlobal());
if (REGION.containsPoint(globalCoords))
return p;
}
@@ -349,3 +370,7 @@ WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
return {};
}
bool CPopup::inert() const {
return m_bInert;
}

View File

@@ -10,11 +10,11 @@ class CXDGPopupResource;
class CPopup {
public:
// dummy head nodes
CPopup(PHLWINDOW pOwner);
CPopup(PHLLS pOwner);
static UP<CPopup> create(PHLWINDOW pOwner);
static UP<CPopup> create(PHLLS pOwner);
// real nodes
CPopup(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
static UP<CPopup> create(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
~CPopup();
@@ -34,6 +34,7 @@ class CPopup {
void recheckTree();
bool visible();
bool inert() const;
// will also loop over this node
void breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data);
@@ -45,6 +46,8 @@ class CPopup {
bool m_bMapped = false;
private:
CPopup() = default;
// T1 owners, each popup has to have one of these
PHLWINDOWREF m_pWindowOwner;
PHLLSREF m_pLayerOwner;
@@ -62,7 +65,7 @@ class CPopup {
bool m_bInert = false;
//
std::vector<SP<CPopup>> m_vChildren;
std::vector<UP<CPopup>> m_vChildren;
UP<CSubsurface> m_pSubsurfaceHead;
struct {

View File

@@ -7,32 +7,47 @@
#include "../render/Renderer.hpp"
#include "../managers/input/InputManager.hpp"
CSubsurface::CSubsurface(PHLWINDOW pOwner) : m_pWindowParent(pOwner) {
initSignals();
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
UP<CSubsurface> CSubsurface::create(PHLWINDOW pOwner) {
auto subsurface = UP<CSubsurface>(new CSubsurface());
subsurface->m_pWindowParent = pOwner;
subsurface->m_pSelf = subsurface;
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
return subsurface;
}
CSubsurface::CSubsurface(WP<CPopup> pOwner) : m_pPopupParent(pOwner) {
initSignals();
initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
UP<CSubsurface> CSubsurface::create(WP<CPopup> pOwner) {
auto subsurface = UP<CSubsurface>(new CSubsurface());
subsurface->m_pPopupParent = pOwner;
subsurface->m_pSelf = subsurface;
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pOwner->m_pWLSurface->resource());
return subsurface;
}
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) {
m_pWLSurface = CWLSurface::create();
m_pWLSurface->assign(pSubsurface->surface.lock(), this);
initSignals();
initExistingSubsurfaces(pSubsurface->surface.lock());
UP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner) {
auto subsurface = UP<CSubsurface>(new CSubsurface());
subsurface->m_pWindowParent = pOwner;
subsurface->m_pSubsurface = pSubsurface;
subsurface->m_pSelf = subsurface;
subsurface->m_pWLSurface = CWLSurface::create();
subsurface->m_pWLSurface->assign(pSubsurface->surface.lock(), subsurface.get());
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pSubsurface->surface.lock());
return subsurface;
}
CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) {
m_pWLSurface = CWLSurface::create();
m_pWLSurface->assign(pSubsurface->surface.lock(), this);
initSignals();
initExistingSubsurfaces(pSubsurface->surface.lock());
}
CSubsurface::~CSubsurface() {
;
UP<CSubsurface> CSubsurface::create(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner) {
auto subsurface = UP<CSubsurface>(new CSubsurface());
subsurface->m_pPopupParent = pOwner;
subsurface->m_pSubsurface = pSubsurface;
subsurface->m_pSelf = subsurface;
subsurface->m_pWLSurface = CWLSurface::create();
subsurface->m_pWLSurface->assign(pSubsurface->surface.lock(), subsurface.get());
subsurface->initSignals();
subsurface->initExistingSubsurfaces(pSubsurface->surface.lock());
return subsurface;
}
void CSubsurface::initSignals() {
@@ -92,7 +107,7 @@ void CSubsurface::onCommit() {
g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y);
if (m_pPopupParent)
if (m_pPopupParent && !m_pPopupParent->inert() && m_pPopupParent->m_pWLSurface)
m_pPopupParent->recheckTree();
if (!m_pWindowParent.expired()) // I hate you firefox why are you doing this
m_pWindowParent->m_pPopupHead->recheckTree();
@@ -109,7 +124,7 @@ void CSubsurface::onCommit() {
// g_pHyprRenderer->damageBox(box);
CBox box;
if (m_pPopupParent)
if (m_pPopupParent && !m_pPopupParent->inert() && m_pPopupParent->m_pWLSurface)
box = m_pPopupParent->m_pWLSurface->getSurfaceBoxGlobal().value_or(CBox{});
else if (m_pWindowParent)
box = m_pWindowParent->getWindowMainSurfaceBox();
@@ -135,15 +150,15 @@ void CSubsurface::onNewSubsurface(SP<CWLSubsurfaceResource> pSubsurface) {
WP<CSubsurface> PSUBSURFACE;
if (!m_pWindowParent.expired())
PSUBSURFACE = m_vChildren.emplace_back(makeUnique<CSubsurface>(pSubsurface, m_pWindowParent.lock()));
PSUBSURFACE = m_vChildren.emplace_back(CSubsurface::create(pSubsurface, m_pWindowParent.lock()));
else if (m_pPopupParent)
PSUBSURFACE = m_vChildren.emplace_back(makeUnique<CSubsurface>(pSubsurface, m_pPopupParent));
PSUBSURFACE = m_vChildren.emplace_back(CSubsurface::create(pSubsurface, m_pPopupParent));
PSUBSURFACE->m_pSelf = PSUBSURFACE;
ASSERT(PSUBSURFACE);
PSUBSURFACE->m_pParent = PSUBSURFACE;
PSUBSURFACE->m_pParent = m_pSelf;
}
void CSubsurface::onMap() {

View File

@@ -10,14 +10,14 @@ class CWLSubsurfaceResource;
class CSubsurface {
public:
// root dummy nodes
CSubsurface(PHLWINDOW pOwner);
CSubsurface(WP<CPopup> pOwner);
static UP<CSubsurface> create(PHLWINDOW pOwner);
static UP<CSubsurface> create(WP<CPopup> pOwner);
// real nodes
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner);
static UP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, PHLWINDOW pOwner);
static UP<CSubsurface> create(SP<CWLSubsurfaceResource> pSubsurface, WP<CPopup> pOwner);
~CSubsurface();
~CSubsurface() = default;
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
@@ -37,6 +37,8 @@ class CSubsurface {
WP<CSubsurface> m_pSelf;
private:
CSubsurface() = default;
struct {
CHyprSignalListener destroySubsurface;
CHyprSignalListener commitSubsurface;

View File

@@ -98,7 +98,7 @@ CRegion CWLSurface::computeDamage() const {
if (!m_pResource->current.texture)
return {};
CRegion damage = m_pResource->accumulateCurrentBufferDamage();
CRegion damage = m_pResource->current.accumulateBufferDamage();
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
const auto BUFSIZE = m_pResource->current.bufferSize;

View File

@@ -13,8 +13,10 @@
#include "../config/ConfigValue.hpp"
#include "../managers/TokenManager.hpp"
#include "../managers/AnimationManager.hpp"
#include "../managers/ANRManager.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ContentType.hpp"
#include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp"
#include "../events/Events.hpp"
@@ -29,6 +31,7 @@
using namespace Hyprutils::String;
using namespace Hyprutils::Animation;
using enum NContentType::eContentType;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
@@ -46,6 +49,7 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
g_pAnimationManager->createAnimation(0.f, pWindow->m_fDimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow));
@@ -69,6 +73,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
g_pAnimationManager->createAnimation(0.f, pWindow->m_fDimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow));
@@ -93,22 +98,22 @@ CWindow::CWindow(SP<CXDGSurfaceResource> resource) : m_pXDGSurface(resource) {
CWindow::CWindow(SP<CXWaylandSurface> surface) : m_pXWaylandSurface(surface) {
m_pWLSurface = CWLSurface::create();
listeners.map = m_pXWaylandSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); });
listeners.unmap = m_pXWaylandSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); });
listeners.destroy = m_pXWaylandSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); });
listeners.commit = m_pXWaylandSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); });
listeners.configure = m_pXWaylandSurface->events.configure.registerListener([this](std::any d) { onX11Configure(std::any_cast<CBox>(d)); });
listeners.updateState = m_pXWaylandSurface->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); });
listeners.updateMetadata = m_pXWaylandSurface->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); });
listeners.resourceChange = m_pXWaylandSurface->events.resourceChange.registerListener([this](std::any d) { onResourceChangeX11(); });
listeners.activate = m_pXWaylandSurface->events.activate.registerListener([this](std::any d) { Events::listener_activateX11(this, nullptr); });
listeners.map = m_pXWaylandSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); });
listeners.unmap = m_pXWaylandSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); });
listeners.destroy = m_pXWaylandSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); });
listeners.commit = m_pXWaylandSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); });
listeners.configureRequest = m_pXWaylandSurface->events.configureRequest.registerListener([this](std::any d) { onX11ConfigureRequest(std::any_cast<CBox>(d)); });
listeners.updateState = m_pXWaylandSurface->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); });
listeners.updateMetadata = m_pXWaylandSurface->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); });
listeners.resourceChange = m_pXWaylandSurface->events.resourceChange.registerListener([this](std::any d) { onResourceChangeX11(); });
listeners.activate = m_pXWaylandSurface->events.activate.registerListener([this](std::any d) { Events::listener_activateX11(this, nullptr); });
if (m_pXWaylandSurface->overrideRedirect)
listeners.setGeometry = m_pXWaylandSurface->events.setGeometry.registerListener([this](std::any d) { Events::listener_unmanagedSetGeometry(this, nullptr); });
}
CWindow::~CWindow() {
if (g_pCompositor->m_pLastWindow.lock().get() == this) {
if (g_pCompositor->m_pLastWindow == m_pSelf) {
g_pCompositor->m_pLastFocus.reset();
g_pCompositor->m_pLastWindow.reset();
}
@@ -119,7 +124,7 @@ CWindow::~CWindow() {
return;
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return !other.first.lock() || other.first.lock().get() == this; });
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.get() == this; });
}
SBoxExtents CWindow::getFullWindowExtents() {
@@ -410,7 +415,7 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
if (*PINITIALWSTRACKING == 2) {
// persistent
SInitialWorkspaceToken token = std::any_cast<SInitialWorkspaceToken>(TOKEN->data);
if (token.primaryOwner.lock().get() == this) {
if (token.primaryOwner == m_pSelf) {
token.workspace = pWorkspace->getConfigName();
TOKEN->data = token;
}
@@ -450,13 +455,12 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
}
if (const auto SWALLOWED = m_pSwallowed.lock()) {
SWALLOWED->moveToWorkspace(pWorkspace);
SWALLOWED->m_pMonitor = m_pMonitor;
if (SWALLOWED->m_bCurrentlySwallowed) {
SWALLOWED->moveToWorkspace(pWorkspace);
SWALLOWED->m_pMonitor = m_pMonitor;
}
}
// update xwayland coords
sendWindowSize(m_vRealSize->goal());
if (OLDWORKSPACE && g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE->m_iID) && OLDWORKSPACE->getWindows() == 0 && *PCLOSEONLASTSPECIAL) {
if (const auto PMONITOR = OLDWORKSPACE->m_pMonitor.lock(); PMONITOR)
PMONITOR->setSpecialWorkspace(nullptr);
@@ -501,7 +505,7 @@ void CWindow::onUnmap() {
if (*PINITIALWSTRACKING == 2) {
// persistent token, but the first window got removed so the token is gone
SInitialWorkspaceToken token = std::any_cast<SInitialWorkspaceToken>(TOKEN->data);
if (token.primaryOwner.lock().get() == this)
if (token.primaryOwner == m_pSelf)
g_pTokenManager->removeToken(TOKEN);
}
}
@@ -509,7 +513,7 @@ void CWindow::onUnmap() {
m_iLastWorkspace = m_pWorkspace->m_iID;
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other.expired() || other.lock().get() == this; });
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [this](const auto& other) { return other.expired() || other == m_pSelf; });
if (*PCLOSEONLASTSPECIAL && m_pWorkspace && m_pWorkspace->getWindows() == 0 && onSpecialWorkspace()) {
const auto PMONITOR = m_pMonitor.lock();
@@ -519,7 +523,7 @@ void CWindow::onUnmap() {
const auto PMONITOR = m_pMonitor.lock();
if (PMONITOR && PMONITOR->solitaryClient.lock().get() == this)
if (PMONITOR && PMONITOR->solitaryClient == m_pSelf)
PMONITOR->solitaryClient.reset();
if (m_pWorkspace) {
@@ -559,6 +563,15 @@ void CWindow::onMap() {
*m_fBorderAngleAnimationProgress = 1.f;
}
m_vRealSize->setCallbackOnBegin(
[this](auto) {
if (!m_bIsMapped || isX11OverrideRedirect())
return;
sendWindowSize();
},
false);
m_fMovingFromWorkspaceAlpha->setValueAndWarp(1.F);
g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf);
@@ -571,10 +584,8 @@ void CWindow::onMap() {
if (m_bIsX11)
return;
m_pSubsurfaceHead = makeUnique<CSubsurface>(m_pSelf.lock());
m_pSubsurfaceHead->m_pSelf = m_pSubsurfaceHead;
m_pPopupHead = makeUnique<CPopup>(m_pSelf.lock());
m_pPopupHead->m_pSelf = m_pPopupHead;
m_pSubsurfaceHead = CSubsurface::create(m_pSelf.lock());
m_pPopupHead = CPopup::create(m_pSelf.lock());
}
void CWindow::onBorderAngleAnimEnd(WP<CBaseAnimatedVariable> pav) {
@@ -598,9 +609,8 @@ void CWindow::onBorderAngleAnimEnd(WP<CBaseAnimatedVariable> pav) {
void CWindow::setHidden(bool hidden) {
m_bHidden = hidden;
if (hidden && g_pCompositor->m_pLastWindow.lock().get() == this) {
if (hidden && g_pCompositor->m_pLastWindow == m_pSelf)
g_pCompositor->m_pLastWindow.reset();
}
setSuspended(hidden);
}
@@ -766,21 +776,25 @@ void CWindow::applyDynamicRule(const SP<CWindowRule>& r) {
}
case CWindowRule::RULE_PROP: {
const CVarList VARS(r->szRule, 0, ' ');
if (auto search = g_pConfigManager->miWindowProperties.find(VARS[1]); search != g_pConfigManager->miWindowProperties.end()) {
if (auto search = NWindowProperties::intWindowProperties.find(VARS[1]); search != NWindowProperties::intWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stoi(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
} else if (auto search = g_pConfigManager->mfWindowProperties.find(VARS[1]); search != g_pConfigManager->mfWindowProperties.end()) {
} else if (auto search = NWindowProperties::floatWindowProperties.find(VARS[1]); search != NWindowProperties::floatWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stof(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
} else if (auto search = g_pConfigManager->mbWindowProperties.find(VARS[1]); search != g_pConfigManager->mbWindowProperties.end()) {
} else if (auto search = NWindowProperties::boolWindowProperties.find(VARS[1]); search != NWindowProperties::boolWindowProperties.end()) {
try {
*(search->second(m_pSelf.lock())) = CWindowOverridableVar(VARS[2].empty() ? true : (bool)std::stoi(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
}
break;
}
case CWindowRule::RULE_PERSISTENTSIZE: {
m_sWindowData.persistentSize = CWindowOverridableVar(true, PRIORITY_WINDOW_RULE);
break;
}
default: break;
}
}
@@ -890,7 +904,7 @@ void CWindow::createGroup() {
}
void CWindow::destroyGroup() {
if (m_sGroupData.pNextWindow.lock().get() == this) {
if (m_sGroupData.pNextWindow == m_pSelf) {
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;
@@ -972,7 +986,7 @@ PHLWINDOW CWindow::getGroupCurrent() {
int CWindow::getGroupSize() {
int size = 1;
PHLWINDOW curr = m_pSelf.lock();
while (curr->m_sGroupData.pNextWindow.lock().get() != this) {
while (curr->m_sGroupData.pNextWindow != m_pSelf) {
curr = curr->m_sGroupData.pNextWindow.lock();
size++;
}
@@ -1078,7 +1092,7 @@ void CWindow::insertWindowToGroup(PHLWINDOW pWindow) {
PHLWINDOW CWindow::getGroupPrevious() {
PHLWINDOW curr = m_sGroupData.pNextWindow.lock();
while (curr != m_pSelf.lock() && curr->m_sGroupData.pNextWindow.lock().get() != this)
while (curr != m_pSelf && curr->m_sGroupData.pNextWindow != m_pSelf)
curr = curr->m_sGroupData.pNextWindow.lock();
return curr;
@@ -1093,7 +1107,7 @@ void CWindow::switchWithWindowInGroup(PHLWINDOW pWindow) {
m_sGroupData.pNextWindow = pWindow->m_sGroupData.pNextWindow;
pWindow->m_sGroupData.pNextWindow = m_pSelf;
} else if (pWindow->m_sGroupData.pNextWindow.lock().get() == this) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
} else if (pWindow->m_sGroupData.pNextWindow == m_pSelf) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
pWindow->getGroupPrevious()->m_sGroupData.pNextWindow = m_pSelf;
pWindow->m_sGroupData.pNextWindow = m_sGroupData.pNextWindow;
m_sGroupData.pNextWindow = pWindow;
@@ -1244,7 +1258,7 @@ void CWindow::setAnimationsToMove() {
void CWindow::onWorkspaceAnimUpdate() {
// clip box for animated offsets
if (!m_bIsFloating || m_bPinned || isFullscreen()) {
if (!m_bIsFloating || m_bPinned || isFullscreen() || m_bDraggingTiled) {
m_vFloatingOffset = Vector2D(0, 0);
return;
}
@@ -1313,7 +1327,6 @@ void CWindow::clampWindowSize(const std::optional<Vector2D> minSize, const std::
*m_vRealPosition = m_vRealPosition->goal() + DELTA / 2.0;
*m_vRealSize = NEWSIZE;
sendWindowSize(NEWSIZE);
}
bool CWindow::isFullscreen() {
@@ -1523,21 +1536,21 @@ void CWindow::onResourceChangeX11() {
Debug::log(LOG, "xwayland window {:x} -> association to {:x}", (uintptr_t)m_pXWaylandSurface.get(), (uintptr_t)m_pWLSurface->resource().get());
}
void CWindow::onX11Configure(CBox box) {
void CWindow::onX11ConfigureRequest(CBox box) {
if (!m_pXWaylandSurface->surface || !m_pXWaylandSurface->mapped || !m_bIsMapped) {
m_pXWaylandSurface->configure(box);
m_vPendingReportedSize = box.size();
m_vReportedSize = box.size();
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR)
m_fX11SurfaceScaledBy = PMONITOR->scale;
m_vReportedPosition = box.pos();
updateX11SurfaceScale();
return;
}
g_pHyprRenderer->damageWindow(m_pSelf.lock());
if (!m_bIsFloating || isFullscreen() || g_pInputManager->currentlyDraggedWindow == m_pSelf) {
sendWindowSize(m_vRealSize->goal(), true);
sendWindowSize(true);
g_pInputManager->refocus();
g_pHyprRenderer->damageWindow(m_pSelf.lock());
return;
@@ -1548,27 +1561,20 @@ void CWindow::onX11Configure(CBox box) {
else
setHidden(true);
const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords(box.pos());
m_vRealPosition->setValueAndWarp(LOGICALPOS);
m_vRealSize->setValueAndWarp(box.size());
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR) {
m_vRealSize->setValueAndWarp(m_vRealSize->goal() / PMONITOR->scale);
m_fX11SurfaceScaledBy = PMONITOR->scale;
}
}
m_vRealPosition->setValueAndWarp(xwaylandPositionToReal(box.pos()));
m_vRealSize->setValueAndWarp(xwaylandSizeToReal(box.size()));
m_vPosition = m_vRealPosition->goal();
m_vSize = m_vRealSize->goal();
sendWindowSize(box.size(), true);
m_vPendingReportedSize = box.size();
m_vReportedSize = box.size();
if (m_vPendingReportedSize != box.size() || m_vReportedPosition != box.pos()) {
m_pXWaylandSurface->configure(box);
m_vReportedSize = box.size();
m_vPendingReportedSize = box.size();
m_vReportedPosition = box.pos();
}
updateX11SurfaceScale();
updateWindowDecos();
if (!m_pWorkspace || !m_pWorkspace->isVisible())
@@ -1624,17 +1630,17 @@ PHLWINDOW CWindow::getSwallower() {
if (!(*PSWALLOWREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return !RE2::FullMatch(other->m_szClass, *PSWALLOWREGEX); });
if (candidates.size() <= 0)
if (candidates.size() == 0)
return nullptr;
if (!(*PSWALLOWEXREGEX).empty())
std::erase_if(candidates, [&](const auto& other) { return RE2::FullMatch(other->m_szTitle, *PSWALLOWEXREGEX); });
if (candidates.size() <= 0)
if (candidates.size() == 0)
return nullptr;
if (candidates.size() == 1)
return candidates.at(0);
return candidates[0];
// walk up the focus history and find the last focused
for (auto const& w : g_pCompositor->m_vWindowFocusHistory) {
@@ -1646,17 +1652,17 @@ PHLWINDOW CWindow::getSwallower() {
}
// if none are found (??) then just return the first one
return candidates.at(0);
return candidates[0];
}
void CWindow::unsetWindowData(eOverridePriority priority) {
for (auto const& element : g_pConfigManager->mbWindowProperties) {
for (auto const& element : NWindowProperties::boolWindowProperties) {
element.second(m_pSelf.lock())->unset(priority);
}
for (auto const& element : g_pConfigManager->miWindowProperties) {
for (auto const& element : NWindowProperties::intWindowProperties) {
element.second(m_pSelf.lock())->unset(priority);
}
for (auto const& element : g_pConfigManager->mfWindowProperties) {
for (auto const& element : NWindowProperties::floatWindowProperties) {
element.second(m_pSelf.lock())->unset(priority);
}
}
@@ -1695,37 +1701,108 @@ Vector2D CWindow::requestedMaxSize() {
return maxSize;
}
void CWindow::sendWindowSize(Vector2D size, bool force, std::optional<Vector2D> overridePos) {
Vector2D CWindow::realToReportSize() {
if (!m_bIsX11)
return m_vRealSize->goal().clamp(Vector2D{0, 0}, Vector2D{std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()});
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
const auto REPORTSIZE = m_vRealSize->goal().clamp(Vector2D{1, 1}, Vector2D{std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()});
const auto PMONITOR = m_pMonitor.lock();
if (*PXWLFORCESCALEZERO && PMONITOR)
return REPORTSIZE * PMONITOR->scale;
return REPORTSIZE;
}
Vector2D CWindow::realToReportPosition() {
if (!m_bIsX11)
return m_vRealPosition->goal();
return g_pXWaylandManager->waylandToXWaylandCoords(m_vRealPosition->goal());
}
Vector2D CWindow::xwaylandSizeToReal(Vector2D size) {
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
const auto PMONITOR = m_pMonitor.lock();
const auto SIZE = size.clamp(Vector2D{1, 1}, Vector2D{std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()});
const auto SCALE = *PXWLFORCESCALEZERO ? PMONITOR->scale : 1.0f;
size = size.clamp(Vector2D{0, 0}, Vector2D{std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()});
return SIZE / SCALE;
}
// calculate pos
// TODO: this should be decoupled from setWindowSize IMO
Vector2D windowPos = overridePos.value_or(m_vRealPosition->goal());
Vector2D CWindow::xwaylandPositionToReal(Vector2D pos) {
return g_pXWaylandManager->xwaylandToWaylandCoords(pos);
}
if (m_bIsX11) {
if (const auto XWAYLANDPOS = g_pXWaylandManager->waylandToXWaylandCoords(windowPos); XWAYLANDPOS != Vector2D{})
windowPos = XWAYLANDPOS;
}
if (!force && m_vPendingReportedSize == size && (windowPos == m_vReportedPosition || !m_bIsX11))
return;
m_vReportedPosition = windowPos;
m_vPendingReportedSize = size;
void CWindow::updateX11SurfaceScale() {
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
m_fX11SurfaceScaledBy = 1.0f;
if (*PXWLFORCESCALEZERO && m_bIsX11 && PMONITOR) {
size *= PMONITOR->scale;
m_fX11SurfaceScaledBy = PMONITOR->scale;
if (m_bIsX11 && *PXWLFORCESCALEZERO) {
if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR)
m_fX11SurfaceScaledBy = PMONITOR->scale;
}
}
void CWindow::sendWindowSize(bool force) {
const auto PMONITOR = m_pMonitor.lock();
Debug::log(TRACE, "sendWindowSize: window:{:x},title:{} with real pos {}, real size {} (force: {})", (uintptr_t)this, this->m_szTitle, m_vRealPosition->goal(),
m_vRealSize->goal(), force);
// TODO: this should be decoupled from setWindowSize IMO
const auto REPORTPOS = realToReportPosition();
const auto REPORTSIZE = realToReportSize();
if (!force && m_vPendingReportedSize == REPORTSIZE && (m_vReportedPosition == REPORTPOS || !m_bIsX11))
return;
m_vReportedPosition = REPORTPOS;
m_vPendingReportedSize = REPORTSIZE;
updateX11SurfaceScale();
if (m_bIsX11 && m_pXWaylandSurface)
m_pXWaylandSurface->configure({windowPos, size});
m_pXWaylandSurface->configure({REPORTPOS, REPORTSIZE});
else if (m_pXDGSurface && m_pXDGSurface->toplevel)
m_vPendingSizeAcks.emplace_back(m_pXDGSurface->toplevel->setSize(size), size.floor());
m_vPendingSizeAcks.emplace_back(m_pXDGSurface->toplevel->setSize(REPORTSIZE), REPORTPOS.floor());
}
NContentType::eContentType CWindow::getContentType() {
if (!m_pWLSurface || !m_pWLSurface->resource() || !m_pWLSurface->resource()->contentType.valid())
return CONTENT_TYPE_NONE;
return m_pWLSurface->resource()->contentType->value;
}
void CWindow::setContentType(NContentType::eContentType contentType) {
if (!m_pWLSurface->resource()->contentType.valid())
m_pWLSurface->resource()->contentType = PROTO::contentType->getContentType(m_pWLSurface->resource());
// else disallow content type change if proto is used?
Debug::log(INFO, "ContentType for window {}", (int)contentType);
m_pWLSurface->resource()->contentType->value = contentType;
}
void CWindow::deactivateGroupMembers() {
auto curr = getGroupHead();
while (curr) {
if (curr != m_pSelf.lock()) {
if (curr->m_bIsX11)
curr->m_pXWaylandSurface->activate(false);
else if (curr->m_pXDGSurface && curr->m_pXDGSurface->toplevel)
curr->m_pXDGSurface->toplevel->setActive(false);
}
curr = curr->m_sGroupData.pNextWindow.lock();
if (curr == getGroupHead())
break;
}
}
bool CWindow::isNotResponding() {
return g_pANRManager->isNotResponding(m_pSelf.lock());
}

View File

@@ -19,6 +19,7 @@
#include "WLSurface.hpp"
#include "Workspace.hpp"
#include "WindowRule.hpp"
#include "../protocols/types/ContentType.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
@@ -197,6 +198,8 @@ struct SWindowData {
CWindowOverridableVar<CGradientValueData> activeBorderColor;
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
CWindowOverridableVar<bool> persistentSize;
};
struct SInitialWorkspaceToken {
@@ -354,7 +357,8 @@ class CWindow {
// swallowing
PHLWINDOWREF m_pSwallowed;
bool m_bGroupSwallowed = false;
bool m_bCurrentlySwallowed = false;
bool m_bGroupSwallowed = false;
// focus stuff
bool m_bStayFocused = false;
@@ -386,6 +390,9 @@ class CWindow {
// window tags
CTagKeeper m_tags;
// ANR
PHLANIMVAR<float> m_notRespondingTint;
// For the list lookup
bool operator==(const CWindow& rhs) const {
return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
@@ -393,85 +400,94 @@ class CWindow {
}
// methods
CBox getFullWindowBoundingBox();
SBoxExtents getFullWindowExtents();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(UP<IHyprWindowDecoration> deco);
void updateWindowDecos();
void removeWindowDeco(IHyprWindowDecoration* deco);
void uncacheWindowDecos();
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType);
void updateToplevel();
void updateSurfaceScaleTransformDetails(bool force = false);
void moveToWorkspace(PHLWORKSPACE);
PHLWINDOW x11TransientFor();
void onUnmap();
void onMap();
void setHidden(bool hidden);
bool isHidden();
void applyDynamicRule(const SP<CWindowRule>& r);
void updateDynamicRules();
SBoxExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
float rounding();
float roundingPower();
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(PHLMONITOR pMonitor);
WORKSPACEID workspaceID();
MONITORID monitorID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode);
int getRealBorderSize();
float getScrollMouse();
float getScrollTouchpad();
void updateWindowData();
void updateWindowData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
int popupsCount();
void applyGroupRules();
void createGroup();
void destroyGroup();
PHLWINDOW getGroupHead();
PHLWINDOW getGroupTail();
PHLWINDOW getGroupCurrent();
PHLWINDOW getGroupPrevious();
PHLWINDOW getGroupWindowByIndex(int);
int getGroupSize();
bool canBeGroupedInto(PHLWINDOW pWindow);
void setGroupCurrent(PHLWINDOW pWindow);
void insertWindowToGroup(PHLWINDOW pWindow);
void updateGroupOutputs();
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
void onFocusAnimUpdate();
void onUpdateState();
void onUpdateMeta();
void onX11Configure(CBox box);
void onResourceChangeX11();
std::string fetchTitle();
std::string fetchClass();
void warpCursor(bool force = false);
PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
Vector2D requestedMinSize();
Vector2D requestedMaxSize();
void sendWindowSize(Vector2D size, bool force = false, std::optional<Vector2D> overridePos = std::nullopt);
CBox getFullWindowBoundingBox();
SBoxExtents getFullWindowExtents();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(UP<IHyprWindowDecoration> deco);
void updateWindowDecos();
void removeWindowDeco(IHyprWindowDecoration* deco);
void uncacheWindowDecos();
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType);
void updateToplevel();
void updateSurfaceScaleTransformDetails(bool force = false);
void moveToWorkspace(PHLWORKSPACE);
PHLWINDOW x11TransientFor();
void onUnmap();
void onMap();
void setHidden(bool hidden);
bool isHidden();
void applyDynamicRule(const SP<CWindowRule>& r);
void updateDynamicRules();
SBoxExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
float rounding();
float roundingPower();
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(PHLMONITOR pMonitor);
WORKSPACEID workspaceID();
MONITORID monitorID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode);
int getRealBorderSize();
float getScrollMouse();
float getScrollTouchpad();
void updateWindowData();
void updateWindowData(const struct SWorkspaceRule&);
void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
int popupsCount();
void applyGroupRules();
void createGroup();
void destroyGroup();
PHLWINDOW getGroupHead();
PHLWINDOW getGroupTail();
PHLWINDOW getGroupCurrent();
PHLWINDOW getGroupPrevious();
PHLWINDOW getGroupWindowByIndex(int);
int getGroupSize();
bool canBeGroupedInto(PHLWINDOW pWindow);
void setGroupCurrent(PHLWINDOW pWindow);
void insertWindowToGroup(PHLWINDOW pWindow);
void updateGroupOutputs();
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
void onFocusAnimUpdate();
void onUpdateState();
void onUpdateMeta();
void onX11ConfigureRequest(CBox box);
void onResourceChangeX11();
std::string fetchTitle();
std::string fetchClass();
void warpCursor(bool force = false);
PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
Vector2D requestedMinSize();
Vector2D requestedMaxSize();
Vector2D realToReportSize();
Vector2D realToReportPosition();
Vector2D xwaylandSizeToReal(Vector2D size);
Vector2D xwaylandPositionToReal(Vector2D size);
void updateX11SurfaceScale();
void sendWindowSize(bool force = false);
NContentType::eContentType getContentType();
void setContentType(NContentType::eContentType contentType);
void deactivateGroupMembers();
bool isNotResponding();
CBox getWindowMainSurfaceBox() const {
CBox getWindowMainSurfaceBox() const {
return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y};
}
@@ -492,7 +508,7 @@ class CWindow {
CHyprSignalListener commit;
CHyprSignalListener destroy;
CHyprSignalListener activate;
CHyprSignalListener configure;
CHyprSignalListener configureRequest;
CHyprSignalListener setGeometry;
CHyprSignalListener updateState;
CHyprSignalListener updateMetadata;
@@ -526,6 +542,42 @@ inline bool validMapped(PHLWINDOWREF w) {
return w->m_bIsMapped;
}
namespace NWindowProperties {
static const std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> boolWindowProperties = {
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.allowsInput; }},
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.dimAround; }},
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.decorate; }},
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }},
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }},
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }},
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noAnim; }},
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBlur; }},
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBorder; }},
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noDim; }},
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noFocus; }},
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noRounding; }},
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShadow; }},
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.opaque; }},
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.RGBX; }},
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.tearing; }},
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.xray; }},
};
const std::unordered_map<std::string, std::function<CWindowOverridableVar<int>*(const PHLWINDOW&)>> intWindowProperties = {
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.rounding; }},
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.borderSize; }},
};
const std::unordered_map<std::string, std::function<CWindowOverridableVar<float>*(PHLWINDOW)>> floatWindowProperties = {
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.roundingPower; }},
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }},
};
};
/**
format specification
- 'x', only address, equivalent of (uintpr_t)CWindow*

View File

@@ -5,19 +5,20 @@
#include "../config/ConfigManager.hpp"
static const auto RULES = std::unordered_set<std::string>{
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused",
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused", "persistentsize",
};
static const auto RULES_PREFIX = std::unordered_set<std::string>{
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", "opacity",
"plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size", "suppressevent", "tag", "workspace", "xray",
"animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize",
"monitor", "move", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad",
"size", "suppressevent", "tag", "workspace", "xray",
};
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
const auto VALS = CVarList(rule, 2, ' ');
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
(g_pConfigManager->mbWindowProperties.find(VALS[0]) != g_pConfigManager->mbWindowProperties.end()) ||
(g_pConfigManager->miWindowProperties.find(VALS[0]) != g_pConfigManager->miWindowProperties.end()) ||
(g_pConfigManager->mfWindowProperties.find(VALS[0]) != g_pConfigManager->mfWindowProperties.end());
(NWindowProperties::boolWindowProperties.find(VALS[0]) != NWindowProperties::boolWindowProperties.end()) ||
(NWindowProperties::intWindowProperties.find(VALS[0]) != NWindowProperties::intWindowProperties.end()) ||
(NWindowProperties::floatWindowProperties.find(VALS[0]) != NWindowProperties::floatWindowProperties.end());
if (!VALID)
return;
@@ -38,6 +39,8 @@ CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool
ruleType = RULE_TILE;
else if (rule == "renderunfocused")
ruleType = RULE_RENDERUNFOCUSED;
else if (rule == "persistentsize")
ruleType = RULE_PERSISTENTSIZE;
else if (rule.starts_with("animation"))
ruleType = RULE_ANIMATION;
else if (rule.starts_with("bordercolor"))
@@ -74,12 +77,14 @@ CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool
ruleType = RULE_WORKSPACE;
else if (rule.starts_with("prop"))
ruleType = RULE_PROP;
else if (rule.starts_with("content"))
ruleType = RULE_CONTENT;
else {
// check if this is a prop.
const CVarList VARS(rule, 0, 's', true);
if (g_pConfigManager->miWindowProperties.find(VARS[0]) != g_pConfigManager->miWindowProperties.end() ||
g_pConfigManager->mbWindowProperties.find(VARS[0]) != g_pConfigManager->mbWindowProperties.end() ||
g_pConfigManager->mfWindowProperties.find(VARS[0]) != g_pConfigManager->mfWindowProperties.end()) {
if (NWindowProperties::intWindowProperties.find(VARS[0]) != NWindowProperties::intWindowProperties.end() ||
NWindowProperties::boolWindowProperties.find(VARS[0]) != NWindowProperties::boolWindowProperties.end() ||
NWindowProperties::floatWindowProperties.find(VARS[0]) != NWindowProperties::floatWindowProperties.end()) {
*const_cast<std::string*>(&szRule) = "prop " + rule;
ruleType = RULE_PROP;
Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, szRule);

View File

@@ -36,6 +36,8 @@ class CWindowRule {
RULE_TAG,
RULE_WORKSPACE,
RULE_PROP,
RULE_CONTENT,
RULE_PERSISTENTSIZE,
};
eRuleType ruleType = RULE_INVALID;
@@ -58,6 +60,7 @@ class CWindowRule {
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
std::string szContentType = ""; // empty means any
// precompiled regexes
CRuleRegexContainer rTitle;

View File

@@ -261,6 +261,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
// n - named: n[true] or n[s:string] or n[e:string]
// m - monitor: m[monitor_selector]
// w - windowCount: w[1-4] or w[1], optional flag t or f for tiled or floating and
// flag p to count only pinned windows, e.g. w[p1-2], w[pg4]
// flag g to count groups instead of windows, e.g. w[t1-2], w[fg4]
// flag v will count only visible windows
// f - fullscreen state : f[-1], f[0], f[1], or f[2] for different fullscreen states
@@ -370,6 +371,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
prop = prop.substr(2, prop.length() - 3);
int wantsOnlyTiled = -1;
int wantsOnlyPinned = false;
bool wantsCountGroup = false;
bool wantsCountVisible = false;
@@ -381,6 +383,9 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
} else if (flag == 'f' && wantsOnlyTiled == -1) {
wantsOnlyTiled = 0;
flagCount++;
} else if (flag == 'p' && !wantsOnlyPinned) {
wantsOnlyPinned = true;
flagCount++;
} else if (flag == 'g' && !wantsCountGroup) {
wantsCountGroup = true;
flagCount++;
@@ -411,9 +416,11 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
int count;
if (wantsCountGroup)
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
else
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
if (count != from)
@@ -444,10 +451,12 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
WORKSPACEID count;
if (wantsCountGroup)
count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
count =
getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
else
count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsOnlyPinned ? std::optional<bool>(wantsOnlyPinned) : std::nullopt,
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
if (std::clamp(count, from, to) != count)
@@ -535,13 +544,15 @@ bool CWorkspace::isVisibleNotCovered() {
return PMONITOR->activeWorkspace->m_iID == m_iID;
}
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
int no = 0;
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
continue;
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
continue;
if (onlyPinned.has_value() && w->m_bPinned != onlyPinned.value())
continue;
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
continue;
no++;
@@ -550,7 +561,7 @@ int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> on
return no;
}
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyVisible) {
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
int no = 0;
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->workspaceID() != m_iID || !w->m_bIsMapped)
@@ -559,6 +570,8 @@ int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onl
continue;
if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value())
continue;
if (onlyPinned.has_value() && w->m_bPinned != onlyPinned.value())
continue;
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
continue;
no++;
@@ -624,7 +637,7 @@ void CWorkspace::forceReportSizesToWindows() {
if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden())
continue;
w->sendWindowSize(w->m_vRealSize->goal(), true);
w->sendWindowSize(true);
}
}
@@ -635,6 +648,12 @@ void CWorkspace::rename(const std::string& name) {
Debug::log(LOG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_iID, name);
m_szName = name;
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_pSelf.lock());
m_bPersistent = WORKSPACERULE.isPersistent;
if (WORKSPACERULE.isPersistent)
g_pCompositor->ensurePersistentWorkspacesPresent(std::vector<SWorkspaceRule>{WORKSPACERULE}, m_pSelf.lock());
g_pEventManager->postEvent({"renameworkspace", std::to_string(m_iID) + "," + m_szName});
}

View File

@@ -72,8 +72,8 @@ class CWorkspace {
SWorkspaceIDName getPrevWorkspaceIDName() const;
void updateWindowDecos();
void updateWindowData();
int getWindows(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroups(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getWindows(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyPinned = {}, std::optional<bool> onlyVisible = {});
int getGroups(std::optional<bool> onlyTiled = {}, std::optional<bool> onlyPinned = {}, std::optional<bool> onlyVisible = {});
bool hasUrgentWindow();
PHLWINDOW getFirstWindow();
PHLWINDOW getTopLeftWindow();

View File

@@ -8,6 +8,8 @@
#include <aquamarine/input/Input.hpp>
#include <cstring>
using namespace Hyprutils::OS;
#define LED_COUNT 3
constexpr static std::array<const char*, 8> MODNAMES = {
@@ -41,13 +43,14 @@ void IKeyboard::clearManuallyAllocd() {
if (xkbKeymap)
xkb_keymap_unref(xkbKeymap);
if (xkbKeymapFD >= 0)
close(xkbKeymapFD);
if (xkbSymState)
xkb_state_unref(xkbSymState);
xkbSymState = nullptr;
xkbKeymap = nullptr;
xkbState = nullptr;
xkbStaticState = nullptr;
xkbKeymapFD = -1;
xkbKeymapFD.reset();
}
void IKeyboard::setKeymap(const SStringRuleNames& rules) {
@@ -123,14 +126,14 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
updateModifiers(0, 0, modifiersState.locked, modifiersState.group);
}
for (size_t i = 0; i < LEDNAMES.size(); ++i) {
ledIndexes.at(i) = xkb_map_led_get_index(xkbKeymap, LEDNAMES.at(i));
Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES.at(i), ledIndexes.at(i));
for (size_t i = 0; i < std::min(LEDNAMES.size(), ledIndexes.size()); ++i) {
ledIndexes[i] = xkb_map_led_get_index(xkbKeymap, LEDNAMES[i]);
Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES[i], ledIndexes[i]);
}
for (size_t i = 0; i < MODNAMES.size(); ++i) {
modIndexes.at(i) = xkb_map_mod_get_index(xkbKeymap, MODNAMES.at(i));
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES.at(i), modIndexes.at(i));
for (size_t i = 0; i < std::min(MODNAMES.size(), modIndexes.size()); ++i) {
modIndexes[i] = xkb_map_mod_get_index(xkbKeymap, MODNAMES[i]);
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES[i], modIndexes[i]);
}
updateKeymapFD();
@@ -143,31 +146,30 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
void IKeyboard::updateKeymapFD() {
Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName);
if (xkbKeymapFD >= 0)
close(xkbKeymapFD);
xkbKeymapFD = -1;
if (xkbKeymapFD.isValid())
xkbKeymapFD.reset();
auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
xkbKeymapString = cKeymapStr;
free(cKeymapStr);
int rw, ro;
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, &rw, &ro))
CFileDescriptor rw, ro;
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, rw, ro))
Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap");
else {
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw, 0);
close(rw);
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw.get(), 0);
rw.reset();
if (keymapFDDest == MAP_FAILED) {
Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap");
close(ro);
ro.reset();
} else {
memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length());
munmap(keymapFDDest, xkbKeymapString.length() + 1);
xkbKeymapFD = ro;
xkbKeymapFD = std::move(ro);
}
}
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD);
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD.get());
}
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
@@ -287,8 +289,8 @@ std::optional<uint32_t> IKeyboard::getLEDs() {
return {};
uint32_t leds = 0;
for (uint32_t i = 0; i < LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(xkbState, ledIndexes.at(i)))
for (uint32_t i = 0; i < std::min((size_t)LED_COUNT, ledIndexes.size()); ++i) {
if (xkb_state_led_index_is_active(xkbState, ledIndexes[i]))
leds |= (1 << i);
}
@@ -321,10 +323,10 @@ uint32_t IKeyboard::getModifiers() {
uint32_t modMask = modifiersState.depressed | modifiersState.latched;
uint32_t mods = 0;
for (size_t i = 0; i < modIndexes.size(); ++i) {
if (modIndexes.at(i) == XKB_MOD_INVALID)
if (modIndexes[i] == XKB_MOD_INVALID)
continue;
if (!(modMask & (1 << modIndexes.at(i))))
if (!(modMask & (1 << modIndexes[i])))
continue;
mods |= (1 << i);

View File

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

View File

@@ -17,9 +17,10 @@ class IPointer : public IHID {
virtual SP<Aquamarine::IPointer> aq() = 0;
struct SMotionEvent {
uint32_t timeMs = 0;
Vector2D delta, unaccel;
bool mouse = false;
uint32_t timeMs = 0;
Vector2D delta, unaccel;
bool mouse = false;
SP<IPointer> device;
};
struct SMotionAbsoluteEvent {
@@ -109,6 +110,9 @@ class IPointer : public IHID {
bool connected = false; // means connected to the cursor
std::string boundOutput = "";
bool flipX = false; // decide to invert horizontal movement
bool flipY = false; // decide to invert vertical movement
bool isTouchpad = false;
WP<IPointer> self;
};

View File

@@ -14,6 +14,11 @@ CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
if (!mouse)
return;
if (auto handle = mouse->getLibinputHandle()) {
double w = 0, h = 0;
isTouchpad = libinput_device_has_capability(handle, LIBINPUT_DEVICE_CAP_POINTER) && libinput_device_get_size(handle, &w, &h) == 0;
}
listeners.destroy = mouse->events.destroy.registerListener([this](std::any d) {
mouse.reset();
events.destroy.emit();
@@ -27,6 +32,7 @@ CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
.delta = E.delta,
.unaccel = E.unaccel,
.mouse = true,
.device = self.lock(),
});
});

View File

@@ -19,7 +19,11 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
events.destroy.emit();
});
listeners.motion = pointer->events.move.registerListener([this](std::any d) { pointerEvents.motion.emit(d); });
listeners.motion = pointer->events.move.registerListener([this](std::any d) {
auto E = std::any_cast<SMotionEvent>(d);
E.device = self.lock();
pointerEvents.motion.emit(E);
});
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) {
// we need to unpack the event and add our device here because it's required to calculate the position correctly
auto E = std::any_cast<SMotionAbsoluteEvent>(d);

View File

@@ -11,11 +11,11 @@
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../protocols/types/ContentType.hpp"
#include "../xwayland/XSurface.hpp"
#include "managers/AnimationManager.hpp"
#include "managers/PointerManager.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/AnimationManager.hpp"
@@ -307,6 +307,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
break;
}
case CWindowRule::RULE_CONTENT: {
const CVarList VARS(r->szRule, 0, ' ');
try {
PWINDOW->setContentType(NContentType::fromString(VARS[1]));
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
break;
}
default: break;
}
@@ -342,7 +349,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (!workspaceSilent) {
if (pWorkspace->m_bIsSpecialWorkspace)
pWorkspace->m_pMonitor->setSpecialWorkspace(pWorkspace);
else if (PMONITOR->activeWorkspaceID() != REQUESTEDWORKSPACEID)
else if (PMONITOR->activeWorkspaceID() != REQUESTEDWORKSPACEID && !PWINDOW->m_bNoInitialFocus)
g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspaceName);
PMONITOR = g_pCompositor->m_pLastMonitor.lock();
@@ -385,6 +392,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
// Verify window swallowing. Get the swallower before calling onWindowCreated(PWINDOW) because getSwallower() wouldn't get it after if PWINDOW gets auto grouped.
const auto SWALLOWER = PWINDOW->getSwallower();
PWINDOW->m_pSwallowed = SWALLOWER;
if (PWINDOW->m_pSwallowed)
PWINDOW->m_pSwallowed->m_bCurrentlySwallowed = true;
if (PWINDOW->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
@@ -679,13 +688,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PMONITOR && PWINDOW->isX11OverrideRedirect())
PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale;
// Fix some X11 popups being invisible / having incorrect size on open.
// What the ACTUAL FUCK is going on?????? I HATE X11
if (!PWINDOW->isX11OverrideRedirect() && PWINDOW->m_bIsX11 && PWINDOW->m_bIsFloating) {
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true, PWINDOW->m_vRealPosition->goal() - Vector2D{1, 1});
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true);
}
}
void Events::listener_unmapWindow(void* owner, void* data) {
@@ -714,6 +716,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
if (PWINDOW->m_bIsFloating && !PWINDOW->m_bIsX11 &&
std::any_of(PWINDOW->m_vMatchedRules.begin(), PWINDOW->m_vMatchedRules.end(), [](const auto& r) { return r->ruleType == CWindowRule::RULE_PERSISTENTSIZE; })) {
Debug::log(LOG, "storing floating size {}x{} for window {}::{} on close", PWINDOW->m_vRealSize->value().x, PWINDOW->m_vRealSize->value().y, PWINDOW->m_szClass,
PWINDOW->m_szTitle);
g_pConfigManager->storeFloatingSize(PWINDOW, PWINDOW->m_vRealSize->value());
}
PROTO::toplevelExport->onWindowUnmap(PWINDOW);
if (PWINDOW->isFullscreen())
@@ -724,12 +733,15 @@ void Events::listener_unmapWindow(void* owner, void* data) {
// swallowing
if (valid(PWINDOW->m_pSwallowed)) {
PWINDOW->m_pSwallowed->setHidden(false);
if (PWINDOW->m_pSwallowed->m_bCurrentlySwallowed) {
PWINDOW->m_pSwallowed->m_bCurrentlySwallowed = false;
PWINDOW->m_pSwallowed->setHidden(false);
if (PWINDOW->m_sGroupData.pNextWindow.lock())
PWINDOW->m_pSwallowed->m_bGroupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false.
if (PWINDOW->m_sGroupData.pNextWindow.lock())
PWINDOW->m_pSwallowed->m_bGroupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false.
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed.lock());
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed.lock());
}
PWINDOW->m_pSwallowed->m_bGroupSwallowed = false;
PWINDOW->m_pSwallowed.reset();
@@ -868,7 +880,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
// tearing: if solitary, redraw it. This still might be a single surface window
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.accumulateBufferDamage()};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {
@@ -952,7 +964,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->setHidden(true);
if (PWINDOW->isFullscreen() || !PWINDOW->m_bIsFloating) {
PWINDOW->sendWindowSize(PWINDOW->m_vRealSize->goal(), true);
PWINDOW->sendWindowSize(true);
g_pHyprRenderer->damageWindow(PWINDOW);
return;
}

View File

@@ -49,97 +49,6 @@ using namespace Hyprutils::OS;
#endif
#endif
static const float transforms[][9] = {
{
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
0.0f,
1.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
-1.0f,
0.0f,
0.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
0.0f,
-1.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
0.0f,
1.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
1.0f,
0.0f,
0.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
{
0.0f,
-1.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
},
};
std::string absolutePath(const std::string& rawpath, const std::string& currentPath) {
auto value = rawpath;
@@ -836,50 +745,48 @@ bool envEnabled(const std::string& env) {
return std::string(ENV) == "1";
}
std::pair<int, std::string> openExclusiveShm() {
std::pair<CFileDescriptor, std::string> openExclusiveShm() {
// Only absolute paths can be shared across different shm_open() calls
std::string name = "/" + g_pTokenManager->getRandomUUID();
for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
return {fd, name};
CFileDescriptor fd{shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)};
if (fd.isValid())
return {std::move(fd), name};
}
return {-1, ""};
return {{}, ""};
}
int allocateSHMFile(size_t len) {
CFileDescriptor allocateSHMFile(size_t len) {
auto [fd, name] = openExclusiveShm();
if (fd < 0)
return -1;
if (!fd.isValid())
return {};
shm_unlink(name.c_str());
int ret;
do {
ret = ftruncate(fd, len);
ret = ftruncate(fd.get(), len);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
return {};
}
return fd;
return std::move(fd);
}
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
bool allocateSHMFilePair(size_t size, CFileDescriptor& rw_fd_ptr, CFileDescriptor& ro_fd_ptr) {
auto [fd, name] = openExclusiveShm();
if (fd < 0) {
if (!fd.isValid()) {
return false;
}
// CLOEXEC is guaranteed to be set by shm_open
int ro_fd = shm_open(name.c_str(), O_RDONLY, 0);
if (ro_fd < 0) {
CFileDescriptor ro_fd{shm_open(name.c_str(), O_RDONLY, 0)};
if (!ro_fd.isValid()) {
shm_unlink(name.c_str());
close(fd);
return false;
}
@@ -887,24 +794,20 @@ bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) {
// Make sure the file cannot be re-opened in read-write mode (e.g. via
// "/proc/self/fd/" on Linux)
if (fchmod(fd, 0) != 0) {
close(fd);
close(ro_fd);
if (fchmod(fd.get(), 0) != 0) {
return false;
}
int ret;
do {
ret = ftruncate(fd, size);
ret = ftruncate(fd.get(), size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
close(ro_fd);
return false;
}
*rw_fd_ptr = fd;
*ro_fd_ptr = ro_fd;
rw_fd_ptr = std::move(fd);
ro_fd_ptr = std::move(ro_fd);
return true;
}

View File

@@ -5,6 +5,7 @@
#include <vector>
#include <format>
#include <expected>
#include <hyprutils/os/FileDescriptor.hpp>
#include "../SharedDefs.hpp"
#include "../macros.hpp"
@@ -35,8 +36,8 @@ double normalizeAngleRad(double ang);
std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err);
bool envEnabled(const std::string& env);
int allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr);
Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len);
bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr);
float stringToPercentage(const std::string& VALUE, const float REL);
template <typename... Args>

View File

@@ -2,6 +2,7 @@
#include "MiscFunctions.hpp"
#include "../macros.hpp"
#include "math/Math.hpp"
#include "../protocols/ColorManagement.hpp"
#include "sync/SyncReleaser.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
@@ -32,6 +33,8 @@
#include <ranges>
using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
using namespace Hyprutils::OS;
using enum NContentType::eContentType;
static int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor(((CMonitor*)data)->self.lock());
@@ -54,8 +57,7 @@ void CMonitor::onConnect(bool noRule) {
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
}
listeners.frame = output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); });
@@ -410,7 +412,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
if (!force && DELTALESSTHAN(vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(vecPixelSize.y, RULE->resolution.y, 1) &&
DELTALESSTHAN(refreshRate, RULE->refreshRate, 1) && setScale == RULE->scale &&
((DELTALESSTHAN(vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(vecPosition.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) &&
transform == RULE->transform && RULE->enable10bit == enabled10bit && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
transform == RULE->transform && RULE->enable10bit == enabled10bit && RULE->cmType == cmType && RULE->sdrSaturation == sdrSaturation &&
RULE->sdrBrightness == sdrBrightness && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", szName);
@@ -434,7 +437,7 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
// accumulate requested modes in reverse order (cause inesrting at front is inefficient)
std::vector<SP<Aquamarine::SOutputMode>> requestedModes;
std::string requestedStr = "preferred";
std::string requestedStr = "unknown";
// use sortFunc, add best 3 to requestedModes in reverse, since we test in reverse
auto addBest3Modes = [&](auto const& sortFunc) {
@@ -445,20 +448,31 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
requestedModes.insert(requestedModes.end(), sortedModes.rbegin(), sortedModes.rend());
};
// last fallback is preferred mode, btw this covers resolution == Vector2D()
// last fallback is always preferred mode
if (!output->preferredMode())
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE", output->name);
else
requestedModes.push_back(output->preferredMode());
if (RULE->resolution == Vector2D(-1, -1)) {
if (RULE->resolution == Vector2D()) {
requestedStr = "preferred";
// fallback to first 3 modes if preferred fails/doesn't exist
requestedModes = output->modes;
if (requestedModes.size() > 3)
requestedModes.erase(requestedModes.begin() + 3, requestedModes.end());
std::ranges::reverse(requestedModes.begin(), requestedModes.end());
if (output->preferredMode())
requestedModes.push_back(output->preferredMode());
} else if (RULE->resolution == Vector2D(-1, -1)) {
requestedStr = "highrr";
// sort prioritizing refresh rate 1st and resolution 2nd, then add best 3
addBest3Modes([](auto const& a, auto const& b) {
if (a->refreshRate > b->refreshRate)
if (std::round(a->refreshRate) > std::round(b->refreshRate))
return true;
else if (DELTALESSTHAN((float)a->refreshRate, (float)b->refreshRate, 1000) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
else if (DELTALESSTHAN((float)a->refreshRate, (float)b->refreshRate, 1.F) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
return true;
return false;
});
@@ -469,7 +483,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
addBest3Modes([](auto const& a, auto const& b) {
if (a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
return true;
else if (DELTALESSTHAN(a->pixelSize.x, b->pixelSize.x, 1) && DELTALESSTHAN(a->pixelSize.y, b->pixelSize.y, 1) && a->refreshRate > b->refreshRate)
else if (DELTALESSTHAN(a->pixelSize.x, b->pixelSize.x, 1) && DELTALESSTHAN(a->pixelSize.y, b->pixelSize.y, 1) &&
std::round(a->refreshRate) > std::round(b->refreshRate))
return true;
return false;
});
@@ -656,6 +671,66 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
enabled10bit = set10bit;
auto oldImageDescription = imageDescription;
cmType = RULE->cmType;
switch (cmType) {
case CM_AUTO: cmType = enabled10bit && output->parsedEDID.supportsBT2020 ? CM_WIDE : CM_SRGB; break;
case CM_EDID: cmType = output->parsedEDID.chromaticityCoords.has_value() ? CM_EDID : CM_SRGB; break;
case CM_HDR:
case CM_HDR_EDID:
cmType = output->parsedEDID.supportsBT2020 && output->parsedEDID.hdrMetadata.has_value() && output->parsedEDID.hdrMetadata->supportsPQ ? cmType : CM_SRGB;
break;
default: break;
}
switch (cmType) {
case CM_SRGB: imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB
case CM_WIDE:
imageDescription = {.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)};
break;
case CM_EDID:
imageDescription = {.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = {
.red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y},
}};
break;
case CM_HDR:
imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = 0, .max = 10000, .reference = 203}};
break;
case CM_HDR_EDID:
imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = output->parsedEDID.chromaticityCoords.has_value() ?
NColorManagement::SPCPRimaries{
.red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y},
} :
NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}};
break;
default: UNREACHABLE();
}
if (oldImageDescription != imageDescription)
PROTO::colorManagement->onMonitorImageDescriptionChanged(self);
sdrSaturation = RULE->sdrSaturation;
sdrBrightness = RULE->sdrBrightness;
Vector2D logicalSize = vecPixelSize / scale;
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
// invalid scale, will produce fractional pixels.
@@ -786,8 +861,8 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr
bool shouldSkip =
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
const bool shouldSkip = activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN &&
(*PNOBREAK == 1 || (*PNOBREAK == 2 && activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && output->state->state().adaptiveSync;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) {
@@ -1005,15 +1080,17 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
if (pWorkspace == activeWorkspace)
return;
const auto POLDWORKSPACE = activeWorkspace;
POLDWORKSPACE->m_bVisible = false;
pWorkspace->m_bVisible = true;
const auto POLDWORKSPACE = activeWorkspace;
if (POLDWORKSPACE)
POLDWORKSPACE->m_bVisible = false;
pWorkspace->m_bVisible = true;
activeWorkspace = pWorkspace;
if (!internal) {
const auto ANIMTOLEFT = pWorkspace->m_iID > POLDWORKSPACE->m_iID;
POLDWORKSPACE->startAnim(false, ANIMTOLEFT);
const auto ANIMTOLEFT = POLDWORKSPACE && pWorkspace->m_iID > POLDWORKSPACE->m_iID;
if (POLDWORKSPACE)
POLDWORKSPACE->startAnim(false, ANIMTOLEFT);
pWorkspace->startAnim(true, ANIMTOLEFT);
// move pinned windows
@@ -1079,6 +1156,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
activeSpecialWorkspace->m_bVisible = false;
activeSpecialWorkspace->startAnim(false, false);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + szName});
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + szName});
}
activeSpecialWorkspace.reset();
@@ -1108,12 +1186,13 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
bool animate = true;
//close if open elsewhere
const auto PMONITORWORKSPACEOWNER = pWorkspace->m_pMonitor.lock();
if (PMONITORWORKSPACEOWNER->activeSpecialWorkspace == pWorkspace) {
PMONITORWORKSPACEOWNER->activeSpecialWorkspace.reset();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITORWORKSPACEOWNER->ID);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMONITORWORKSPACEOWNER->szName});
if (const auto PMWSOWNER = pWorkspace->m_pMonitor.lock(); PMWSOWNER && PMWSOWNER->activeSpecialWorkspace == pWorkspace) {
PMWSOWNER->activeSpecialWorkspace.reset();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMWSOWNER->ID);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->szName});
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->szName});
const auto PACTIVEWORKSPACE = PMONITORWORKSPACEOWNER->activeWorkspace;
const auto PACTIVEWORKSPACE = PMWSOWNER->activeWorkspace;
g_pCompositor->updateFullscreenFadeOnWorkspace(PACTIVEWORKSPACE);
animate = false;
@@ -1160,6 +1239,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
}
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", std::to_string(pWorkspace->m_iID) + "," + pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(self.lock());
@@ -1261,23 +1341,43 @@ bool CMonitor::attemptDirectScanout() {
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
if (!PSURFACE || !PSURFACE->current.texture || !PSURFACE->current.buffer)
return false;
if (PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
return false;
// we can't scanout shm buffers.
if (!PSURFACE->current.buffer || !PSURFACE->current.buffer->buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
if (!params.success || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
return false;
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt", (uintptr_t)PSURFACE.get());
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get());
auto PBUFFER = PSURFACE->current.buffer->buffer;
if (PBUFFER == output->state->state().buffer) {
if (scanoutNeedsCursorUpdate) {
if (!state.test()) {
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
return false;
}
if (!output->commit()) {
Debug::log(TRACE, "attemptDirectScanout: failed to commit cursor update");
lastScanout.reset();
return false;
}
scanoutNeedsCursorUpdate = false;
}
return true;
}
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
// scanout buffer isn't dmabuf, so no scanout
if (!params.success)
return false;
// entering into scanout, so save monitor format
if (lastScanout.expired())
prevDrmFormat = drmFormat;
@@ -1287,7 +1387,7 @@ bool CMonitor::attemptDirectScanout() {
drmFormat = params.format;
}
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
output->state->setBuffer(PBUFFER);
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
@@ -1296,34 +1396,28 @@ bool CMonitor::attemptDirectScanout() {
return false;
}
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings();
// wait for the explicit fence if present, and if kms explicit is allowed
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled;
int explicitWaitFD = -1;
if (DOEXPLICIT) {
explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
if (explicitWaitFD < 0)
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd");
}
DOEXPLICIT = DOEXPLICIT && explicitWaitFD >= 0;
auto cleanup = CScopeGuard([explicitWaitFD, this]() {
output->state->resetExplicitFences();
if (explicitWaitFD >= 0)
close(explicitWaitFD);
});
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
PSURFACE->presentFeedback(&now, self.lock());
output->state->addDamage(CBox{{}, vecPixelSize});
output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
output->state->resetExplicitFences();
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.buffer->acquire && explicitOptions.explicitKMSEnabled;
if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD);
output->state->setExplicitInFence(explicitWaitFD);
// wait for surface's explicit fence if present
inFence = PSURFACE->current.buffer->acquire->exportAsFD();
if (inFence.isValid()) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get());
output->state->setExplicitInFence(inFence.get());
} else {
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an sync file fd for aq IN_FENCE");
DOEXPLICIT = false;
}
}
bool ok = output->commit();
@@ -1335,29 +1429,27 @@ bool CMonitor::attemptDirectScanout() {
ok = output->commit();
}
if (ok) {
if (lastScanout.expired()) {
lastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
// delay explicit sync feedback until kms release of the buffer
if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: Delaying explicit sync release feedback until kms release");
PSURFACE->current.buffer->releaser->drop();
PSURFACE->current.buffer->buffer->hlEvents.backendRelease2 = PSURFACE->current.buffer->buffer->events.backendRelease.registerListener([PSURFACE](std::any d) {
const bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.releaseTimeline && PSURFACE->syncobj->current.releaseTimeline->timeline;
if (DOEXPLICIT)
PSURFACE->syncobj->current.releaseTimeline->timeline->signal(PSURFACE->syncobj->current.releasePoint);
});
}
} else {
if (!ok) {
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
lastScanout.reset();
return false;
}
if (lastScanout.expired()) {
lastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
scanoutNeedsCursorUpdate = false;
if (!PBUFFER->lockedByBackend || PBUFFER->hlEvents.backendRelease)
return true;
// lock buffer while DRM/KMS is using it, then release it when page flip happens since DRM/KMS should be done by then
// btw buffer's syncReleaser will take care of signaling release point, so we don't do that here
PBUFFER->lock();
PBUFFER->onBackendRelease([PBUFFER]() { PBUFFER->unlock(); });
return true;
}
@@ -1429,11 +1521,25 @@ void CMonitor::onMonitorFrame() {
g_pHyprRenderer->renderMonitor(self.lock());
}
CMonitorState::CMonitorState(CMonitor* owner) : m_pOwner(owner) {
;
void CMonitor::onCursorMovedOnMonitor() {
if (!tearingState.activelyTearing || !solitaryClient || !g_pHyprRenderer->shouldRenderCursor())
return;
// submit a frame immediately. This will only update the cursor pos.
// output->state->setBuffer(output->state->state().buffer);
// output->state->addDamage(CRegion{});
// output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE);
// if (!output->commit())
// Debug::log(ERR, "onCursorMovedOnMonitor: tearing and wanted to update cursor, failed.");
// FIXME: try to do the above. We currently can't just render because drm is a fucking bitch
// and throws a "nO pRoP cAn Be ChAnGeD dUrInG AsYnC fLiP" on crtc_x
// this will throw too but fix it if we use sw cursors
tearingState.frameScheduledWhileBusy = true;
}
CMonitorState::~CMonitorState() {
CMonitorState::CMonitorState(CMonitor* owner) : m_pOwner(owner) {
;
}

View File

@@ -6,17 +6,18 @@
#include "../SharedDefs.hpp"
#include "MiscFunctions.hpp"
#include "WLClasses.hpp"
#include <vector>
#include <array>
#include <xf86drmMode.h>
#include "Timer.hpp"
#include "math/Math.hpp"
#include <optional>
#include "../protocols/types/ColorManagement.hpp"
#include "signal/Signal.hpp"
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
// Enum for the different types of auto directions, e.g. auto-left, auto-up.
enum eAutoDirs : uint8_t {
@@ -27,28 +28,41 @@ enum eAutoDirs : uint8_t {
DIR_AUTO_RIGHT
};
enum eCMType : uint8_t {
CM_AUTO = 0, // subject to change. srgb for 8bpc, wide for 10bpc if supported
CM_SRGB, // default, sRGB primaries
CM_WIDE, // wide color gamut, BT2020 primaries
CM_EDID, // primaries from edid (known to be inaccurate)
CM_HDR, // wide color gamut and HDR PQ transfer function
CM_HDR_EDID, // same as CM_HDR with edid primaries
};
struct SMonitorRule {
eAutoDirs autoDir = DIR_AUTO_NONE;
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 = {};
eAutoDirs autoDir = DIR_AUTO_NONE;
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60; // Hz
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
eCMType cmType = CM_SRGB;
float sdrSaturation = 1.0f; // SDR -> HDR
float sdrBrightness = 1.0f; // SDR -> HDR
drmModeModeInfo drmMode = {};
std::optional<int> vrr;
};
class CMonitor;
class CSyncTimeline;
class CEGLSync;
class CMonitorState {
public:
CMonitorState(CMonitor* owner);
~CMonitorState();
~CMonitorState() = default;
bool commit();
bool test();
@@ -92,7 +106,7 @@ class CMonitor {
CDamageRing damage;
SP<Aquamarine::IOutput> output;
float refreshRate = 60;
float refreshRate = 60; // Hz
int forceFullFrames = 0;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
@@ -107,6 +121,9 @@ class CMonitor {
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
eCMType cmType = CM_SRGB;
float sdrSaturation = 1.0f;
float sdrBrightness = 1.0f;
bool createdByUser = false;
bool isUnsafeFallback = false;
@@ -122,11 +139,12 @@ class CMonitor {
SMonitorRule activeMonitorRule;
// explicit sync
SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline;
uint64_t commitSeq = 0;
SP<CSyncTimeline> inTimeline;
Hyprutils::OS::CFileDescriptor inFence;
SP<CEGLSync> eglSync;
uint64_t inTimelinePoint = 0;
PHLMONITORREF self;
PHLMONITORREF self;
// mirroring
PHLMONITORREF pMirrorOf;
@@ -141,6 +159,7 @@ class CMonitor {
// for direct scanout
PHLWINDOWREF lastScanout;
bool scanoutNeedsCursorUpdate = false;
struct {
bool canTear = false;
@@ -162,37 +181,39 @@ class CMonitor {
std::array<std::vector<PHLLSREF>, 4> m_aLayerSurfaceLayers;
// methods
void onConnect(bool noRule);
void onDisconnect(bool destroy = false);
bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false);
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion& rg);
void addDamage(const CBox& box);
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&);
bool isMirror();
bool matchesStaticSelector(const std::string& selector) const;
float getDefaultScale();
void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace);
void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
void updateMatrix();
WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
void setCTM(const Mat3x3& ctm);
void onConnect(bool noRule);
void onDisconnect(bool destroy = false);
bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false);
void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion& rg);
void addDamage(const CBox& box);
bool shouldSkipScheduleFrameOnMouseEvent();
void setMirror(const std::string&);
bool isMirror();
bool matchesStaticSelector(const std::string& selector) const;
float getDefaultScale();
void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace);
void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos);
Vector2D middle();
void updateMatrix();
WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
void setCTM(const Mat3x3& ctm);
void onCursorMovedOnMonitor();
void debugLastPresentation(const std::string& message);
void onMonitorFrame();
void debugLastPresentation(const std::string& message);
void onMonitorFrame();
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
WP<CWindow> m_previousFSWindow;
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
WP<CWindow> m_previousFSWindow;
NColorManagement::SImageDescription imageDescription;
// For the list lookup

View File

@@ -58,7 +58,7 @@ namespace NSplashes {
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo",
"2 years!",
"3 years!",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",

View File

@@ -1,25 +1,65 @@
#include "SyncReleaser.hpp"
#include "SyncTimeline.hpp"
#include "../../render/OpenGL.hpp"
#include <sys/ioctl.h>
CSyncReleaser::CSyncReleaser(WP<CSyncTimeline> timeline_, uint64_t point_) : timeline(timeline_), point(point_) {
#if defined(__linux__)
#include <linux/sync_file.h>
#else
struct sync_merge_data {
char name[32];
__s32 fd2;
__s32 fence;
__u32 flags;
__u32 pad;
};
#define SYNC_IOC_MAGIC '>'
#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
#endif
using namespace Hyprutils::OS;
CSyncReleaser::CSyncReleaser(SP<CSyncTimeline> timeline, uint64_t point) : m_timeline(timeline), m_point(point) {
;
}
CSyncReleaser::~CSyncReleaser() {
if (timeline.expired())
if (!m_timeline) {
Debug::log(ERR, "CSyncReleaser destructing without a timeline");
return;
}
if (sync)
timeline->importFromSyncFileFD(point, sync->fd());
if (m_fd.isValid())
m_timeline->importFromSyncFileFD(m_point, m_fd);
else
timeline->signal(point);
m_timeline->signal(m_point);
}
void CSyncReleaser::addReleaseSync(SP<CEGLSync> sync_) {
sync = sync_;
CFileDescriptor CSyncReleaser::mergeSyncFds(const CFileDescriptor& fd1, const CFileDescriptor& fd2) {
struct sync_merge_data data{
.name = "merged release fence",
.fd2 = fd2.get(),
.fence = -1,
};
int err = -1;
do {
err = ioctl(fd1.get(), SYNC_IOC_MERGE, &data);
} while (err == -1 && (errno == EINTR || errno == EAGAIN));
if (err < 0)
return CFileDescriptor{};
else
return CFileDescriptor(data.fence);
}
void CSyncReleaser::addReleaseSync(SP<CEGLSync> sync) {
if (m_fd.isValid())
m_fd = mergeSyncFds(m_fd, sync->takeFD());
else
m_fd = sync->fd().duplicate();
m_sync = sync;
}
void CSyncReleaser::drop() {
timeline.reset();
m_timeline.reset();
}

View File

@@ -4,6 +4,7 @@
#include <optional>
#include <vector>
#include <functional>
#include <hyprutils/os/FileDescriptor.hpp>
#include "../memory/Memory.hpp"
/*
@@ -15,17 +16,19 @@ class CEGLSync;
class CSyncReleaser {
public:
CSyncReleaser(WP<CSyncTimeline> timeline_, uint64_t point_);
CSyncReleaser(SP<CSyncTimeline> timeline, uint64_t point);
~CSyncReleaser();
// drops the releaser, will never signal anymore
void drop();
// wait for this gpu job to finish before releasing
void addReleaseSync(SP<CEGLSync> sync);
Hyprutils::OS::CFileDescriptor mergeSyncFds(const Hyprutils::OS::CFileDescriptor& fd1, const Hyprutils::OS::CFileDescriptor& fd2);
void addReleaseSync(SP<CEGLSync> sync);
private:
WP<CSyncTimeline> timeline;
uint64_t point = 0;
SP<CEGLSync> sync;
SP<CSyncTimeline> m_timeline;
uint64_t m_point = 0;
Hyprutils::OS::CFileDescriptor m_fd;
SP<CEGLSync> m_sync;
};

View File

@@ -4,6 +4,7 @@
#include <xf86drm.h>
#include <sys/eventfd.h>
using namespace Hyprutils::OS;
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
@@ -18,12 +19,13 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_) {
return timeline;
}
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, int drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->self = timeline;
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->syncobjFd = std::move(drmSyncobjFD);
timeline->self = timeline;
if (drmSyncobjFDToHandle(drmFD_, drmSyncobjFD, &timeline->handle)) {
if (drmSyncobjFDToHandle(drmFD_, timeline->syncobjFd.get(), &timeline->handle)) {
Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj from fd??");
return nullptr;
}
@@ -32,6 +34,13 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, int drmSyncobjFD) {
}
CSyncTimeline::~CSyncTimeline() {
for (auto& w : waiters) {
if (w->source) {
wl_event_source_remove(w->source);
w->source = nullptr;
}
}
if (handle == 0)
return;
@@ -85,10 +94,9 @@ bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t poin
auto w = makeShared<SWaiter>();
w->fn = waiter;
w->timeline = self;
w->eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
int eventFD = eventfd(0, EFD_CLOEXEC);
if (eventFD < 0) {
if (!w->eventFd.isValid()) {
Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
return false;
}
@@ -97,19 +105,17 @@ bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t poin
.handle = handle,
.flags = flags,
.point = point,
.fd = eventFD,
.fd = w->eventFd.get(),
};
if (drmIoctl(drmFD, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobjEventFD) != 0) {
Debug::log(ERR, "CSyncTimeline::addWaiter: drmIoctl failed");
close(eventFD);
return false;
}
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, eventFD, WL_EVENT_READABLE, ::handleWaiterFD, w.get());
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, w->eventFd.get(), WL_EVENT_READABLE, ::handleWaiterFD, w.get());
if (!w->source) {
Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed");
close(eventFD);
return false;
}
@@ -126,32 +132,43 @@ void CSyncTimeline::removeWaiter(SWaiter* w) {
std::erase_if(waiters, [w](const auto& e) { return e.get() == w; });
}
int CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
void CSyncTimeline::removeAllWaiters() {
for (auto& w : waiters) {
if (w->source) {
wl_event_source_remove(w->source);
w->source = nullptr;
}
}
waiters.clear();
}
CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
int sync = -1;
uint32_t syncHandle = 0;
if (drmSyncobjCreate(drmFD, 0, &syncHandle)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjCreate failed");
return -1;
return {};
}
if (drmSyncobjTransfer(drmFD, syncHandle, 0, handle, src, 0)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjTransfer failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
return {};
}
if (drmSyncobjExportSyncFile(drmFD, syncHandle, &sync)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjExportSyncFile failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
return {};
}
drmSyncobjDestroy(drmFD, syncHandle);
return sync;
return CFileDescriptor{sync};
}
bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, int fd) {
bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, CFileDescriptor& fd) {
uint32_t syncHandle = 0;
if (drmSyncobjCreate(drmFD, 0, &syncHandle)) {
@@ -159,7 +176,7 @@ bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, int fd) {
return false;
}
if (drmSyncobjImportSyncFile(drmFD, syncHandle, fd)) {
if (drmSyncobjImportSyncFile(drmFD, syncHandle, fd.get())) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjImportSyncFile failed");
drmSyncobjDestroy(drmFD, syncHandle);
return false;

View File

@@ -4,6 +4,7 @@
#include <optional>
#include <vector>
#include <functional>
#include <hyprutils/os/FileDescriptor.hpp>
#include "../memory/Memory.hpp"
/*
@@ -16,30 +17,33 @@ struct wl_event_source;
class CSyncTimeline {
public:
static SP<CSyncTimeline> create(int drmFD_);
static SP<CSyncTimeline> create(int drmFD_, int drmSyncobjFD);
static SP<CSyncTimeline> create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD);
~CSyncTimeline();
struct SWaiter {
std::function<void()> fn;
wl_event_source* source = nullptr;
WP<CSyncTimeline> timeline;
std::function<void()> fn;
wl_event_source* source = nullptr;
WP<CSyncTimeline> timeline;
Hyprutils::OS::CFileDescriptor eventFd;
};
// check if the timeline point has been signaled
// flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
// std::nullopt on fail
std::optional<bool> check(uint64_t point, uint32_t flags);
std::optional<bool> check(uint64_t point, uint32_t flags);
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
void removeWaiter(SWaiter*);
int exportAsSyncFileFD(uint64_t src);
bool importFromSyncFileFD(uint64_t dst, int fd);
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
void signal(uint64_t point);
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
void removeWaiter(SWaiter*);
void removeAllWaiters();
Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src);
bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd);
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
void signal(uint64_t point);
int drmFD = -1;
uint32_t handle = 0;
WP<CSyncTimeline> self;
int drmFD = -1;
Hyprutils::OS::CFileDescriptor syncobjFd;
uint32_t handle = 0;
WP<CSyncTimeline> self;
private:
CSyncTimeline() = default;

View File

@@ -32,8 +32,6 @@ CHyprError::CHyprError() {
m_pTexture = makeShared<CTexture>();
}
CHyprError::~CHyprError() = default;
void CHyprError::queueCreate(std::string message, const CHyprColor& color) {
m_szQueued = message;
m_cQueued = color;

View File

@@ -9,7 +9,7 @@
class CHyprError {
public:
CHyprError();
~CHyprError();
~CHyprError() = default;
void queueCreate(std::string message, const CHyprColor& color);
void draw();

View File

@@ -198,16 +198,12 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
*PWINDOW->m_vRealPosition = wb.pos();
*PWINDOW->m_vRealSize = wb.size();
PWINDOW->sendWindowSize(wb.size());
} else {
CBox wb = {calcPos, calcSize};
wb.round(); // avoid rounding mess
*PWINDOW->m_vRealSize = wb.size();
*PWINDOW->m_vRealPosition = wb.pos();
PWINDOW->sendWindowSize(wb.size());
}
if (force) {

View File

@@ -14,9 +14,17 @@
#include "../managers/HookSystemManager.hpp"
void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) {
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow);
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow);
if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) {
const bool HASPERSISTENTSIZE =
std::any_of(pWindow->m_vMatchedRules.begin(), pWindow->m_vMatchedRules.end(), [](const auto& rule) { return rule->ruleType == CWindowRule::RULE_PERSISTENTSIZE; });
const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt;
if (STOREDSIZE.has_value()) {
Debug::log(LOG, "using stored size {}x{} for new window {}::{}", STOREDSIZE->x, STOREDSIZE->y, pWindow->m_szClass, pWindow->m_szTitle);
pWindow->m_vLastFloatingSize = STOREDSIZE.value();
} else if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) {
const auto PMONITOR = pWindow->m_pMonitor.lock();
pWindow->m_vLastFloatingSize = PMONITOR->vecSize / 2.f;
} else
@@ -176,11 +184,9 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) {
pWindow->m_vRealSize->warp();
}
if (!pWindow->isX11OverrideRedirect()) {
pWindow->sendWindowSize(pWindow->m_vRealSize->goal());
if (!pWindow->isX11OverrideRedirect())
g_pCompositor->changeWindowZOrder(pWindow, true);
} else {
else {
pWindow->m_vPendingReportedSize = pWindow->m_vRealSize->goal();
pWindow->m_vReportedSize = pWindow->m_vPendingReportedSize;
}
@@ -307,7 +313,7 @@ void IHyprLayout::onBeginDragWindow() {
}
if (g_pInputManager->dragMode != MBIND_RESIZE && g_pInputManager->dragMode != MBIND_RESIZE_FORCE_RATIO && g_pInputManager->dragMode != MBIND_RESIZE_BLOCK_RATIO)
g_pInputManager->setCursorImageUntilUnset("grab");
g_pInputManager->setCursorImageUntilUnset("grabbing");
g_pHyprRenderer->damageWindow(DRAGGINGWINDOW);
@@ -362,9 +368,6 @@ void IHyprLayout::onEndDragWindow() {
DRAGGINGWINDOW->m_vLastFloatingSize = m_vDraggingWindowOriginalFloatSize;
DRAGGINGWINDOW->m_bDraggingTiled = false;
if (pWindow->m_bIsFloating)
DRAGGINGWINDOW->sendWindowSize(DRAGGINGWINDOW->m_vRealSize->goal()); // match the size of the window
static auto USECURRPOS = CConfigValue<Hyprlang::INT>("group:insert_after_current");
(*USECURRPOS ? pWindow : pWindow->getGroupTail())->insertWindowToGroup(DRAGGINGWINDOW);
pWindow->setGroupCurrent(DRAGGINGWINDOW);
@@ -606,10 +609,11 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
if (*PANIMATEMOUSE)
*DRAGGINGWINDOW->m_vRealPosition = wb.pos();
else
else {
DRAGGINGWINDOW->m_vRealPosition->setValueAndWarp(wb.pos());
DRAGGINGWINDOW->sendWindowSize();
}
DRAGGINGWINDOW->sendWindowSize(DRAGGINGWINDOW->m_vRealSize->goal());
} else if (g_pInputManager->dragMode == MBIND_RESIZE || g_pInputManager->dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->dragMode == MBIND_RESIZE_BLOCK_RATIO) {
if (DRAGGINGWINDOW->m_bIsFloating) {
@@ -679,9 +683,8 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
} else {
DRAGGINGWINDOW->m_vRealSize->setValueAndWarp(wb.size());
DRAGGINGWINDOW->m_vRealPosition->setValueAndWarp(wb.pos());
DRAGGINGWINDOW->sendWindowSize();
}
DRAGGINGWINDOW->sendWindowSize(DRAGGINGWINDOW->m_vRealSize->goal());
} else {
resizeActiveWindow(TICKDELTA, m_eGrabbedCorner, DRAGGINGWINDOW);
}
@@ -787,7 +790,6 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
pWindow->updateToplevel();
pWindow->sendWindowSize(pWindow->m_vRealSize->goal());
g_pHyprRenderer->damageWindow(pWindow);
}
@@ -950,5 +952,3 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
return sizePredicted;
}
IHyprLayout::~IHyprLayout() = default;

View File

@@ -43,7 +43,7 @@ enum eDirection : int8_t {
class IHyprLayout {
public:
virtual ~IHyprLayout() = 0;
virtual ~IHyprLayout() = default;
virtual void onEnable() = 0;
virtual void onDisable() = 0;

View File

@@ -374,8 +374,22 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) {
// compute placement of master window(s)
if (WINDOWS == 1 && !centerMasterWindow) {
PMASTERNODE->size = WSSIZE;
PMASTERNODE->position = WSPOS;
static auto PALWAYSKEEPPOSITION = CConfigValue<Hyprlang::INT>("master:always_keep_position");
if (*PALWAYSKEEPPOSITION) {
const float WIDTH = WSSIZE.x * PMASTERNODE->percMaster;
float nextX = 0;
if (orientation == ORIENTATION_RIGHT)
nextX = WSSIZE.x - WIDTH;
else if (orientation == ORIENTATION_CENTER)
nextX = (WSSIZE.x - WIDTH) / 2;
PMASTERNODE->size = Vector2D(WIDTH, WSSIZE.y);
PMASTERNODE->position = WSPOS + Vector2D((double)nextX, 0.0);
} else {
PMASTERNODE->size = WSSIZE;
PMASTERNODE->position = WSPOS;
}
applyNodeDataToWindow(PMASTERNODE);
return;
@@ -678,16 +692,12 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
*PWINDOW->m_vRealPosition = wb.pos();
*PWINDOW->m_vRealSize = wb.size();
PWINDOW->sendWindowSize(wb.size());
} else {
CBox wb = {calcPos, calcSize};
wb.round(); // avoid rounding mess
*PWINDOW->m_vRealPosition = wb.pos();
*PWINDOW->m_vRealSize = wb.size();
PWINDOW->sendWindowSize(wb.size());
}
if (m_bForceWarps && !*PANIMATE) {
@@ -1357,7 +1367,7 @@ void CHyprMasterLayout::runOrientationCycle(SLayoutMessageHeader& header, CVarLi
int nextOrPrev = 0;
for (size_t i = 0; i < cycle.size(); ++i) {
if (PWORKSPACEDATA->orientation == cycle.at(i)) {
if (PWORKSPACEDATA->orientation == cycle[i]) {
nextOrPrev = i + direction;
break;
}

View File

@@ -5,7 +5,6 @@
#include "../helpers/varlist/VarList.hpp"
#include <vector>
#include <list>
#include <vector>
#include <any>
enum eFullscreenMode : int8_t;

View File

@@ -38,7 +38,8 @@
#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 VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x < (x2) && (vec).y >= (y1) && (vec).y < (y2))
#define VECNOTINRECT(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))
@@ -88,6 +89,7 @@
{ \
Debug::log(CRIT, "\n\nMEMORY CORRUPTED: Unreachable failed! (Reached an unreachable position, memory corruption!!!)"); \
raise(SIGABRT); \
std::unreachable(); \
}
#else
#define UNREACHABLE() std::unreachable();
@@ -110,6 +112,7 @@
} \
}
#define AQUAMARINE_VERSION_NUMBER (AQUAMARINE_VERSION_MAJOR * 10000 + AQUAMARINE_VERSION_MINOR * 100 + AQUAMARINE_VERSION_PATCH)
#define AQUAMARINE_FORWARD(name) \
namespace Aquamarine { \
class name; \

View File

@@ -43,6 +43,7 @@ int main(int argc, char** argv) {
setenv("HYPRLAND_CMD", cmd.c_str(), 1);
setenv("XDG_BACKEND", "wayland", 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
setenv("_JAVA_AWT_WM_NONREPARENTING", "1", 1);
setenv("MOZ_ENABLE_WAYLAND", "1", 1);

251
src/managers/ANRManager.cpp Normal file
View File

@@ -0,0 +1,251 @@
#include "ANRManager.hpp"
#include "../helpers/fs/FsUtils.hpp"
#include "../debug/Log.hpp"
#include "../macros.hpp"
#include "HookSystemManager.hpp"
#include "../Compositor.hpp"
#include "../protocols/XDGShell.hpp"
#include "./eventLoop/EventLoopManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../xwayland/XSurface.hpp"
using namespace Hyprutils::OS;
static constexpr auto TIMER_TIMEOUT = std::chrono::milliseconds(1500);
CANRManager::CANRManager() {
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
Debug::log(ERR, "hyprland-dialog missing from PATH, cannot start ANRManager");
return;
}
m_timer = makeShared<CEventLoopTimer>(TIMER_TIMEOUT, [this](SP<CEventLoopTimer> self, void* data) { onTick(); }, this);
g_pEventLoopManager->addTimer(m_timer);
m_active = true;
static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) {
auto window = std::any_cast<PHLWINDOW>(data);
for (const auto& d : m_data) {
if (d->fitsWindow(window))
return;
}
m_data.emplace_back(makeShared<SANRData>(window));
});
m_timer->updateTimeout(TIMER_TIMEOUT);
}
void CANRManager::onTick() {
std::erase_if(m_data, [](const auto& e) { return e->isDefunct(); });
static auto PENABLEANR = CConfigValue<Hyprlang::INT>("misc:enable_anr_dialog");
if (!*PENABLEANR) {
m_timer->updateTimeout(TIMER_TIMEOUT * 10);
return;
}
for (auto& data : m_data) {
PHLWINDOW firstWindow;
int count = 0;
for (const auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped)
continue;
if (!data->fitsWindow(w))
continue;
count++;
if (!firstWindow)
firstWindow = w;
}
if (count == 0)
continue;
if (data->missedResponses > 0) {
if (!data->isThreadRunning() && !data->dialogThreadSaidWait) {
data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, data->getPid());
for (const auto& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped)
continue;
if (!data->fitsWindow(w))
continue;
*w->m_notRespondingTint = 0.2F;
}
}
} else if (data->isThreadRunning())
data->killDialog();
if (data->missedResponses == 0)
data->dialogThreadSaidWait = false;
data->missedResponses++;
data->ping();
}
m_timer->updateTimeout(TIMER_TIMEOUT);
}
void CANRManager::onResponse(SP<CXDGWMBase> wmBase) {
const auto DATA = dataFor(wmBase);
if (!DATA)
return;
onResponse(DATA);
}
void CANRManager::onResponse(SP<CXWaylandSurface> pXwaylandSurface) {
const auto DATA = dataFor(pXwaylandSurface);
if (!DATA)
return;
onResponse(DATA);
}
void CANRManager::onResponse(SP<CANRManager::SANRData> data) {
data->missedResponses = 0;
if (data->isThreadRunning())
data->killDialog();
}
bool CANRManager::isNotResponding(PHLWINDOW pWindow) {
const auto DATA = dataFor(pWindow);
if (!DATA)
return false;
return isNotResponding(DATA);
}
bool CANRManager::isNotResponding(SP<CANRManager::SANRData> data) {
return data->missedResponses > 1;
}
SP<CANRManager::SANRData> CANRManager::dataFor(PHLWINDOW pWindow) {
auto it = m_data.end();
if (pWindow->m_pXWaylandSurface)
it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xwaylandSurface && data->xwaylandSurface == pWindow->m_pXWaylandSurface; });
else if (pWindow->m_pXDGSurface)
it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xdgBase && data->xdgBase == pWindow->m_pXDGSurface->owner; });
return it == m_data.end() ? nullptr : *it;
}
SP<CANRManager::SANRData> CANRManager::dataFor(SP<CXDGWMBase> wmBase) {
auto it = std::ranges::find_if(m_data, [&wmBase](const auto& data) { return data->xdgBase && data->xdgBase == wmBase; });
return it == m_data.end() ? nullptr : *it;
}
SP<CANRManager::SANRData> CANRManager::dataFor(SP<CXWaylandSurface> pXwaylandSurface) {
auto it = std::ranges::find_if(m_data, [&pXwaylandSurface](const auto& data) { return data->xwaylandSurface && data->xwaylandSurface == pXwaylandSurface; });
return it == m_data.end() ? nullptr : *it;
}
CANRManager::SANRData::SANRData(PHLWINDOW pWindow) :
xwaylandSurface(pWindow->m_pXWaylandSurface), xdgBase(pWindow->m_pXDGSurface ? pWindow->m_pXDGSurface->owner : WP<CXDGWMBase>{}) {
;
}
CANRManager::SANRData::~SANRData() {
if (dialogThread.joinable()) {
killDialog();
// dangerous: might lock if the above failed!!
dialogThread.join();
}
}
void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) {
if (!dialogThreadExited)
killDialog();
// dangerous: might lock if the above failed!!
if (dialogThread.joinable())
dialogThread.join();
dialogThreadExited = false;
dialogThreadSaidWait = false;
dialogThread = std::thread([title, appName, appClass, dialogWmPID, this]() {
SP<CProcess> proc = makeShared<CProcess>("hyprland-dialog",
std::vector<std::string>{"--title", title, "--text",
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?",
appName.empty() ? "unknown" : appName, appClass.empty() ? "unknown" : appClass),
"--buttons", "Terminate;Wait"});
dialogProc = proc;
proc->runSync();
dialogThreadExited = true;
if (proc->stdOut().empty())
return;
if (proc->stdOut().starts_with("Terminate"))
kill(dialogWmPID, SIGKILL);
if (proc->stdOut().starts_with("Wait"))
dialogThreadSaidWait = true;
});
}
bool CANRManager::SANRData::isThreadRunning() {
if (dialogThread.native_handle() == 0)
return false;
if (dialogThreadExited)
return false;
return pthread_kill(dialogThread.native_handle(), 0) != ESRCH;
}
void CANRManager::SANRData::killDialog() const {
if (!dialogProc)
return;
if (!dialogProc->pid()) {
Debug::log(ERR, "ANR: cannot kill dialogProc, as it doesn't have a pid. If you have hyprutils <= 0.6.0, you will crash soon. Otherwise, dialog failed to spawn??");
return;
}
kill(dialogProc->pid(), SIGKILL);
}
bool CANRManager::SANRData::fitsWindow(PHLWINDOW pWindow) const {
if (pWindow->m_pXWaylandSurface)
return pWindow->m_pXWaylandSurface == xwaylandSurface;
else if (pWindow->m_pXDGSurface)
return pWindow->m_pXDGSurface->owner == xdgBase && xdgBase;
return false;
}
bool CANRManager::SANRData::isDefunct() const {
return xdgBase.expired() && xwaylandSurface.expired();
}
pid_t CANRManager::SANRData::getPid() const {
if (xdgBase) {
pid_t pid = 0;
wl_client_get_credentials(xdgBase->client(), &pid, nullptr, nullptr);
return pid;
}
if (xwaylandSurface)
return xwaylandSurface->pid;
return 0;
}
void CANRManager::SANRData::ping() {
if (xdgBase) {
xdgBase->ping();
return;
}
if (xwaylandSurface)
xwaylandSurface->ping();
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include "../helpers/memory/Memory.hpp"
#include "../desktop/DesktopTypes.hpp"
#include <chrono>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
#include "./eventLoop/EventLoopTimer.hpp"
#include "../helpers/signal/Signal.hpp"
#include <atomic>
#include <thread>
#include <vector>
class CXDGWMBase;
class CXWaylandSurface;
class CANRManager {
public:
CANRManager();
void onResponse(SP<CXDGWMBase> wmBase);
void onResponse(SP<CXWaylandSurface> xwaylandSurface);
bool isNotResponding(PHLWINDOW pWindow);
private:
bool m_active = false;
SP<CEventLoopTimer> m_timer;
void onTick();
struct SANRData {
SANRData(PHLWINDOW pWindow);
~SANRData();
WP<CXWaylandSurface> xwaylandSurface;
WP<CXDGWMBase> xdgBase;
int missedResponses = 0;
std::thread dialogThread;
SP<Hyprutils::OS::CProcess> dialogProc;
std::atomic<bool> dialogThreadExited = false;
std::atomic<bool> dialogThreadSaidWait = false;
void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID);
bool isThreadRunning();
void killDialog() const;
bool isDefunct() const;
bool fitsWindow(PHLWINDOW pWindow) const;
pid_t getPid() const;
void ping();
};
void onResponse(SP<SANRData> data);
bool isNotResponding(SP<SANRData> data);
SP<SANRData> dataFor(PHLWINDOW pWindow);
SP<SANRData> dataFor(SP<CXDGWMBase> wmBase);
SP<SANRData> dataFor(SP<CXWaylandSurface> pXwaylandSurface);
std::vector<SP<SANRData>> m_data;
};
inline UP<CANRManager> g_pANRManager;

View File

@@ -43,7 +43,7 @@ CHyprAnimationManager::CHyprAnimationManager() {
template <Animable VarType>
static void updateVariable(CAnimatedVariable<VarType>& av, const float POINTY, bool warp = false) {
if (warp || av.value() == av.goal()) {
av.warp();
av.warp(true, false);
return;
}
@@ -53,7 +53,7 @@ static void updateVariable(CAnimatedVariable<VarType>& av, const float POINTY, b
static void updateColorVariable(CAnimatedVariable<CHyprColor>& av, const float POINTY, bool warp) {
if (warp || av.value() == av.goal()) {
av.warp();
av.warp(true, false);
return;
}
@@ -357,6 +357,21 @@ void CHyprAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force,
*pWindow->m_vRealPosition = posOffset;
}
void CHyprAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) {
const auto GOALPOS = pWindow->m_vRealPosition->goal();
const auto GOALSIZE = pWindow->m_vRealSize->goal();
if (close) {
*pWindow->m_vRealPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F};
*pWindow->m_vRealSize = Vector2D{GOALSIZE.x, 0.F};
} else {
pWindow->m_vRealPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F});
pWindow->m_vRealSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F});
*pWindow->m_vRealPosition = GOALPOS;
*pWindow->m_vRealSize = GOALSIZE;
}
}
void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
if (!close) {
pWindow->m_vRealPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn"));
@@ -387,7 +402,9 @@ void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool clos
if (STYLE.starts_with("slide")) {
CVarList animList2(STYLE, 0, 's');
animationSlide(pWindow, animList2[1], close);
} else {
} else if (STYLE == "gnomed" || STYLE == "gnome")
animationGnomed(pWindow, close);
else {
// anim popin, fallback
float minPerc = 0.f;
@@ -405,6 +422,8 @@ void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool clos
} else {
if (animList[0] == "slide")
animationSlide(pWindow, animList[1], close);
else if (animList[0] == "gnomed" || animList[0] == "gnome")
animationGnomed(pWindow, close);
else {
// anim popin, fallback
@@ -425,7 +444,7 @@ void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool clos
std::string CHyprAnimationManager::styleValidInConfigVar(const std::string& config, const std::string& style) {
if (config.starts_with("window")) {
if (style.starts_with("slide"))
if (style.starts_with("slide") || style == "gnome" || style == "gnomed")
return "";
else if (style.starts_with("popin")) {
// try parsing

View File

@@ -59,6 +59,7 @@ class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager {
// Anim stuff
void animationPopin(PHLWINDOW, bool close = false, float minPerc = 0.f);
void animationSlide(PHLWINDOW, std::string force = "", bool close = false);
void animationGnomed(PHLWINDOW, bool close = false);
};
inline UP<CHyprAnimationManager> g_pAnimationManager;

View File

@@ -9,9 +9,10 @@
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
using namespace Hyprutils::OS;
CEventManager::CEventManager() : m_iSocketFD(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) {
if (m_iSocketFD < 0) {
if (!m_iSocketFD.isValid()) {
Debug::log(ERR, "Couldn't start the Hyprland Socket 2. (1) IPC will not work.");
return;
}
@@ -25,31 +26,27 @@ CEventManager::CEventManager() : m_iSocketFD(socket(AF_UNIX, SOCK_STREAM | SOCK_
strncpy(SERVERADDRESS.sun_path, PATH.c_str(), sizeof(SERVERADDRESS.sun_path) - 1);
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
if (bind(m_iSocketFD.get(), (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
Debug::log(ERR, "Couldn't bind the Hyprland Socket 2. (3) IPC will not work.");
return;
}
// 10 max queued.
if (listen(m_iSocketFD, 10) < 0) {
if (listen(m_iSocketFD.get(), 10) < 0) {
Debug::log(ERR, "Couldn't listen on the Hyprland Socket 2. (4) IPC will not work.");
return;
}
m_pEventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, onClientEvent, nullptr);
m_pEventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD.get(), WL_EVENT_READABLE, onClientEvent, nullptr);
}
CEventManager::~CEventManager() {
for (const auto& client : m_vClients) {
wl_event_source_remove(client.eventSource);
close(client.fd);
}
if (m_pEventSource != nullptr)
wl_event_source_remove(m_pEventSource);
if (m_iSocketFD >= 0)
close(m_iSocketFD);
}
int CEventManager::onServerEvent(int fd, uint32_t mask, void* data) {
@@ -66,33 +63,31 @@ int CEventManager::onServerEvent(int fd, uint32_t mask) {
wl_event_source_remove(m_pEventSource);
m_pEventSource = nullptr;
close(fd);
m_iSocketFD = -1;
m_iSocketFD.reset();
return 0;
}
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC | SOCK_NONBLOCK);
if (ACCEPTEDCONNECTION < 0) {
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
CFileDescriptor ACCEPTEDCONNECTION{accept4(m_iSocketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC | SOCK_NONBLOCK)};
if (!ACCEPTEDCONNECTION.isValid()) {
if (errno != EAGAIN) {
Debug::log(ERR, "Socket2 failed receiving connection, errno: {}", errno);
wl_event_source_remove(m_pEventSource);
m_pEventSource = nullptr;
close(fd);
m_iSocketFD = -1;
m_iSocketFD.reset();
}
return 0;
}
Debug::log(LOG, "Socket2 accepted a new client at FD {}", ACCEPTEDCONNECTION);
Debug::log(LOG, "Socket2 accepted a new client at FD {}", ACCEPTEDCONNECTION.get());
// add to event loop so we can close it when we need to
auto* eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, ACCEPTEDCONNECTION, 0, onServerEvent, nullptr);
auto* eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, ACCEPTEDCONNECTION.get(), 0, onServerEvent, nullptr);
m_vClients.emplace_back(SClient{
ACCEPTEDCONNECTION,
std::move(ACCEPTEDCONNECTION),
{},
eventSource,
});
@@ -113,7 +108,7 @@ int CEventManager::onClientEvent(int fd, uint32_t mask) {
// send all queued events
while (!CLIENTIT->events.empty()) {
const auto& event = CLIENTIT->events.front();
if (write(CLIENTIT->fd, event->c_str(), event->length()) < 0)
if (write(CLIENTIT->fd.get(), event->c_str(), event->length()) < 0)
break;
CLIENTIT->events.erase(CLIENTIT->events.begin());
@@ -128,13 +123,12 @@ int CEventManager::onClientEvent(int fd, uint32_t mask) {
}
std::vector<CEventManager::SClient>::iterator CEventManager::findClientByFD(int fd) {
return std::find_if(m_vClients.begin(), m_vClients.end(), [fd](const auto& client) { return client.fd == fd; });
return std::find_if(m_vClients.begin(), m_vClients.end(), [fd](const auto& client) { return client.fd.get() == fd; });
}
std::vector<CEventManager::SClient>::iterator CEventManager::removeClientByFD(int fd) {
const auto CLIENTIT = findClientByFD(fd);
wl_event_source_remove(CLIENTIT->eventSource);
close(fd);
return m_vClients.erase(CLIENTIT);
}
@@ -157,11 +151,11 @@ void CEventManager::postEvent(const SHyprIPCEvent& event) {
for (auto it = m_vClients.begin(); it != m_vClients.end();) {
// try to send the event immediately if the queue is empty
const auto QUEUESIZE = it->events.size();
if (QUEUESIZE > 0 || write(it->fd, sharedEvent->c_str(), sharedEvent->length()) < 0) {
if (QUEUESIZE > 0 || write(it->fd.get(), sharedEvent->c_str(), sharedEvent->length()) < 0) {
if (QUEUESIZE >= MAX_QUEUED_EVENTS) {
// too many events queued, remove the client
Debug::log(ERR, "Socket2 fd {} overflowed event queue, removing", it->fd);
it = removeClientByFD(it->fd);
Debug::log(ERR, "Socket2 fd {} overflowed event queue, removing", it->fd.get());
it = removeClientByFD(it->fd.get());
continue;
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include <vector>
#include <vector>
#include <hyprutils/os/FileDescriptor.hpp>
#include "../defines.hpp"
#include "../helpers/memory/Memory.hpp"
@@ -27,19 +26,19 @@ class CEventManager {
int onClientEvent(int fd, uint32_t mask);
struct SClient {
int fd = -1;
std::vector<SP<std::string>> events;
wl_event_source* eventSource = nullptr;
Hyprutils::OS::CFileDescriptor fd;
std::vector<SP<std::string>> events;
wl_event_source* eventSource = nullptr;
};
std::vector<SClient>::iterator findClientByFD(int fd);
std::vector<SClient>::iterator removeClientByFD(int fd);
private:
int m_iSocketFD = -1;
wl_event_source* m_pEventSource = nullptr;
Hyprutils::OS::CFileDescriptor m_iSocketFD;
wl_event_source* m_pEventSource = nullptr;
std::vector<SClient> m_vClients;
std::vector<SClient> m_vClients;
};
inline UP<CEventManager> g_pEventManager;

View File

@@ -12,8 +12,6 @@
#include "TokenManager.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "debug/Log.hpp"
#include "helpers/varlist/VarList.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/LayoutManager.hpp"
@@ -28,7 +26,9 @@
#include <cstring>
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
#include <sys/ioctl.h>
#include <fcntl.h>
@@ -113,6 +113,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["focuswindowbyclass"] = focusWindow;
m_mDispatchers["focuswindow"] = focusWindow;
m_mDispatchers["tagwindow"] = tagWindow;
m_mDispatchers["toggleswallow"] = toggleSwallow;
m_mDispatchers["submap"] = setSubmap;
m_mDispatchers["pass"] = pass;
m_mDispatchers["sendshortcut"] = sendshortcut;
@@ -367,7 +368,7 @@ bool CKeybindManager::tryMoveFocusToMonitor(PHLMONITOR monitor) {
return true;
}
void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool preserveFocusHistory) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");
@@ -386,7 +387,7 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
if (!PWINDOWTOCHANGETO->m_bPinned)
g_pCompositor->setWindowFullscreenInternal(PLASTWINDOW, FSMODE_NONE);
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
g_pCompositor->focusWindow(PWINDOWTOCHANGETO, nullptr, preserveFocusHistory);
if (!PWINDOWTOCHANGETO->m_bPinned)
g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, MODE);
@@ -396,7 +397,7 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
PWINDOWTOCHANGETO->m_vRealSize->warp();
} else {
updateRelativeCursorCoords();
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
g_pCompositor->focusWindow(PWINDOWTOCHANGETO, nullptr, preserveFocusHistory);
PWINDOWTOCHANGETO->warpCursor();
// Move mouse focus to the new window if required by current follow_mouse and warp modes
@@ -853,19 +854,18 @@ bool CKeybindManager::handleVT(xkb_keysym_t keysym) {
const unsigned int TTY = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
// vtnr is bugged for some reason.
unsigned int ttynum = 0;
int fd;
if ((fd = open("/dev/tty", O_RDONLY | O_NOCTTY)) >= 0) {
unsigned int ttynum = 0;
Hyprutils::OS::CFileDescriptor fd{open("/dev/tty", O_RDONLY | O_NOCTTY)};
if (fd.isValid()) {
#if defined(VT_GETSTATE)
struct vt_stat st;
if (!ioctl(fd, VT_GETSTATE, &st))
if (!ioctl(fd.get(), VT_GETSTATE, &st))
ttynum = st.v_active;
#elif defined(VT_GETACTIVE)
int vt;
if (!ioctl(fd, VT_GETACTIVE, &vt))
if (!ioctl(fd.get(), VT_GETACTIVE, &vt))
ttynum = vt;
#endif
close(fd);
}
if (ttynum == TTY)
@@ -944,11 +944,11 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo
Debug::log(LOG, "Unable to create pipe for fork");
}
pid_t child, grandchild;
CFileDescriptor pipeSock[2] = {CFileDescriptor{socket[0]}, CFileDescriptor{socket[1]}};
pid_t child, grandchild;
child = fork();
if (child < 0) {
close(socket[0]);
close(socket[1]);
Debug::log(LOG, "Fail to create the first fork");
return 0;
}
@@ -967,22 +967,24 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo
setenv(e.first.c_str(), e.second.c_str(), 1);
}
setenv("WAYLAND_DISPLAY", g_pCompositor->m_szWLDisplaySocket.c_str(), 1);
close(socket[0]);
close(socket[1]);
int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
if (devnull != -1) {
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
close(devnull);
}
execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
// exit grandchild
_exit(0);
}
close(socket[0]);
write(socket[1], &grandchild, sizeof(grandchild));
close(socket[1]);
write(pipeSock[1].get(), &grandchild, sizeof(grandchild));
// exit child
_exit(0);
}
// run in parent
close(socket[1]);
read(socket[0], &grandchild, sizeof(grandchild));
close(socket[0]);
read(pipeSock[0].get(), &grandchild, sizeof(grandchild));
// clear child and leave grandchild to init
waitpid(child, nullptr, 0);
if (grandchild < 0) {
@@ -1041,7 +1043,7 @@ SDispatchResult CKeybindManager::killWindow(std::string args) {
}
SDispatchResult CKeybindManager::signalActive(std::string args) {
if (!std::all_of(args.begin(), args.end(), ::isdigit))
if (!isNumber(args))
return {.success = false, .error = "signalActive: signal has to be int"};
try {
@@ -1480,7 +1482,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
}
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ?
(arg == 'd' || arg == 'b' || arg == 'r' ? g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true) : g_pCompositor->getPrevWindowOnWorkspace(PLASTWINDOW, true)) :
g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, arg != 'd' && arg != 'b' && arg != 'r') :
g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
// Prioritize focus change within groups if the window is a part of it.
@@ -1721,7 +1723,7 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) {
// index starts from '1'; '0' means last window
const int INDEX = std::stoi(args);
if (INDEX > PWINDOW->getGroupSize())
return {};
return {.success = false, .error = "Index too big, there aren't that many windows in this group"};
if (INDEX == 0)
PWINDOW->setGroupCurrent(PWINDOW->getGroupTail());
else
@@ -1914,7 +1916,6 @@ SDispatchResult CKeybindManager::workspaceOpt(std::string args) {
if (PWORKSPACE->m_bDefaultFloating) {
w->m_vRealPosition->setValueAndWarp(SAVEDPOS);
w->m_vRealSize->setValueAndWarp(SAVEDSIZE);
w->sendWindowSize(SAVEDSIZE);
*w->m_vRealSize = w->m_vRealSize->value() + Vector2D(4, 4);
*w->m_vRealPosition = w->m_vRealPosition->value() - Vector2D(2, 2);
}
@@ -1985,7 +1986,7 @@ SDispatchResult CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args)
SDispatchResult CKeybindManager::moveWorkspaceToMonitor(std::string args) {
if (!args.contains(' '))
return {};
return {.success = false, .error = "Invalid arguments, expected: workspace monitor"};
std::string workspace = args.substr(0, args.find_first_of(' '));
std::string monitor = args.substr(args.find_first_of(' ') + 1);
@@ -2128,13 +2129,16 @@ SDispatchResult CKeybindManager::forceRendererReload(std::string args) {
SDispatchResult CKeybindManager::resizeActive(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return {};
if (!PLASTWINDOW)
return {.success = false, .error = "No window found"};
if (PLASTWINDOW->isFullscreen())
return {.success = false, .error = "Window is fullscreen"};
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealSize->goal());
if (SIZ.x < 1 || SIZ.y < 1)
return {};
return {.success = false, .error = "Invalid size provided"};
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PLASTWINDOW->m_vRealSize->goal());
@@ -2147,8 +2151,11 @@ SDispatchResult CKeybindManager::resizeActive(std::string args) {
SDispatchResult CKeybindManager::moveActive(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return {};
if (!PLASTWINDOW)
return {.success = false, .error = "No window found"};
if (PLASTWINDOW->isFullscreen())
return {.success = false, .error = "Window is fullscreen"};
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealPosition->goal());
@@ -2170,7 +2177,7 @@ SDispatchResult CKeybindManager::moveWindow(std::string args) {
}
if (PWINDOW->isFullscreen())
return {};
return {.success = false, .error = "Window is fullscreen"};
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealPosition->goal());
@@ -2192,12 +2199,12 @@ SDispatchResult CKeybindManager::resizeWindow(std::string args) {
}
if (PWINDOW->isFullscreen())
return {};
return {.success = false, .error = "Window is fullscreen"};
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealSize->goal());
if (SIZ.x < 1 || SIZ.y < 1)
return {};
return {.success = false, .error = "Invalid size provided"};
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PWINDOW->m_vRealSize->goal(), CORNER_NONE, PWINDOW);
@@ -2228,11 +2235,13 @@ SDispatchResult CKeybindManager::circleNext(std::string arg) {
floatStatus = true;
const auto VISIBLE = args.contains("visible") || args.contains("v");
const auto& w = (args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l")) ?
g_pCompositor->getPrevWindowOnWorkspace(g_pCompositor->m_pLastWindow.lock(), true, floatStatus, VISIBLE) :
g_pCompositor->getNextWindowOnWorkspace(g_pCompositor->m_pLastWindow.lock(), true, floatStatus, VISIBLE);
const auto PREV = args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l");
const auto NEXT = args.contains("next") || args.contains("n"); // prev is default in classic alt+tab
const auto HIST = args.contains("hist") || args.contains("h");
const auto& w = HIST ? g_pCompositor->getWindowCycleHist(g_pCompositor->m_pLastWindow, true, floatStatus, VISIBLE, NEXT) :
g_pCompositor->getWindowCycle(g_pCompositor->m_pLastWindow.lock(), true, floatStatus, VISIBLE, PREV);
switchToWindow(w);
switchToWindow(w, HIST);
return {};
}
@@ -2241,7 +2250,7 @@ SDispatchResult CKeybindManager::focusWindow(std::string regexp) {
const auto PWINDOW = g_pCompositor->getWindowByRegex(regexp);
if (!PWINDOW)
return {};
return {.success = false, .error = "No such window found"};
Debug::log(LOG, "Focusing to window name: {}", PWINDOW->m_szTitle);
@@ -2301,7 +2310,7 @@ SDispatchResult CKeybindManager::tagWindow(std::string args) {
else if (vars.size() == 2)
PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
else
return {};
return {.success = false, .error = "Invalid number of arguments, expected 1 or 2 arguments"};
if (PWINDOW && PWINDOW->m_tags.applyTag(vars[0])) {
PWINDOW->updateDynamicRules();
@@ -2311,6 +2320,27 @@ SDispatchResult CKeybindManager::tagWindow(std::string args) {
return {};
}
SDispatchResult CKeybindManager::toggleSwallow(std::string args) {
PHLWINDOWREF pWindow = g_pCompositor->m_pLastWindow;
if (!valid(pWindow) || !valid(pWindow->m_pSwallowed))
return {};
if (pWindow->m_pSwallowed->m_bCurrentlySwallowed) {
// Unswallow
pWindow->m_pSwallowed->m_bCurrentlySwallowed = false;
pWindow->m_pSwallowed->setHidden(false);
g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow->m_pSwallowed.lock());
} else {
// Reswallow
pWindow->m_pSwallowed->m_bCurrentlySwallowed = true;
pWindow->m_pSwallowed->setHidden(true);
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow->m_pSwallowed.lock());
}
return {};
}
SDispatchResult CKeybindManager::setSubmap(std::string submap) {
if (submap == "reset" || submap == "") {
m_szCurrentSelectedSubmap = "";
@@ -2627,18 +2657,12 @@ SDispatchResult CKeybindManager::swapnext(std::string arg) {
g_pCompositor->m_pLastWindow->m_pLastCycledWindow.lock() :
nullptr;
if (arg == "last" || arg == "l" || arg == "prev" || arg == "p")
toSwap = g_pCompositor->getPrevWindowOnWorkspace(PLASTCYCLED ? PLASTCYCLED : PLASTWINDOW, true);
else
toSwap = g_pCompositor->getNextWindowOnWorkspace(PLASTCYCLED ? PLASTCYCLED : PLASTWINDOW, true);
const bool NEED_PREV = arg == "last" || arg == "l" || arg == "prev" || arg == "p";
toSwap = g_pCompositor->getWindowCycle(PLASTCYCLED ? PLASTCYCLED : PLASTWINDOW, true, std::nullopt, false, NEED_PREV);
// sometimes we may come back to ourselves.
if (toSwap == PLASTWINDOW) {
if (arg == "last" || arg == "l" || arg == "prev" || arg == "p")
toSwap = g_pCompositor->getPrevWindowOnWorkspace(PLASTWINDOW, true);
else
toSwap = g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true);
}
if (toSwap == PLASTWINDOW)
toSwap = g_pCompositor->getWindowCycle(PLASTWINDOW, true, std::nullopt, false, NEED_PREV);
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, toSwap);
@@ -2656,7 +2680,10 @@ SDispatchResult CKeybindManager::swapActiveWorkspaces(std::string args) {
const auto PMON1 = g_pCompositor->getMonitorFromString(MON1);
const auto PMON2 = g_pCompositor->getMonitorFromString(MON2);
if (!PMON1 || !PMON2 || PMON1 == PMON2)
if (!PMON1 || !PMON2)
return {.success = false, .error = "No such monitor found"};
if (PMON1 == PMON2)
return {};
g_pCompositor->swapActiveWorkspaces(PMON1, PMON2);
@@ -2679,7 +2706,7 @@ SDispatchResult CKeybindManager::pinActive(std::string args) {
}
if (!PWINDOW->m_bIsFloating || PWINDOW->isFullscreen())
return {};
return {.success = false, .error = "Window does not qualify to be pinned"};
PWINDOW->m_bPinned = !PWINDOW->m_bPinned;
@@ -2800,6 +2827,7 @@ SDispatchResult CKeybindManager::lockGroups(std::string args) {
g_pKeybindManager->m_bGroupsLocked = false;
g_pEventManager->postEvent(SHyprIPCEvent{"lockgroups", g_pKeybindManager->m_bGroupsLocked ? "1" : "0"});
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
return {};
}
@@ -2807,8 +2835,11 @@ SDispatchResult CKeybindManager::lockGroups(std::string args) {
SDispatchResult CKeybindManager::lockActiveGroup(std::string args) {
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW || !PWINDOW->m_sGroupData.pNextWindow.lock())
return {};
if (!PWINDOW)
return {.success = false, .error = "No window found"};
if (!PWINDOW->m_sGroupData.pNextWindow.lock())
return {.success = false, .error = "Not a group"};
const auto PHEAD = PWINDOW->getGroupHead();
@@ -2929,7 +2960,7 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) {
static auto PIGNOREGROUPLOCK = CConfigValue<Hyprlang::INT>("binds:ignore_group_lock");
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked)
return {};
return {.success = false, .error = "Groups locked"};
PHLWINDOW PWINDOW = nullptr;
@@ -2938,8 +2969,11 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) {
else
PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW || !PWINDOW->m_sGroupData.pNextWindow.lock())
return {};
if (!PWINDOW)
return {.success = false, .error = "No window found"};
if (!PWINDOW->m_sGroupData.pNextWindow.lock())
return {.success = false, .error = "Window not in a group"};
moveWindowOutOfGroup(PWINDOW);
@@ -2957,7 +2991,10 @@ SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) {
}
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW || PWINDOW->isFullscreen())
if (!PWINDOW)
return {.success = false, .error = "No window found"};
if (PWINDOW->isFullscreen())
return {};
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) {
@@ -2967,7 +3004,7 @@ SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) {
const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, arg);
const bool ISWINDOWGROUP = PWINDOW->m_sGroupData.pNextWindow.lock().get();
const bool ISWINDOWGROUP = PWINDOW->m_sGroupData.pNextWindow;
const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->getGroupHead()->m_sGroupData.locked;
const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_sGroupData.pNextWindow.lock() == PWINDOW;
@@ -3046,8 +3083,11 @@ SDispatchResult CKeybindManager::moveGroupWindow(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || !PLASTWINDOW->m_sGroupData.pNextWindow.lock())
return {};
if (!PLASTWINDOW)
return {.success = false, .error = "No window found"};
if (!PLASTWINDOW->m_sGroupData.pNextWindow.lock())
return {.success = false, .error = "Window not in a group"};
if ((!BACK && PLASTWINDOW->m_sGroupData.pNextWindow->m_sGroupData.head) || (BACK && PLASTWINDOW->m_sGroupData.head)) {
std::swap(PLASTWINDOW->m_sGroupData.head, PLASTWINDOW->m_sGroupData.pNextWindow->m_sGroupData.head);
@@ -3138,7 +3178,7 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
PWINDOW->m_sWindowData.activeBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
else
PWINDOW->m_sWindowData.inactiveBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP);
} else if (auto search = g_pConfigManager->mbWindowProperties.find(PROP); search != g_pConfigManager->mbWindowProperties.end()) {
} else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end()) {
auto pWindowDataElement = search->second(PWINDOW);
if (VAL == "toggle")
*pWindowDataElement = CWindowOverridableVar(!pWindowDataElement->valueOrDefault(), PRIORITY_SET_PROP);
@@ -3146,12 +3186,12 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
pWindowDataElement->unset(PRIORITY_SET_PROP);
else
*pWindowDataElement = CWindowOverridableVar((bool)configStringToInt(VAL).value_or(0), PRIORITY_SET_PROP);
} else if (auto search = g_pConfigManager->miWindowProperties.find(PROP); search != g_pConfigManager->miWindowProperties.end()) {
} else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end()) {
if (VAL == "unset")
search->second(PWINDOW)->unset(PRIORITY_SET_PROP);
else if (const auto V = configStringToInt(VAL); V)
*(search->second(PWINDOW)) = CWindowOverridableVar((int)*V, PRIORITY_SET_PROP);
} else if (auto search = g_pConfigManager->mfWindowProperties.find(PROP); search != g_pConfigManager->mfWindowProperties.end()) {
} else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end()) {
if (VAL == "unset")
search->second(PWINDOW)->unset(PRIORITY_SET_PROP);
else {

View File

@@ -148,7 +148,7 @@ class CKeybindManager {
static bool tryMoveFocusToMonitor(PHLMONITOR monitor);
static void moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& dir = "");
static void moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection);
static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO);
static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool preserveFocusHistory = false);
static uint64_t spawnRawProc(std::string, PHLWORKSPACE pInitialWorkspace);
static uint64_t spawnWithRules(std::string, PHLWORKSPACE pInitialWorkspace);
@@ -199,6 +199,7 @@ class CKeybindManager {
static SDispatchResult circleNext(std::string);
static SDispatchResult focusWindow(std::string);
static SDispatchResult tagWindow(std::string);
static SDispatchResult toggleSwallow(std::string);
static SDispatchResult setSubmap(std::string);
static SDispatchResult pass(std::string);
static SDispatchResult sendshortcut(std::string);

View File

@@ -17,6 +17,9 @@
#include <cstring>
#include <gbm.h>
#include <cairo/cairo.h>
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
CPointerManager::CPointerManager() {
hooks.monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any data) {
@@ -271,7 +274,7 @@ void CPointerManager::updateCursorBackend() {
continue;
}
if (state->softwareLocks > 0 || g_pConfigManager->shouldUseSoftwareCursors() || !attemptHardwareCursor(state)) {
if (state->softwareLocks > 0 || g_pConfigManager->shouldUseSoftwareCursors(m) || !attemptHardwareCursor(state)) {
Debug::log(TRACE, "Output {} rejected hardware cursors, falling back to sw", m->szName);
state->box = getCursorBoxLogicalForMonitor(state->monitor.lock());
state->hardwareFailed = true;
@@ -310,11 +313,18 @@ void CPointerManager::onCursorMoved() {
recalc = true;
}
if (state->hardwareFailed || !state->entered)
if (!state->entered)
continue;
CScopeGuard x([m] { m->onCursorMovedOnMonitor(); });
if (state->hardwareFailed)
continue;
const auto CURSORPOS = getCursorPosForMonitor(m);
m->output->moveCursor(CURSORPOS, m->shouldSkipScheduleFrameOnMouseEvent());
state->monitor->scanoutNeedsCursorUpdate = true;
}
if (recalc)
@@ -374,6 +384,8 @@ bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquam
if (!state->monitor->shouldSkipScheduleFrameOnMouseEvent())
g_pCompositor->scheduleFrameForMonitor(state->monitor.lock(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
state->monitor->scanoutNeedsCursorUpdate = true;
return true;
}
@@ -411,7 +423,9 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
}
}
state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, state->monitor->output->getBackend());
auto backend = state->monitor->output->getBackend();
auto primary = backend->getPrimary();
state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, primary ? primary.lock() : backend);
}
auto options = state->monitor->cursorSwapchain->currentOptions();
@@ -475,7 +489,7 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
if (flipRB) {
for (size_t i = 0; i < shmBuffer.size(); i += 4) {
std::swap(shmBuffer.at(i), shmBuffer.at(i + 2)); // little-endian!!!!!!
std::swap(shmBuffer[i], shmBuffer[i + 2]); // little-endian!!!!!!
}
}
} else {
@@ -607,14 +621,10 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* no
box.y = std::round(box.y);
CTexPassElement::SRenderData data;
data.tex = texture;
data.box = box.round();
data.syncTimeline = currentCursorImage.waitTimeline;
data.syncPoint = currentCursorImage.waitPoint;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
data.tex = texture;
data.box = box.round();
currentCursorImage.waitTimeline.reset();
currentCursorImage.waitPoint = 0;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);
@@ -729,7 +739,8 @@ void CPointerManager::damageIfSoftware() {
if (mw->monitor.expired() || !mw->monitor->output)
continue;
if ((mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors()) && b.overlaps({mw->monitor->vecPosition, mw->monitor->vecSize})) {
if ((mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(mw->monitor.lock())) &&
b.overlaps({mw->monitor->vecPosition, mw->monitor->vecSize})) {
g_pHyprRenderer->damageBox(b, mw->monitor->shouldSkipScheduleFrameOnMouseEvent());
break;
}

View File

@@ -148,8 +148,6 @@ class CPointerManager {
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
SP<CSyncTimeline> waitTimeline = nullptr;
uint64_t waitPoint = 0;
} currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors
Vector2D pointerPos = {0, 0};

View File

@@ -57,15 +57,23 @@
#include "../protocols/core/Output.hpp"
#include "../protocols/core/Shm.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../protocols/XXColorManagement.hpp"
#include "../protocols/FrogColorManagement.hpp"
#include "../protocols/ContentType.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Renderer.hpp"
#include "../Compositor.hpp"
#include "content-type-v1.hpp"
#include <aquamarine/buffer/Buffer.hpp>
#include <aquamarine/backend/Backend.hpp>
// ********************************************************************************************
// * IMPORTANT: make sure to .reset() any protocol UP's you create! (put reset in destructor) *
// * otherwise Hyprland might crash when exiting. *
// ********************************************************************************************
void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
const bool ISMIRROR = pMonitor->isMirror();
@@ -81,14 +89,18 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
PROTO::outputs.emplace(pMonitor->szName, makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
}
if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription())
PROTO::colorManagement->onImagePreferredChanged();
if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription()) {
Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id");
PROTO::colorManagement->onImagePreferredChanged(0);
}
}
CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static const auto PENABLEXXCM = CConfigValue<Hyprlang::INT>("experimental:xx_color_management_v4");
static const auto PDEBUGCM = CConfigValue<Hyprlang::INT>("debug:full_cm_proto");
// Outputs are a bit dumb, we have to agree.
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
@@ -145,7 +157,7 @@ CProtocolManager::CProtocolManager() {
PROTO::constraints = makeUnique<CPointerConstraintsProtocol>(&zwp_pointer_constraints_v1_interface, 1, "PointerConstraints");
PROTO::outputPower = makeUnique<COutputPowerProtocol>(&zwlr_output_power_manager_v1_interface, 1, "OutputPower");
PROTO::activation = makeUnique<CXDGActivationProtocol>(&xdg_activation_v1_interface, 1, "XDGActivation");
PROTO::idle = makeUnique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
PROTO::idle = makeUnique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 2, "IdleNotify");
PROTO::lockNotify = makeUnique<CLockNotifyProtocol>(&hyprland_lock_notifier_v1_interface, 1, "IdleNotify");
PROTO::sessionLock = makeUnique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
PROTO::ime = makeUnique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
@@ -164,17 +176,23 @@ CProtocolManager::CProtocolManager() {
PROTO::screencopy = makeUnique<CScreencopyProtocol>(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy");
PROTO::toplevelExport = makeUnique<CToplevelExportProtocol>(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport");
PROTO::globalShortcuts = makeUnique<CGlobalShortcutsProtocol>(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts");
PROTO::xdgDialog = makeUnique<CXDGDialogProtocol>(&xdg_dialog_v1_interface, 1, "XDGDialog");
PROTO::xdgDialog = makeUnique<CXDGDialogProtocol>(&xdg_wm_dialog_v1_interface, 1, "XDGDialog");
PROTO::singlePixel = makeUnique<CSinglePixelProtocol>(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel");
PROTO::securityContext = makeUnique<CSecurityContextProtocol>(&wp_security_context_manager_v1_interface, 1, "SecurityContext");
PROTO::ctm = makeUnique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl");
PROTO::ctm = makeUnique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl");
PROTO::hyprlandSurface = makeUnique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface");
PROTO::contentType = makeUnique<CContentTypeProtocol>(&wp_content_type_manager_v1_interface, 1, "ContentType");
if (*PENABLEXXCM) {
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "ColorManagement");
if (*PENABLECM)
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM);
if (*PENABLEXXCM && *PENABLECM) {
PROTO::xxColorManagement = makeUnique<CXXColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "XXColorManagement");
PROTO::frogColorManagement = makeUnique<CFrogColorManagementProtocol>(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement");
}
// ! please read the top of this file before adding another protocol
for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue;
@@ -249,6 +267,10 @@ CProtocolManager::~CProtocolManager() {
PROTO::securityContext.reset();
PROTO::ctm.reset();
PROTO::hyprlandSurface.reset();
PROTO::contentType.reset();
PROTO::colorManagement.reset();
PROTO::xxColorManagement.reset();
PROTO::frogColorManagement.reset();
PROTO::lease.reset();
PROTO::sync.reset();

View File

@@ -606,11 +606,11 @@ void CSeatManager::setGrab(SP<CSeatGrab> grab) {
if (!refocus) {
surf = CWLSurface::fromResource(currentFocus);
layer = surf->getLayer();
layer = surf ? surf->getLayer() : nullptr;
}
if (!refocus && !layer) {
auto popup = surf->getPopup();
auto popup = surf ? surf->getPopup() : nullptr;
if (popup) {
auto parent = popup->getT1Owner();
layer = parent->getLayer();

View File

@@ -89,6 +89,13 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
g_pCompositor->focusSurface(nullptr);
g_pSeatManager->setGrab(nullptr);
// Normally the locked event is sent after each output rendered a lock screen frame.
// When there are no outputs, send it right away.
if (g_pCompositor->m_bUnsafeState) {
m_pSessionLock->lock->sendLocked();
m_pSessionLock->m_hasSentLocked = true;
}
}
bool CSessionLockManager::isSessionLocked() {
@@ -131,8 +138,7 @@ void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) {
if (!m_pSessionLock || m_pSessionLock->m_hasSentLocked)
return;
m_pSessionLock->m_lockedMonitors.emplace(id);
const auto MONITORS = g_pCompositor->m_vMonitors;
const bool LOCKED = std::all_of(MONITORS.begin(), MONITORS.end(), [this](auto m) { return m_pSessionLock->m_lockedMonitors.contains(m->ID); });
const bool LOCKED = std::ranges::all_of(g_pCompositor->m_vMonitors, [this](auto m) { return m_pSessionLock->m_lockedMonitors.contains(m->ID); });
if (LOCKED && m_pSessionLock->lock->good()) {
m_pSessionLock->lock->sendLocked();
m_pSessionLock->m_hasSentLocked = true;

View File

@@ -156,9 +156,7 @@ void CXCursorManager::loadTheme(std::string const& name, int size, float scale)
cursors.emplace_back(cursor);
}
static auto SYNCGSETTINGS = CConfigValue<Hyprlang::INT>("cursor:sync_gsettings_theme");
if (*SYNCGSETTINGS)
syncGsettings();
syncGsettings();
}
SP<SXCursors> CXCursorManager::getShape(std::string const& shape, int size, float scale) {
@@ -482,7 +480,7 @@ std::vector<SP<SXCursors>> CXCursorManager::loadStandardCursors(std::string cons
// load the default xcursor shapes that exist in the theme
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
std::string shape{XCURSOR_STANDARD_NAMES.at(i)};
std::string shape{XCURSOR_STANDARD_NAMES[i]};
auto xImages = XcursorShapeLoadImages(i << 1 /* wtf xcursor? */, name.c_str(), size);
if (!xImages) {
@@ -560,6 +558,10 @@ std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& pa
}
void CXCursorManager::syncGsettings() {
static auto SYNCGSETTINGS = CConfigValue<Hyprlang::INT>("cursor:sync_gsettings_theme");
if (!*SYNCGSETTINGS)
return;
auto checkParamExists = [](std::string const& paramName, std::string const& category) {
auto* gSettingsSchemaSource = g_settings_schema_source_get_default();

View File

@@ -13,10 +13,10 @@ extern "C" {
// gangsta bootleg XCursor impl. adidas balkanized
struct SXCursorImage {
Vector2D size;
Vector2D hotspot;
std::vector<uint32_t> pixels; // XPixel is a u32
uint32_t delay; // animation delay to next frame (ms)
Hyprutils::Math::Vector2D size;
Hyprutils::Math::Vector2D hotspot;
std::vector<uint32_t> pixels; // XPixel is a u32
uint32_t delay; // animation delay to next frame (ms)
};
struct SXCursors {

View File

@@ -55,7 +55,7 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) {
if (pWindow->m_bIsX11) {
if (activate) {
pWindow->sendWindowSize(pWindow->m_vRealSize->value(), true); // update xwayland output pos
pWindow->sendWindowSize(true); // update xwayland output pos
pWindow->m_pXWaylandSurface->setMinimized(false);
if (!pWindow->isX11OverrideRedirect())
@@ -82,29 +82,9 @@ CBox CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow) {
CBox box;
if (pWindow->m_bIsX11) {
const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get();
if (SIZEHINTS && !pWindow->isX11OverrideRedirect()) {
// WM_SIZE_HINTS' x,y,w,h is deprecated it seems.
// Source: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#wm_normal_hints_property
box.x = pWindow->m_pXWaylandSurface->geometry.x;
box.y = pWindow->m_pXWaylandSurface->geometry.y;
constexpr int ICCCM_USSize = 0x2;
constexpr int ICCCM_PSize = 0x8;
if ((SIZEHINTS->flags & ICCCM_USSize) || (SIZEHINTS->flags & ICCCM_PSize)) {
box.w = SIZEHINTS->base_width;
box.h = SIZEHINTS->base_height;
} else {
box.w = pWindow->m_pXWaylandSurface->geometry.w;
box.h = pWindow->m_pXWaylandSurface->geometry.h;
}
} else
box = pWindow->m_pXWaylandSurface->geometry;
} else if (pWindow->m_pXDGSurface)
if (pWindow->m_bIsX11)
box = pWindow->m_pXWaylandSurface->geometry;
else if (pWindow->m_pXDGSurface)
box = pWindow->m_pXDGSurface->current.geometry;
return box;

View File

@@ -11,11 +11,12 @@
#include <ctime>
#include <aquamarine/backend/Backend.hpp>
using namespace Hyprutils::OS;
#define TIMESPEC_NSEC_PER_SEC 1000000000L
CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop) {
m_sTimers.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
m_sTimers.timerfd = CFileDescriptor{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)};
m_sWayland.loop = wlEventLoop;
m_sWayland.display = display;
}
@@ -31,8 +32,6 @@ CEventLoopManager::~CEventLoopManager() {
wl_event_source_remove(m_sIdle.eventSource);
if (m_configWatcherInotifySource)
wl_event_source_remove(m_configWatcherInotifySource);
if (m_sTimers.timerfd >= 0)
close(m_sTimers.timerfd);
}
static int timerWrite(int fd, uint32_t mask, void* data) {
@@ -52,10 +51,10 @@ static int configWatcherWrite(int fd, uint32_t mask, void* data) {
}
void CEventLoopManager::enterLoop() {
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr);
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr);
if (const auto FD = g_pConfigWatcher->getInotifyFD(); FD >= 0)
m_configWatcherInotifySource = wl_event_loop_add_fd(m_sWayland.loop, FD, WL_EVENT_READABLE, configWatcherWrite, nullptr);
if (const auto& FD = g_pConfigWatcher->getInotifyFD(); FD.isValid())
m_configWatcherInotifySource = wl_event_loop_add_fd(m_sWayland.loop, FD.get(), WL_EVENT_READABLE, configWatcherWrite, nullptr);
syncPollFDs();
m_sListeners.pollFDsChanged = g_pCompositor->m_pAqBackend->events.pollFDsChanged.registerListener([this](std::any d) { syncPollFDs(); });
@@ -120,7 +119,7 @@ void CEventLoopManager::nudgeTimers() {
itimerspec ts = {.it_value = now};
timerfd_settime(m_sTimers.timerfd, TFD_TIMER_ABSTIME, &ts, nullptr);
timerfd_settime(m_sTimers.timerfd.get(), TFD_TIMER_ABSTIME, &ts, nullptr);
}
void CEventLoopManager::doLater(const std::function<void()>& fn) {

View File

@@ -6,6 +6,7 @@
#include <thread>
#include <wayland-server.h>
#include "helpers/signal/Signal.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
#include "EventLoopTimer.hpp"
@@ -54,7 +55,7 @@ class CEventLoopManager {
struct {
std::vector<SP<CEventLoopTimer>> timers;
int timerfd = -1;
Hyprutils::OS::CFileDescriptor timerfd;
} m_sTimers;
SIdleData m_sIdle;

View File

@@ -90,14 +90,30 @@ CInputManager::~CInputManager() {
void CInputManager::onMouseMoved(IPointer::SMotionEvent e) {
static auto PNOACCEL = CConfigValue<Hyprlang::INT>("input:force_no_accel");
const auto DELTA = *PNOACCEL == 1 ? e.unaccel : e.delta;
Vector2D delta = e.delta;
Vector2D unaccel = e.unaccel;
if (e.device) {
if (e.device->isTouchpad) {
if (e.device->flipX) {
delta.x = -delta.x;
unaccel.x = -unaccel.x;
}
if (e.device->flipY) {
delta.y = -delta.y;
unaccel.y = -unaccel.y;
}
}
}
const auto DELTA = *PNOACCEL == 1 ? unaccel : delta;
if (g_pSeatManager->isPointerFrameSkipped)
g_pPointerManager->storeMovement((uint64_t)e.timeMs, DELTA, e.unaccel);
g_pPointerManager->storeMovement((uint64_t)e.timeMs, DELTA, unaccel);
else
g_pPointerManager->setStoredMovement((uint64_t)e.timeMs, DELTA, e.unaccel);
g_pPointerManager->setStoredMovement((uint64_t)e.timeMs, DELTA, unaccel);
PROTO::relativePointer->sendRelativeMotion((uint64_t)e.timeMs * 1000, DELTA, e.unaccel);
PROTO::relativePointer->sendRelativeMotion((uint64_t)e.timeMs * 1000, DELTA, unaccel);
if (e.mouse)
recheckMouseWarpOnMouseInput();
@@ -161,17 +177,23 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
if (MOUSECOORDSFLOORED == m_vLastCursorPosFloored && !refocus)
return;
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PMOUSEREFOCUS = CConfigValue<Hyprlang::INT>("input:mouse_refocus");
static auto PFOLLOWONDND = CConfigValue<Hyprlang::INT>("misc:always_follow_on_dnd");
static auto PFLOATBEHAVIOR = CConfigValue<Hyprlang::INT>("input:float_switch_override_focus");
static auto PMOUSEFOCUSMON = CConfigValue<Hyprlang::INT>("misc:mouse_move_focuses_monitor");
static auto PRESIZEONBORDER = CConfigValue<Hyprlang::INT>("general:resize_on_border");
static auto PRESIZECURSORICON = CConfigValue<Hyprlang::INT>("general:hover_icon_on_border");
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PFOLLOWMOUSETHRESHOLD = CConfigValue<Hyprlang::FLOAT>("input:follow_mouse_threshold");
static auto PMOUSEREFOCUS = CConfigValue<Hyprlang::INT>("input:mouse_refocus");
static auto PFOLLOWONDND = CConfigValue<Hyprlang::INT>("misc:always_follow_on_dnd");
static auto PFLOATBEHAVIOR = CConfigValue<Hyprlang::INT>("input:float_switch_override_focus");
static auto PMOUSEFOCUSMON = CConfigValue<Hyprlang::INT>("misc:mouse_move_focuses_monitor");
static auto PRESIZEONBORDER = CConfigValue<Hyprlang::INT>("general:resize_on_border");
static auto PRESIZECURSORICON = CConfigValue<Hyprlang::INT>("general:hover_icon_on_border");
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
const auto FOLLOWMOUSE = *PFOLLOWONDND && PROTO::data->dndActive() ? 1 : *PFOLLOWMOUSE;
if (FOLLOWMOUSE == 1 && m_tmrLastCursorMovement.getSeconds() < 0.5)
m_fMousePosDelta += MOUSECOORDSFLOORED.distance(m_vLastCursorPosFloored);
else
m_fMousePosDelta = 0;
m_pFoundSurfaceToFocus.reset();
m_pFoundLSToFocus.reset();
m_pFoundWindowToFocus.reset();
@@ -271,9 +293,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
const auto BOX = HLSurface->getSurfaceBoxGlobal();
if (BOX) {
const auto PWINDOW = HLSurface->getWindow();
surfacePos = BOX->pos();
pFoundLayerSurface = HLSurface->getLayer();
pFoundWindow = HLSurface->getWindow();
pFoundWindow = !PWINDOW || PWINDOW->isHidden() ? g_pCompositor->m_pLastWindow.lock() : PWINDOW;
} else // reset foundSurface, find one normally
foundSurface = nullptr;
} else // reset foundSurface, find one normally
@@ -513,9 +536,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
// TODO: this looks wrong. When over a popup, it constantly is switching.
// Temp fix until that's figured out. Otherwise spams windowrule lookups and other shit.
if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
else
if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow) {
if (m_fMousePosDelta > *PFOLLOWMOUSETHRESHOLD || refocus)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
} else
g_pCompositor->focusSurface(foundSurface, pFoundWindow);
}
}
@@ -1173,6 +1197,9 @@ void CInputManager::setPointerConfigs() {
const auto LIBINPUTSENS = std::clamp(g_pConfigManager->getDeviceFloat(devname, "sensitivity", "input:sensitivity"), -1.f, 1.f);
libinput_device_config_accel_set_speed(LIBINPUTDEV, LIBINPUTSENS);
m->flipX = g_pConfigManager->getDeviceInt(devname, "flip_x", "input:touchpad:flip_x") != 0;
m->flipY = g_pConfigManager->getDeviceInt(devname, "flip_y", "input:touchpad:flip_y") != 0;
const auto ACCELPROFILE = g_pConfigManager->getDeviceString(devname, "accel_profile", "input:accel_profile");
const auto SCROLLPOINTS = g_pConfigManager->getDeviceString(devname, "scroll_points", "input:scroll_points");
@@ -1552,10 +1579,13 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
if (libinput_device_config_send_events_get_mode(LIBINPUTDEV) != mode)
libinput_device_config_send_events_set_mode(LIBINPUTDEV, mode);
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "transform", "input:touchdevice:transform"), 0, 7);
Debug::log(LOG, "Setting calibration matrix for device {}", PTOUCHDEV->hlName);
if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV))
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV)) {
Debug::log(LOG, "Setting calibration matrix for device {}", PTOUCHDEV->hlName);
// default value of transform being -1 means it's unset.
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "transform", "input:touchdevice:transform"), -1, 7);
if (ROTATION > -1)
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
}
auto output = g_pConfigManager->getDeviceString(PTOUCHDEV->hlName, "output", "input:touchdevice:output");
bool bound = !output.empty() && output != STRVAL_EMPTY;
@@ -1597,9 +1627,10 @@ void CInputManager::setTabletConfigs() {
const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input");
t->relativeInput = RELINPUT;
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(NAME, "transform", "input:tablet:transform"), 0, 7);
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(NAME, "transform", "input:tablet:transform"), -1, 7);
Debug::log(LOG, "Setting calibration matrix for device {}", NAME);
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
if (ROTATION > -1)
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
if (g_pConfigManager->getDeviceInt(NAME, "left_handed", "input:tablet:left_handed") == 0)
libinput_device_config_left_handed_set(LIBINPUTDEV, 0);
@@ -1700,8 +1731,11 @@ void CInputManager::releaseAllMouseButtons() {
if (PROTO::data->dndActive())
return;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
for (auto const& mb : buttonsCopy) {
g_pSeatManager->sendPointerButton(0, mb, WL_POINTER_BUTTON_STATE_RELEASED);
g_pSeatManager->sendPointerButton(now.tv_sec * 1000 + now.tv_nsec / 1000000, mb, WL_POINTER_BUTTON_STATE_RELEASED);
}
m_lCurrentlyHeldButtons.clear();

View File

@@ -255,6 +255,7 @@ class CInputManager {
// used for warping back after non-mouse input
Vector2D m_vLastMousePos = {};
double m_fMousePosDelta = 0;
bool m_bLastInputMouse = true;
// for holding focus on buttons held

View File

@@ -88,6 +88,20 @@ static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = f
PROTO::tablet->motion(tool, local);
}
static Vector2D transformToActiveRegion(const Vector2D pos, const CBox activeArea) {
auto newPos = pos;
//Calculate transformations if active area is set
if (!activeArea.empty()) {
if (!std::isnan(pos.x))
newPos.x = (pos.x - activeArea.x) / (activeArea.w - activeArea.x);
if (!std::isnan(pos.y))
newPos.y = (pos.y - activeArea.y) / (activeArea.h - activeArea.y);
}
return newPos;
}
void CInputManager::onTabletAxis(CTablet::SAxisEvent e) {
const auto PTAB = e.tablet;
const auto PTOOL = ensureTabletToolPresent(e.tool);
@@ -113,16 +127,9 @@ void CInputManager::onTabletAxis(CTablet::SAxisEvent e) {
if (PTAB->relativeInput)
g_pPointerManager->move(delta);
else {
//Calculate transformations if active area is set
if (!PTAB->activeArea.empty()) {
if (!std::isnan(x))
x = (x - PTAB->activeArea.x) / (PTAB->activeArea.w - PTAB->activeArea.x);
if (!std::isnan(y))
y = (y - PTAB->activeArea.y) / (PTAB->activeArea.h - PTAB->activeArea.y);
}
g_pPointerManager->warpAbsolute({x, y}, PTAB);
}
else
g_pPointerManager->warpAbsolute(transformToActiveRegion({x, y}, PTAB->activeArea), PTAB);
break;
}
}
@@ -160,7 +167,12 @@ void CInputManager::onTabletTip(CTablet::STipEvent e) {
const auto PTAB = e.tablet;
const auto PTOOL = ensureTabletToolPresent(e.tool);
const auto POS = e.tip;
g_pPointerManager->warpAbsolute(POS, PTAB);
if (PTAB->relativeInput)
g_pPointerManager->move({0, 0});
else
g_pPointerManager->warpAbsolute(transformToActiveRegion(POS, PTAB->activeArea), PTAB);
refocusTablet(PTAB, PTOOL, true);
if (e.in)

View File

@@ -41,7 +41,7 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
if (m_sActiveSwipe.pWorkspaceBegin) {
return;
// TODO: Don't swipe if you touched a floating window.
} else if (*PSWIPETOUCH && (m_pFoundLSToFocus.expired() || m_pFoundLSToFocus->layer <= 1)) {
} else if (*PSWIPETOUCH && (m_pFoundLSToFocus.expired() || m_pFoundLSToFocus->layer <= 1) && !g_pSessionLockManager->isSessionLocked()) {
const auto PWORKSPACE = PMONITOR->activeWorkspace;
const auto STYLE = PWORKSPACE->m_vRenderOffset->getStyle();
const bool VERTANIMS = STYLE == "slidevert" || STYLE.starts_with("slidefadevert");

View File

@@ -32,6 +32,7 @@ executable(
xcb_xfixes_dep,
backtrace_dep,
epoll_dep,
inotify_dep,
gio_dep,
tracy,

View File

@@ -59,6 +59,13 @@ struct SVersionInfo {
#define OPTIONAL
#define HANDLE void*
// C ABI is needed to prevent symbol mangling, but we don't actually need C compatibility,
// so we ignore this warning about return types that are potentially incompatible with C.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#endif
class IHyprLayout;
class CWindow;
class IHyprWindowDecoration;
@@ -315,3 +322,7 @@ APICALL inline EXPORT const char* __hyprland_api_get_client_hash() {
return GIT_COMMIT_HASH;
}
// NOLINTEND
#ifdef __clang__
#pragma clang diagnostic pop
#endif

View File

@@ -15,6 +15,9 @@ CHyprlandCTMControlResource::CHyprlandCTMControlResource(SP<CHyprlandCtmControlM
resource->setSetCtmForOutput([this](CHyprlandCtmControlManagerV1* r, wl_resource* output, wl_fixed_t mat0, wl_fixed_t mat1, wl_fixed_t mat2, wl_fixed_t mat3, wl_fixed_t mat4,
wl_fixed_t mat5, wl_fixed_t mat6, wl_fixed_t mat7, wl_fixed_t mat8) {
if (blocked)
return;
const auto OUTPUTRESOURCE = CWLOutputResource::fromResource(output);
if UNLIKELY (!OUTPUTRESOURCE)
@@ -41,6 +44,9 @@ CHyprlandCTMControlResource::CHyprlandCTMControlResource(SP<CHyprlandCtmControlM
});
resource->setCommit([this](CHyprlandCtmControlManagerV1* r) {
if (blocked)
return;
LOGM(LOG, "Committing ctms to outputs");
for (auto& m : g_pCompositor->m_vMonitors) {
@@ -54,7 +60,17 @@ CHyprlandCTMControlResource::CHyprlandCTMControlResource(SP<CHyprlandCtmControlM
});
}
void CHyprlandCTMControlResource::block() {
blocked = true;
if (resource->version() >= 2)
resource->sendBlocked();
}
CHyprlandCTMControlResource::~CHyprlandCTMControlResource() {
if (blocked)
return;
for (auto& m : g_pCompositor->m_vMonitors) {
PROTO::ctm->setCTM(m, Mat3x3::identity());
}
@@ -69,7 +85,6 @@ CHyprlandCTMControlProtocol::CHyprlandCTMControlProtocol(const wl_interface* ifa
}
void CHyprlandCTMControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CHyprlandCTMControlResource>(makeShared<CHyprlandCtmControlManagerV1>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
@@ -78,6 +93,11 @@ void CHyprlandCTMControlProtocol::bindManager(wl_client* client, void* data, uin
return;
}
if (m_pManager)
RESOURCE->block();
else
m_pManager = RESOURCE;
LOGM(LOG, "New CTM Manager at 0x{:x}", (uintptr_t)RESOURCE.get());
}

View File

@@ -16,11 +16,13 @@ class CHyprlandCTMControlResource {
~CHyprlandCTMControlResource();
bool good();
void block();
private:
SP<CHyprlandCtmControlManagerV1> resource;
std::unordered_map<std::string, Mat3x3> ctms;
bool blocked = false;
};
class CHyprlandCTMControlProtocol : public IWaylandProtocol {
@@ -37,6 +39,7 @@ class CHyprlandCTMControlProtocol : public IWaylandProtocol {
//
std::vector<SP<CHyprlandCTMControlResource>> m_vManagers;
WP<CHyprlandCTMControlResource> m_pManager;
//
struct SCTMData {

View File

@@ -1,42 +1,90 @@
#include "ColorManagement.hpp"
#include "Compositor.hpp"
#include "color-management-v1.hpp"
#include "../helpers/Monitor.hpp"
#include "core/Output.hpp"
#include "types/ColorManagement.hpp"
#include <cstdint>
CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resource_) {
using namespace NColorManagement;
static uint64_t lastImageID = 0; // FIXME use for deduplication
CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_EXTENDED_TARGET_VOLUME);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES);
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB);
}
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_BT2020);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_NTSC);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB);
resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_ABSOLUTE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE_BPC);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ);
}
resource->setDestroy([](CXxColorManagerV4* r) { LOGM(TRACE, "Destroy xx_color_manager at {:x} (generated default)", (uintptr_t)r); });
resource->setGetOutput([](CXxColorManagerV4* r, uint32_t id, wl_resource* output) {
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428);
}
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE);
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION);
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_ABSOLUTE);
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE_BPC);
}
m_resource->setDestroy([](CWpColorManagerV1* r) { LOGM(TRACE, "Destroy WP_color_manager at {:x} (generated default)", (uintptr_t)r); });
m_resource->setGetOutput([](CWpColorManagerV1* r, uint32_t id, wl_resource* output) {
LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output);
const auto OUTPUTRESOURCE = CWLOutputResource::fromResource(output);
if UNLIKELY (!OUTPUTRESOURCE) {
r->error(-1, "Invalid output (2)");
return;
}
const auto PMONITOR = OUTPUTRESOURCE->monitor.lock();
if UNLIKELY (!PMONITOR) {
r->error(-1, "Invalid output (2)");
return;
}
const auto RESOURCE =
PROTO::colorManagement->m_vOutputs.emplace_back(makeShared<CColorManagementOutput>(makeShared<CXxColorManagementOutputV4>(r->client(), r->version(), id)));
PROTO::colorManagement->m_vOutputs.emplace_back(makeShared<CColorManagementOutput>(makeShared<CWpColorManagementOutputV1>(r->client(), r->version(), id), PMONITOR));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -46,7 +94,7 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
RESOURCE->self = RESOURCE;
});
resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
m_resource->setGetSurface([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) {
LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
@@ -57,12 +105,12 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
}
if (SURF->colorManagement) {
r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists");
r->error(WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS, "CM Surface already exists");
return;
}
const auto RESOURCE =
PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared<CColorManagementSurface>(makeShared<CXxColorManagementSurfaceV4>(r->client(), r->version(), id), SURF));
PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared<CColorManagementSurface>(makeShared<CWpColorManagementSurfaceV1>(r->client(), r->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vSurfaces.pop_back();
@@ -73,7 +121,7 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
SURF->colorManagement = RESOURCE;
});
resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
m_resource->setGetSurfaceFeedback([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) {
LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
@@ -84,7 +132,7 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
}
const auto RESOURCE = PROTO::colorManagement->m_vFeedbackSurfaces.emplace_back(
makeShared<CColorManagementFeedbackSurface>(makeShared<CXxColorManagementFeedbackSurfaceV4>(r->client(), r->version(), id), SURF));
makeShared<CColorManagementFeedbackSurface>(makeShared<CWpColorManagementSurfaceFeedbackV1>(r->client(), r->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -94,15 +142,29 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
RESOURCE->self = RESOURCE;
});
resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) {
m_resource->setCreateIccCreator([](CWpColorManagerV1* r, uint32_t id) {
LOGM(WARN, "New ICC creator for id={} (unsupported)", id);
r->error(XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
return;
}
const auto RESOURCE =
PROTO::colorManagement->m_vIccCreators.emplace_back(makeShared<CColorManagementIccCreator>(makeShared<CWpImageDescriptionCreatorIccV1>(r->client(), r->version(), id)));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vIccCreators.pop_back();
return;
}
RESOURCE->self = RESOURCE;
});
resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) {
m_resource->setCreateParametricCreator([](CWpColorManagerV1* r, uint32_t id) {
LOGM(TRACE, "New parametric creator for id={}", id);
const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back(
makeShared<CColorManagementParametricCreator>(makeShared<CXxImageDescriptionCreatorParamsV4>(r->client(), r->version(), id)));
makeShared<CColorManagementParametricCreator>(makeShared<CWpImageDescriptionCreatorParamsV1>(r->client(), r->version(), id)));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -112,30 +174,59 @@ CColorManager::CColorManager(SP<CXxColorManagerV4> resource_) : resource(resourc
RESOURCE->self = RESOURCE;
});
m_resource->setCreateWindowsScrgb([](CWpColorManagerV1* r, uint32_t id) {
LOGM(WARN, "New Windows scRGB description id={} (unsupported)", id);
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Windows scRGB profiles are not supported");
return;
}
resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::colorManagement->destroyResource(this); });
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), false));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
RESOURCE->self = RESOURCE;
RESOURCE->settings.id = ++lastImageID;
RESOURCE->settings.windowsScRGB = true;
RESOURCE->settings.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB;
RESOURCE->settings.primariesNameSet = true;
RESOURCE->settings.primaries = NColorPrimaries::BT709;
RESOURCE->settings.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR;
RESOURCE->settings.luminances.reference = 203;
RESOURCE->resource()->sendReady(RESOURCE->settings.id);
});
m_resource->setOnDestroy([this](CWpColorManagerV1* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->sendDone();
}
bool CColorManager::good() {
return resource->resource();
return m_resource->resource();
}
CColorManagementOutput::CColorManagementOutput(SP<CXxColorManagementOutputV4> resource_) : resource(resource_) {
CColorManagementOutput::CColorManagementOutput(SP<CWpColorManagementOutputV1> resource, WP<CMonitor> monitor) : m_resource(resource), m_monitor(monitor) {
if UNLIKELY (!good())
return;
pClient = resource->client();
pClient = m_resource->client();
resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); });
resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); });
resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) {
m_resource->setGetImageDescription([this](CWpColorManagementOutputV1* r, uint32_t id) {
LOGM(TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id);
if (imageDescription.valid())
PROTO::colorManagement->destroyResource(imageDescription.get());
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id)));
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), true));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -144,11 +235,19 @@ CColorManagementOutput::CColorManagementOutput(SP<CXxColorManagementOutputV4> re
}
RESOURCE->self = RESOURCE;
if (!m_monitor.valid())
RESOURCE->m_resource->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, "No output");
else {
RESOURCE->settings = m_monitor->imageDescription;
if (RESOURCE->settings.id)
RESOURCE->settings.id = ++lastImageID;
RESOURCE->m_resource->sendReady(RESOURCE->settings.id); // FIXME: create correct id
}
});
}
bool CColorManagementOutput::good() {
return resource->resource();
return m_resource->resource();
}
wl_client* CColorManagementOutput::client() {
@@ -159,45 +258,45 @@ CColorManagementSurface::CColorManagementSurface(SP<CWLSurfaceResource> surface_
// only for frog cm untill wayland cm is adopted
}
CColorManagementSurface::CColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
CColorManagementSurface::CColorManagementSurface(SP<CWpColorManagementSurfaceV1> resource, SP<CWLSurfaceResource> surface_) : surface(surface_), m_resource(resource) {
if UNLIKELY (!good())
return;
pClient = resource->client();
pClient = m_resource->client();
resource->setDestroy([this](CXxColorManagementSurfaceV4* r) {
m_resource->setDestroy([this](CWpColorManagementSurfaceV1* r) {
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface);
PROTO::colorManagement->destroyResource(this);
});
resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) {
m_resource->setOnDestroy([this](CWpColorManagementSurfaceV1* r) {
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface);
PROTO::colorManagement->destroyResource(this);
});
resource->setSetImageDescription([this](CXxColorManagementSurfaceV4* r, wl_resource* image_description, uint32_t render_intent) {
m_resource->setSetImageDescription([this](CWpColorManagementSurfaceV1* r, wl_resource* image_description, uint32_t render_intent) {
LOGM(TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent);
const auto PO = (CXxImageDescriptionV4*)wl_resource_get_user_data(image_description);
const auto PO = (CWpImageDescriptionV1*)wl_resource_get_user_data(image_description);
if (!PO) { // FIXME check validity
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description creation failed");
r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, "Image description creation failed");
return;
}
if (render_intent != XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Unsupported render intent");
if (render_intent != WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL) {
r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_RENDER_INTENT, "Unsupported render intent");
return;
}
const auto imageDescription = std::find_if(PROTO::colorManagement->m_vImageDescriptions.begin(), PROTO::colorManagement->m_vImageDescriptions.end(),
[&](const auto& other) { return other->resource()->resource() == image_description; });
if (imageDescription == PROTO::colorManagement->m_vImageDescriptions.end()) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found");
r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, "Image description not found");
return;
}
setHasImageDescription(true);
m_imageDescription = imageDescription->get()->settings;
});
resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) {
m_resource->setUnsetImageDescription([this](CWpColorManagementSurfaceV1* r) {
LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r);
m_imageDescription = SImageDescription{};
setHasImageDescription(false);
@@ -205,7 +304,7 @@ CColorManagementSurface::CColorManagementSurface(SP<CXxColorManagementSurfaceV4>
}
bool CColorManagementSurface::good() {
return resource && resource->resource();
return m_resource && m_resource->resource();
}
wl_client* CColorManagementSurface::client() {
@@ -240,34 +339,34 @@ bool CColorManagementSurface::needsHdrMetadataUpdate() {
return m_needsNewMetadata;
}
CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) :
surface(surface_), resource(resource_) {
CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorManagementSurfaceFeedbackV1> resource, SP<CWLSurfaceResource> surface_) :
surface(surface_), m_resource(resource) {
if UNLIKELY (!good())
return;
pClient = resource->client();
pClient = m_resource->client();
resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
m_resource->setDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) {
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
PROTO::colorManagement->destroyResource(this);
});
resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
m_resource->setOnDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) {
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
PROTO::colorManagement->destroyResource(this);
});
resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) {
m_resource->setGetPreferred([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) {
LOGM(TRACE, "Get preferred for id {}", id);
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id), true));
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), true));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -279,44 +378,130 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CXxColorMana
m_currentPreferred = RESOURCE;
m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription();
if (!m_currentPreferred->settings.id)
m_currentPreferred->settings.id = ++lastImageID;
RESOURCE->resource()->sendReady(id);
RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id
});
m_resource->setGetPreferredParametric([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) {
LOGM(TRACE, "Get preferred for id {}", id);
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Parametric descriptions are not supported");
return;
}
if (m_currentPreferred.valid())
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), true));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
RESOURCE->self = RESOURCE;
m_currentPreferred = RESOURCE;
m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription();
if (!PROTO::colorManagement->m_debug && m_currentPreferred->settings.icc.fd) {
LOGM(ERR, "FIXME: parse icc profile");
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
return;
}
RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id
});
}
bool CColorManagementFeedbackSurface::good() {
return resource->resource();
return m_resource->resource();
}
wl_client* CColorManagementFeedbackSurface::client() {
return pClient;
}
CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_) : resource(resource_) {
CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCreatorIccV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
//
pClient = resource->client();
pClient = m_resource->client();
resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CWpImageDescriptionCreatorIccV1* r) { PROTO::colorManagement->destroyResource(this); });
resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) {
LOGM(TRACE, "Create image description from params for id {}", id);
m_resource->setCreate([this](CWpImageDescriptionCreatorIccV1* r, uint32_t id) {
LOGM(TRACE, "Create image description from icc for id {}", id);
// FIXME actually check completeness
if (!valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Missing required settings");
return;
}
// FIXME actually check consistency
if (!valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCONSISTENT_SET, "Set is not consistent");
if (settings.icc.fd < 0 || !settings.icc.length) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings");
return;
}
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id)));
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), false));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::colorManagement->m_vImageDescriptions.pop_back();
return;
}
LOGM(ERR, "FIXME: Parse icc file {}({},{}) for id {}", settings.icc.fd, settings.icc.offset, settings.icc.length, id);
// FIXME actually check support
if (settings.icc.fd < 0 || !settings.icc.length) {
RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported");
return;
}
RESOURCE->self = RESOURCE;
RESOURCE->settings = settings;
settings.id = ++lastImageID;
RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id
PROTO::colorManagement->destroyResource(this);
});
m_resource->setSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) {
settings.icc.fd = fd;
settings.icc.offset = offset;
settings.icc.length = length;
});
}
bool CColorManagementIccCreator::good() {
return m_resource->resource();
}
wl_client* CColorManagementIccCreator::client() {
return pClient;
}
CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImageDescriptionCreatorParamsV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
//
pClient = m_resource->client();
m_resource->setOnDestroy([this](CWpImageDescriptionCreatorParamsV1* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setCreate([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t id) {
LOGM(TRACE, "Create image description from params for id {}", id);
// FIXME actually check completeness
if (!valuesSet) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings");
return;
}
const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back(
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), false));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
@@ -326,128 +511,168 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CXxImage
// FIXME actually check support
if (!valuesSet) {
RESOURCE->resource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported");
RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported");
return;
}
RESOURCE->self = RESOURCE;
RESOURCE->settings = settings;
RESOURCE->resource()->sendReady(id);
settings.id = ++lastImageID;
RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id
PROTO::colorManagement->destroyResource(this);
});
resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) {
m_resource->setSetTfNamed([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t tf) {
LOGM(TRACE, "Set image description transfer function to {}", tf);
if (valuesSet & PC_TF) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function already set");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Transfer function already set");
return;
}
switch (tf) {
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: break;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG: break;
default: r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Unsupported transfer function"); return;
}
settings.transferFunction = (xxColorManagerV4TransferFunction)tf;
settings.transferFunction = convertTransferFunction((wpColorManagerV1TransferFunction)tf);
valuesSet |= PC_TF;
});
resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) {
m_resource->setSetTfPower([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t eexp) {
LOGM(TRACE, "Set image description tf power to {}", eexp);
if (valuesSet & PC_TF_POWER) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function power already set");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Transfer function power already set");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "TF power is not supported");
return;
}
settings.transferFunctionPower = eexp / 10000.0f;
if (settings.transferFunctionPower < 1.0 || settings.transferFunctionPower > 10.0) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Power should be between 1.0 and 10.0");
return;
}
valuesSet |= PC_TF_POWER;
});
resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) {
m_resource->setSetPrimariesNamed([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t primaries) {
LOGM(TRACE, "Set image description primaries by name {}", primaries);
if (valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set");
return;
}
switch (primaries) {
case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB:
settings.primariesNameSet = true;
settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_SRGB;
settings.primaries = NColorPrimaries::BT709;
valuesSet |= PC_PRIMARIES;
break;
case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020:
settings.primariesNameSet = true;
settings.primariesNamed = XX_COLOR_MANAGER_V4_PRIMARIES_BT2020;
settings.primaries = NColorPrimaries::BT2020;
valuesSet |= PC_PRIMARIES;
break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries");
case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB:
case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020:
case WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M:
case WP_COLOR_MANAGER_V1_PRIMARIES_PAL:
case WP_COLOR_MANAGER_V1_PRIMARIES_NTSC:
case WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM:
case WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB: break;
default:
if (!PROTO::colorManagement->m_debug) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "Unsupported primaries");
return;
}
}
settings.primariesNameSet = true;
settings.primariesNamed = convertPrimaries((wpColorManagerV1Primaries)primaries);
settings.primaries = getPrimaries(settings.primariesNamed);
valuesSet |= PC_PRIMARIES;
});
resource->setSetPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
m_resource->setSetPrimaries(
[this](CWpImageDescriptionCreatorParamsV1* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
LOGM(TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
if (valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Custom primaries aren't supported");
return;
}
settings.primariesNameSet = false;
settings.primaries =
SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
settings.primaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f},
.green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f},
.blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f},
.white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}};
valuesSet |= PC_PRIMARIES;
});
resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) {
m_resource->setSetLuminances([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) {
auto min = min_lum / 10000.0f;
LOGM(TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum);
if (valuesSet & PC_LUMINANCES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Luminances already set");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Luminances already set");
return;
}
if (max_lum < reference_lum || reference_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum};
valuesSet |= PC_LUMINANCES;
});
resource->setSetMasteringDisplayPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
m_resource->setSetMasteringDisplayPrimaries(
[this](CWpImageDescriptionCreatorParamsV1* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
LOGM(TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
// if (valuesSet & PC_MASTERING_PRIMARIES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering primaries already set");
// return;
// }
settings.masteringPrimaries =
SImageDescription::SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
if (valuesSet & PC_MASTERING_PRIMARIES) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering primaries already set");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering primaries are not supported");
return;
}
settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f},
.green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f},
.blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f},
.white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}};
valuesSet |= PC_MASTERING_PRIMARIES;
// FIXME:
// If a compositor additionally supports target color volume exceeding the primary color volume, it must advertise wp_color_manager_v1.feature.extended_target_volume.
// If a client uses target color volume exceeding the primary color volume and the compositor does not support it, the result is implementation defined.
// Compositors are recommended to detect this case and fail the image description gracefully, but it may as well result in color artifacts.
});
resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) {
m_resource->setSetMasteringLuminance([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t min_lum, uint32_t max_lum) {
auto min = min_lum / 10000.0f;
LOGM(TRACE, "Set image description mastering luminances to {} - {}", min, max_lum);
// if (valuesSet & PC_MASTERING_LUMINANCES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering luminances already set");
// r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering luminances already set");
// return;
// }
if (min > 0 && max_lum > 0 && max_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering luminances are not supported");
return;
}
settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum};
valuesSet |= PC_MASTERING_LUMINANCES;
});
resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) {
m_resource->setSetMaxCll([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t max_cll) {
LOGM(TRACE, "Set image description max content light level to {}", max_cll);
// if (valuesSet & PC_CLL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max CLL already set");
// r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max CLL already set");
// return;
// }
settings.maxCLL = max_cll;
valuesSet |= PC_CLL;
});
resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) {
m_resource->setSetMaxFall([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t max_fall) {
LOGM(TRACE, "Set image description max frame-average light level to {}", max_fall);
// if (valuesSet & PC_FALL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max FALL already set");
// r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max FALL already set");
// return;
// }
settings.maxFALL = max_fall;
@@ -456,31 +681,31 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CXxImage
}
bool CColorManagementParametricCreator::good() {
return resource->resource();
return m_resource->resource();
}
wl_client* CColorManagementParametricCreator::client() {
return pClient;
}
CColorManagementImageDescription::CColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation) :
m_resource(resource_), m_allowGetInformation(allowGetInformation) {
CColorManagementImageDescription::CColorManagementImageDescription(SP<CWpImageDescriptionV1> resource, bool allowGetInformation) :
m_resource(resource), m_allowGetInformation(allowGetInformation) {
if UNLIKELY (!good())
return;
pClient = m_resource->client();
m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); });
m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) {
m_resource->setGetInformation([this](CWpImageDescriptionV1* r, uint32_t id) {
LOGM(TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id);
if (!m_allowGetInformation) {
r->error(XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request");
r->error(WP_IMAGE_DESCRIPTION_V1_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request");
return;
}
auto RESOURCE = makeShared<CColorManagementImageDescriptionInfo>(makeShared<CXxImageDescriptionInfoV4>(r->client(), r->version(), id), settings);
auto RESOURCE = makeShared<CColorManagementImageDescriptionInfo>(makeShared<CWpImageDescriptionInfoV1>(r->client(), r->version(), id), settings);
if UNLIKELY (!RESOURCE->good())
r->noMemory();
@@ -498,12 +723,12 @@ wl_client* CColorManagementImageDescription::client() {
return pClient;
}
SP<CXxImageDescriptionV4> CColorManagementImageDescription::resource() {
SP<CWpImageDescriptionV1> CColorManagementImageDescription::resource() {
return m_resource;
}
CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const SImageDescription& settings_) :
m_resource(resource_), settings(settings_) {
CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CWpImageDescriptionInfoV1> resource, const SImageDescription& settings_) :
m_resource(resource), settings(settings_) {
if UNLIKELY (!good())
return;
@@ -511,8 +736,8 @@ CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CX
const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); };
if (settings.iccFd >= 0)
m_resource->sendIccFile(settings.iccFd, settings.iccSize);
if (settings.icc.fd >= 0)
m_resource->sendIccFile(settings.icc.fd, settings.icc.length);
// send preferred client paramateres
m_resource->sendPrimaries(toProto(settings.primaries.red.x), toProto(settings.primaries.red.y), toProto(settings.primaries.green.x), toProto(settings.primaries.green.y),
@@ -542,12 +767,13 @@ wl_client* CColorManagementImageDescriptionInfo::client() {
return pClient;
}
CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name, bool debug) :
IWaylandProtocol(iface, ver, name), m_debug(debug) {
;
}
void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CColorManager>(makeShared<CXxColorManagerV4>(client, ver, id)));
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CColorManager>(makeShared<CWpColorManagerV1>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);
@@ -555,12 +781,19 @@ void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32
return;
}
LOGM(TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get());
LOGM(TRACE, "New WP_color_manager at {:x}", (uintptr_t)RESOURCE.get());
}
void CColorManagementProtocol::onImagePreferredChanged() {
void CColorManagementProtocol::onImagePreferredChanged(uint32_t preferredId) {
for (auto const& feedback : m_vFeedbackSurfaces) {
feedback->resource->sendPreferredChanged();
feedback->m_resource->sendPreferredChanged(preferredId);
}
}
void CColorManagementProtocol::onMonitorImageDescriptionChanged(WP<CMonitor> monitor) {
for (auto const& output : m_vOutputs) {
if (output->m_monitor == monitor)
output->m_resource->sendImageDescriptionChanged();
}
}
@@ -580,6 +813,10 @@ void CColorManagementProtocol::destroyResource(CColorManagementFeedbackSurface*
std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementIccCreator* resource) {
std::erase_if(m_vIccCreators, [&](const auto& other) { return other.get() == resource; });
}
void CColorManagementProtocol::destroyResource(CColorManagementParametricCreator* resource) {
std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; });
}

View File

@@ -4,8 +4,9 @@
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "protocols/core/Compositor.hpp"
#include "xx-color-management-v4.hpp"
#include "../helpers/Monitor.hpp"
#include "core/Compositor.hpp"
#include "color-management-v1.hpp"
#include "types/ColorManagement.hpp"
class CColorManager;
@@ -15,17 +16,17 @@ class CColorManagementProtocol;
class CColorManager {
public:
CColorManager(SP<CXxColorManagerV4> resource_);
CColorManager(SP<CWpColorManagerV1> resource);
bool good();
private:
SP<CXxColorManagerV4> resource;
SP<CWpColorManagerV1> m_resource;
};
class CColorManagementOutput {
public:
CColorManagementOutput(SP<CXxColorManagementOutputV4> resource_);
CColorManagementOutput(SP<CWpColorManagementOutputV1> resource, WP<CMonitor> monitor);
bool good();
wl_client* client();
@@ -34,8 +35,9 @@ class CColorManagementOutput {
WP<CColorManagementImageDescription> imageDescription;
private:
SP<CXxColorManagementOutputV4> resource;
SP<CWpColorManagementOutputV1> m_resource;
wl_client* pClient = nullptr;
WP<CMonitor> m_monitor;
friend class CColorManagementProtocol;
friend class CColorManagementImageDescription;
@@ -44,35 +46,36 @@ class CColorManagementOutput {
class CColorManagementSurface {
public:
CColorManagementSurface(SP<CWLSurfaceResource> surface_); // temporary interface for frog CM
CColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
CColorManagementSurface(SP<CWpColorManagementSurfaceV1> resource, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
bool good();
wl_client* client();
WP<CColorManagementSurface> self;
WP<CWLSurfaceResource> surface;
WP<CColorManagementSurface> self;
WP<CWLSurfaceResource> surface;
const SImageDescription& imageDescription();
bool hasImageDescription();
void setHasImageDescription(bool has);
const hdr_output_metadata& hdrMetadata();
void setHDRMetadata(const hdr_output_metadata& metadata);
bool needsHdrMetadataUpdate();
const NColorManagement::SImageDescription& imageDescription();
bool hasImageDescription();
void setHasImageDescription(bool has);
const hdr_output_metadata& hdrMetadata();
void setHDRMetadata(const hdr_output_metadata& metadata);
bool needsHdrMetadataUpdate();
private:
SP<CXxColorManagementSurfaceV4> resource;
wl_client* pClient = nullptr;
SImageDescription m_imageDescription;
bool m_hasImageDescription = false;
bool m_needsNewMetadata = false;
hdr_output_metadata m_hdrMetadata;
SP<CWpColorManagementSurfaceV1> m_resource;
wl_client* pClient = nullptr;
NColorManagement::SImageDescription m_imageDescription;
bool m_hasImageDescription = false;
bool m_needsNewMetadata = false;
hdr_output_metadata m_hdrMetadata;
friend class CXXColorManagementSurface;
friend class CFrogColorManagementSurface;
};
class CColorManagementFeedbackSurface {
public:
CColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
CColorManagementFeedbackSurface(SP<CWpColorManagementSurfaceFeedbackV1> resource, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
@@ -81,7 +84,7 @@ class CColorManagementFeedbackSurface {
WP<CWLSurfaceResource> surface;
private:
SP<CXxColorManagementFeedbackSurfaceV4> resource;
SP<CWpColorManagementSurfaceFeedbackV1> m_resource;
wl_client* pClient = nullptr;
WP<CColorManagementImageDescription> m_currentPreferred;
@@ -89,16 +92,32 @@ class CColorManagementFeedbackSurface {
friend class CColorManagementProtocol;
};
class CColorManagementIccCreator {
public:
CColorManagementIccCreator(SP<CWpImageDescriptionCreatorIccV1> resource);
bool good();
wl_client* client();
WP<CColorManagementIccCreator> self;
NColorManagement::SImageDescription settings;
private:
SP<CWpImageDescriptionCreatorIccV1> m_resource;
wl_client* pClient = nullptr;
};
class CColorManagementParametricCreator {
public:
CColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_);
CColorManagementParametricCreator(SP<CWpImageDescriptionCreatorParamsV1> resource);
bool good();
wl_client* client();
WP<CColorManagementParametricCreator> self;
SImageDescription settings;
NColorManagement::SImageDescription settings;
private:
enum eValuesSet : uint32_t { // NOLINT
@@ -112,25 +131,25 @@ class CColorManagementParametricCreator {
PC_FALL = (1 << 7),
};
SP<CXxImageDescriptionCreatorParamsV4> resource;
SP<CWpImageDescriptionCreatorParamsV1> m_resource;
wl_client* pClient = nullptr;
uint32_t valuesSet = 0; // enum eValuesSet
};
class CColorManagementImageDescription {
public:
CColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation = false);
CColorManagementImageDescription(SP<CWpImageDescriptionV1> resource, bool allowGetInformation);
bool good();
wl_client* client();
SP<CXxImageDescriptionV4> resource();
SP<CWpImageDescriptionV1> resource();
WP<CColorManagementImageDescription> self;
SImageDescription settings;
NColorManagement::SImageDescription settings;
private:
SP<CXxImageDescriptionV4> m_resource;
SP<CWpImageDescriptionV1> m_resource;
wl_client* pClient = nullptr;
bool m_allowGetInformation = false;
@@ -139,30 +158,32 @@ class CColorManagementImageDescription {
class CColorManagementImageDescriptionInfo {
public:
CColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const SImageDescription& settings_);
CColorManagementImageDescriptionInfo(SP<CWpImageDescriptionInfoV1> resource, const NColorManagement::SImageDescription& settings_);
bool good();
wl_client* client();
private:
SP<CXxImageDescriptionInfoV4> m_resource;
wl_client* pClient = nullptr;
SImageDescription settings;
SP<CWpImageDescriptionInfoV1> m_resource;
wl_client* pClient = nullptr;
NColorManagement::SImageDescription settings;
};
class CColorManagementProtocol : public IWaylandProtocol {
public:
CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name, bool debug = false);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void onImagePreferredChanged();
void onImagePreferredChanged(uint32_t preferredId);
void onMonitorImageDescriptionChanged(WP<CMonitor> monitor);
private:
void destroyResource(CColorManager* resource);
void destroyResource(CColorManagementOutput* resource);
void destroyResource(CColorManagementSurface* resource);
void destroyResource(CColorManagementFeedbackSurface* resource);
void destroyResource(CColorManagementIccCreator* resource);
void destroyResource(CColorManagementParametricCreator* resource);
void destroyResource(CColorManagementImageDescription* resource);
@@ -170,16 +191,20 @@ class CColorManagementProtocol : public IWaylandProtocol {
std::vector<SP<CColorManagementOutput>> m_vOutputs;
std::vector<SP<CColorManagementSurface>> m_vSurfaces;
std::vector<SP<CColorManagementFeedbackSurface>> m_vFeedbackSurfaces;
std::vector<SP<CColorManagementIccCreator>> m_vIccCreators;
std::vector<SP<CColorManagementParametricCreator>> m_vParametricCreators;
std::vector<SP<CColorManagementImageDescription>> m_vImageDescriptions;
bool m_debug = false;
friend class CColorManager;
friend class CColorManagementOutput;
friend class CColorManagementSurface;
friend class CColorManagementFeedbackSurface;
friend class CColorManagementIccCreator;
friend class CColorManagementParametricCreator;
friend class CColorManagementImageDescription;
friend class CXXColorManagementSurface;
friend class CFrogColorManagementSurface;
};

View File

@@ -0,0 +1,95 @@
#include "ContentType.hpp"
#include "content-type-v1.hpp"
#include "protocols/types/ContentType.hpp"
CContentTypeManager::CContentTypeManager(SP<CWpContentTypeManagerV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
resource->setDestroy([](CWpContentTypeManagerV1* r) {});
resource->setOnDestroy([this](CWpContentTypeManagerV1* r) { PROTO::contentType->destroyResource(this); });
resource->setGetSurfaceContentType([](CWpContentTypeManagerV1* r, uint32_t id, wl_resource* surface) {
LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
if (SURF->colorManagement) {
r->error(WP_CONTENT_TYPE_MANAGER_V1_ERROR_ALREADY_CONSTRUCTED, "CT manager already exists");
return;
}
const auto RESOURCE = PROTO::contentType->m_vContentTypes.emplace_back(makeShared<CContentType>(makeShared<CWpContentTypeV1>(r->client(), r->version(), id)));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::contentType->m_vContentTypes.pop_back();
return;
}
RESOURCE->self = RESOURCE;
SURF->contentType = RESOURCE;
});
}
bool CContentTypeManager::good() {
return m_resource->resource();
}
CContentType::CContentType(WP<CWLSurfaceResource> surface) {
destroy = surface->events.destroy.registerListener([this](std::any d) { PROTO::contentType->destroyResource(this); });
}
CContentType::CContentType(SP<CWpContentTypeV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
m_pClient = resource->client();
resource->setDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); });
resource->setOnDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); });
resource->setSetContentType([this](CWpContentTypeV1* r, wpContentTypeV1Type type) { value = NContentType::fromWP(type); });
}
bool CContentType::good() {
return m_resource && m_resource->resource();
}
wl_client* CContentType::client() {
return m_pClient;
}
CContentTypeProtocol::CContentTypeProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CContentTypeProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CContentTypeManager>(makeShared<CWpContentTypeManagerV1>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
}
SP<CContentType> CContentTypeProtocol::getContentType(WP<CWLSurfaceResource> surface) {
if (surface->contentType.valid())
return surface->contentType.lock();
return m_vContentTypes.emplace_back(makeShared<CContentType>(surface));
}
void CContentTypeProtocol::destroyResource(CContentTypeManager* resource) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
}
void CContentTypeProtocol::destroyResource(CContentType* resource) {
std::erase_if(m_vContentTypes, [&](const auto& other) { return other.get() == resource; });
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include "WaylandProtocol.hpp"
#include "core/Compositor.hpp"
#include "content-type-v1.hpp"
#include "types/ContentType.hpp"
class CContentTypeManager {
public:
CContentTypeManager(SP<CWpContentTypeManagerV1> resource);
bool good();
private:
SP<CWpContentTypeManagerV1> m_resource;
};
class CContentType {
public:
CContentType(SP<CWpContentTypeV1> resource);
CContentType(WP<CWLSurfaceResource> surface);
bool good();
wl_client* client();
NContentType::eContentType value = NContentType::CONTENT_TYPE_NONE;
WP<CContentType> self;
private:
SP<CWpContentTypeV1> m_resource;
wl_client* m_pClient = nullptr;
CHyprSignalListener destroy;
friend class CContentTypeProtocol;
};
class CContentTypeProtocol : public IWaylandProtocol {
public:
CContentTypeProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
SP<CContentType> getContentType(WP<CWLSurfaceResource> surface);
private:
void destroyResource(CContentTypeManager* resource);
void destroyResource(CContentType* resource);
std::vector<SP<CContentTypeManager>> m_vManagers;
std::vector<SP<CContentType>> m_vContentTypes;
friend class CContentTypeManager;
friend class CContentType;
};
namespace PROTO {
inline UP<CContentTypeProtocol> contentType;
};

View File

@@ -41,7 +41,7 @@ void CCursorShapeProtocol::createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr
}
void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t serial, wpCursorShapeDeviceV1Shape shape) {
if UNLIKELY ((uint32_t)shape == 0 || (uint32_t)shape > CURSOR_SHAPE_NAMES.size()) {
if UNLIKELY ((uint32_t)shape == 0 || (uint32_t)shape >= CURSOR_SHAPE_NAMES.size()) {
pMgr->error(WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "The shape is invalid");
return;
}

View File

@@ -4,6 +4,7 @@
#include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/backend/DRM.hpp>
#include <fcntl.h>
using namespace Hyprutils::OS;
CDRMLeaseResource::CDRMLeaseResource(SP<CWpDrmLeaseV1> resource_, SP<CDRMLeaseRequestResource> request) : resource(resource_) {
if UNLIKELY (!good())
@@ -185,15 +186,14 @@ CDRMLeaseDeviceResource::CDRMLeaseDeviceResource(SP<CWpDrmLeaseDeviceV1> resourc
RESOURCE->parent = self;
});
int fd = ((Aquamarine::CDRMBackend*)PROTO::lease->primaryDevice->backend.get())->getNonMasterFD();
if (fd < 0) {
CFileDescriptor fd{((Aquamarine::CDRMBackend*)PROTO::lease->primaryDevice->backend.get())->getNonMasterFD()};
if (!fd.isValid()) {
LOGM(ERR, "Failed to dup fd in lease");
return;
}
LOGM(LOG, "Sending DRMFD {} to new lease device", fd);
resource->sendDrmFd(fd);
close(fd);
LOGM(LOG, "Sending DRMFD {} to new lease device", fd.get());
resource->sendDrmFd(fd.get());
for (auto const& m : PROTO::lease->primaryDevice->offeredOutputs) {
if (m)
@@ -231,16 +231,15 @@ void CDRMLeaseDeviceResource::sendConnector(PHLMONITOR monitor) {
}
CDRMLeaseDevice::CDRMLeaseDevice(SP<Aquamarine::CDRMBackend> drmBackend) : backend(drmBackend) {
auto drm = (Aquamarine::CDRMBackend*)drmBackend.get();
auto drm = (Aquamarine::CDRMBackend*)drmBackend.get();
auto fd = drm->getNonMasterFD();
CFileDescriptor fd{drm->getNonMasterFD()};
if (fd < 0) {
if (!fd.isValid()) {
LOGM(ERR, "Failed to dup fd for drm node {}", drm->gpuName);
return;
}
close(fd);
success = true;
name = drm->gpuName;
}

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