Compare commits

..

102 Commits

Author SHA1 Message Date
Vaxry
84ab8d11e8 props: bump ver to 0.35.0 2024-02-05 01:59:02 +00:00
Vaxry
60bda7ee3d pluginapi: allow registering hyprctl commands
closes #4616
2024-02-05 01:57:29 +00:00
Vaxry
939696f97e hyprctl: move to a class and unify commands 2024-02-05 01:56:49 +00:00
MightyPlaza
cbadf3e3f3 input: focus window on mouse down on decoration (#4514)
Also unifies vectorToWindow funcs
2024-02-04 15:40:20 +00:00
JManch
1ed4f1cb25 screenshader: rename output uniform to wl_output (#4606)
* screenshader: rename output uniform to monitor

* rename to wl_output
2024-02-04 02:30:00 +00:00
Vaxry
5d4ff60f53 hyprpm: fix invalid pkg-config path env in build
ref #4573
2024-02-03 18:32:59 +00:00
Vaxry
504ebe1b37 box: add missing include 2024-02-03 01:31:52 +00:00
Vaxry
cf1886ca44 renderer: avoid unnecessary gpu resource deletions
fixes #4594
2024-02-02 15:36:13 +00:00
Vaxry
341e04a36c dwindle: avoid sending negative sizes to wlr
fixes #4591
2024-02-02 15:04:04 +00:00
Vaxry
d7514412d8 renderer: reset fb pointers after render pass
fixes #4590
2024-02-02 14:56:04 +00:00
Vaxry
7447be8220 hyprpm: fix crash on add plugin
ref #4563
2024-02-02 01:51:14 +00:00
Vaxry
4644de2269 keybinds: fix ignoremods with release 2024-02-02 01:09:57 +00:00
Vaxry
3656045ad8 hyprpm: install headers locally (#4585)
* hyprpm: install headers locally

* oopsie
2024-02-01 19:38:43 +00:00
Abílio Costa
15316aaa31 subsurfaceTree: Fix nullptr crash when disconnecting a monitor (#4577)
I was able to reproduce this frequently by having a kitty terminal on an
monitor running the following command and then unplugging that monitor:

`while true; do echo "" && sleep 0.02; done`
2024-02-01 03:09:31 +00:00
Vaxry
cfd68af5b6 tearing-control: handle unmapped surfaces for hints
fixes #4570
2024-02-01 00:55:29 +00:00
Roger Roger
4f804d5f96 Makefile: remove hyprland symlink on uninstall 2024-01-31 19:34:16 +02:00
Mihai Fufezan
e6f7724ab0 subprojects: remove wlroots.wrap 2024-01-31 14:04:30 +02:00
Mihai Fufezan
e65f52bf2d Makefile: pass PREFIX to CMake 2024-01-30 22:22:06 +02:00
Vaxry
c51b3fb06f events: ignore sending mouse enter to focused if a constraint is active
fixes #4186
2024-01-30 16:24:41 +00:00
Vaxry
3ff59e7e1d hyprpm: update global state on plugin recompile not header update
ref #4547
2024-01-29 23:37:05 +00:00
Mihai Fufezan
3d0d3b6343 Meson: fix wallpaper installation 2024-01-30 01:28:07 +02:00
Vaxry
2e3f0d5991 renderer: Add new background infrastructure
Adds new backgrounds from the winners of the contest
Rewrites how it works
Allows high color precision PNGs (RGB32F precisely)
Fixes a small bug in renderTextureInternalWithDamage
Nukes misc:force_hypr_chan
2024-01-29 23:11:00 +00:00
Vaxry
91e8c42843 hyprpm: don't update headers if they are up-to-date, only recompile
will not update headers if plugins are compiled for different ones, and instead only compile.

ref #4284
2024-01-29 10:30:31 +00:00
Vaxry
4b4bd90b14 renderer: fixup misaligned fsv1 surfaces with uv
fixes #4548
2024-01-28 23:42:49 +00:00
GrizzlT
7009dc9184 nix: fix overlay composition 2024-01-29 00:27:51 +02:00
Vaxry
b7840c6461 xwayland: remove delta from pos sets in configureX11
ref #4536
2024-01-28 20:13:44 +00:00
Vaxry
5a90911b70 hyprpm: verify headersHashCompiled as well in headersValid()
ref #4547
2024-01-28 20:01:46 +00:00
Epilepsy Gatherings
0e5f14d8d2 xwayland: remove reportedsize set in unmanagedSetGeometry (#4539) 2024-01-28 19:22:02 +00:00
Vaxry
df990c80e2 hyprpm: log verbose return of cmake and meson in update 2024-01-28 03:00:05 +00:00
Vaxry
352574d862 hyprpm: add --force for update
closes #4547
2024-01-28 02:04:35 +00:00
Vaxry
bfcc2adbda monitor: wrap usage of wlr_output_state
for better control and convenience in usage.

fixes #4546
2024-01-28 01:57:13 +00:00
Vaxry
9002657bcc monitor: don't call output_state_finish on buffer-less state clears
ref #4546
2024-01-28 00:41:54 +00:00
Vaxry
3e93fdf779 opengl: use texBox for rendering background texture
fixes #4543
2024-01-28 00:32:54 +00:00
Vaxry
bc7e488a4c monitor: clear output state after usage
fixes massive lag
2024-01-27 19:11:14 +00:00
Mihai Fufezan
8b1069b330 flake.lock: update 2024-01-27 17:31:39 +02:00
vaxerski
61fd75b55e [gha] Nix: update wlroots 2024-01-27 13:59:16 +00:00
Vaxry
7b3d039388 deps: update wlroots
drops requirement for WLR_DRM_NO_ATOMIC provided kernel >= 6.8
2024-01-27 13:58:28 +00:00
rszyma
12d79d6342 dwindle: fix windows being created at incorrect position when cursor is over reserved area (#4520)
* fix: smart_split not working correctly when creating a window with cursor over reserved area

* use getClosestNodeOnWorkspace instead of getFirstNodeOnWorkspace when hovering over reserved area

* optimize `getClosestNodeOnWorkspace`

* remove unused methods
2024-01-26 22:30:36 +00:00
bvr-yr
08e3519747 layout: save float props before setting fs state (#4537)
fixes #4388
2024-01-26 17:24:00 +00:00
Epilepsy Gatherings
5cd7e4587e compositor: don't close special on focus on pinned (#4533) 2024-01-26 12:24:52 +00:00
Vaxry
72987dee88 opengl: rassert false on lost context
we do not have infra to deal with this. It will cause hyprland to freeze rendering, we might as well die.
2024-01-26 02:26:10 +00:00
Vaxry
754eaf5b8b pluginapi: fix hooks with negative rip offsets
fixes #4484
2024-01-24 13:53:18 +00:00
Zach DeCook
df17991b1c input: Allow disabling touchscreen input (#4517)
* enable/disable touch device

* ConfigManager: update documentation of 'enabled'
2024-01-24 00:15:01 +00:00
Vaxry
791e1b96b3 internal: minor header cleanup 2024-01-23 01:32:34 +00:00
vaxerski
02b4a9bded compositor: clarify common errors at launch 2024-01-22 09:46:47 +01:00
Sean McGovern
4d403dac32 build: protocols: require wayland-protocols >= 1.32
The cursor-shape-v1 protocol was not available until this release.
2024-01-20 22:33:12 +02:00
vaxerski
f40e382fc6 crashreporter: skip first possibly cut off line in log tail 2024-01-20 09:16:27 +01:00
vaxerski
b86ed02d8a keybinds: avoid duplicated held keys, only use last, remove all
ref #4471
2024-01-19 19:09:32 +01:00
vaxerski
17339e0ae9 input: track exclusive LSes
ref #4465
2024-01-19 16:45:34 +01:00
vaxerski
5eeec8860e core: improve cleanup logic 2024-01-19 16:20:30 +01:00
Epilepsy Gatherings
9f20a15955 input: remove animate checks on resize limiter (#4480) 2024-01-19 15:45:51 +01:00
vaxerski
c4365f20ed damage: use buffer_damage instead of effective_damage 2024-01-17 16:01:20 +01:00
vaxerski
307dd8f511 input: partially revert #4401
ref #4465
2024-01-17 14:43:38 +01:00
Huy Nguyen
8342bac697 Nix: disable fortify for devshell (#4463)
This disables '_FORTIFY_SOURCE' Werrors trying to compile wlroots.
Long standing issue in https://github.com/NixOS/nixpkgs/issues/60919
afaik.

After this change you should be able to:
```
nix develop
mmeson setup build -Dbuildtype=debug
ninja -C build
```
2024-01-17 14:27:35 +02:00
virchau13
3c964a9fdc keybinds: Add dispatcher for xmonad/qtile-style workspace switching (#4439)
* feat: implement xmonad/qtile-style workspace switching

Implements the focusWorkspaceOnCurrentMonitor dispatcher and function,
which implements XMonad/Qtile-style workspace switching.

When called, focusWorkspaceOnCurrentMonitor will:
1. Send the requested workspace to the current monitor,
2. If the workspace was previously active on a different monitor,
   replace it with the workspace that was previously active on the
   current monitor,
3. Focus the workspace on the current monitor.

* fix: address PR comments
2024-01-15 16:30:46 +01:00
MightyPlaza
f14c5ea5c5 groupbar: separate gradients from title (#4444)
* separate gradients from title logic
modified:   src/config/ConfigManager.cpp
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* fix disabled extents
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp

* fix disabled height
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2024-01-15 16:17:42 +01:00
fufexan
dcd7a92b01 [gha] Nix: update inputs 2024-01-15 00:03:29 +00:00
Isaac Myhal
b6516bad02 config: Add border gradients to windowrulev2 (#4335)
* Add border gradients to windowrulev2

* windowrule border gradient: Use CVarList to parse

* windowrule border gradient: No {} around short ifs
2024-01-14 18:27:32 +01:00
dranull
13d9a637d6 hyprctl: screen_shader config fixes (#4102)
* Allow "/" in values of requests

* Don't tick on empty value
2024-01-14 18:12:52 +01:00
1over137
4cee94b91c fractional: Set preferred scale on monitor config reload (#4406) 2024-01-14 14:56:35 +01:00
Zach DeCook
c4da4b026d layershell: Fix greedy mouse grab from keyboard_interactive layer (#4401)
* Layer: Don't allow a keyboard-layer to steal focus from other layers

* Input: Don't change keyboard focus on click if focus is locked
2024-01-12 15:43:16 +01:00
scorpion-26
babb9c07b0 swipe: Prevent hiding current workspace when swiping (#4417)
When workspace_swipe_use_r is enabled, swiping from WS 1 to a non-empty WS 2 would
hide WS 1 (Similar effect to issue #4076). This is caused by a faulty
check which doesn't consider, that workspaceIDLeft could be the current
workspace.
This bug is only a problem for r, because m wraps around on WS 1 m-1, whereas r stays on WS 1.
2024-01-11 19:22:40 +01:00
vaxerski
6b92144f15 surface: avoid spam of window surfaces with scale and transform events
fixes #4408
2024-01-11 14:07:28 +01:00
Clyybber
8d31c84483 layout: Round window pos and size on togglefloating (#4407)
Also restore the behaviour introduced in bc4a51dbbb
2024-01-10 18:08:58 +01:00
vaxerski
d484506600 keybinds: fix tracking of sent key states 2024-01-10 18:06:38 +01:00
vaxerski
b240704bee renderer: allow rendering multiple fullscreen windows in third fs pass
something might be fading out, sliding out, etc. We handle it before, why not use it?

fixes #4076
2024-01-09 20:42:07 +01:00
vaxerski
71166ef40b subsurfaceTree: update surface tree protocol feedback on map 2024-01-09 18:14:08 +01:00
vaxerski
252aaaecfa input: add special_fallthrough
fixes #4323
2024-01-09 13:17:55 +01:00
vaxerski
f92a86af53 renderer: ignore box offsets for fullscreen windows 2024-01-08 19:58:15 +01:00
MightyPlaza
2ba2c8aeee groupbar: improve gradient handling (#4390)
modified:   src/render/decorations/CHyprGroupBarDecoration.cpp
2024-01-08 19:38:22 +01:00
Piroro-hs
955009655d cmake: Propagate NO_XWAYLAND to wlroots building setup (#4385) 2024-01-08 19:24:52 +01:00
vaxerski
d7d333d162 opengl: apply box rot to projections 2024-01-07 18:51:08 +01:00
vaxerski
f5b2fd2bc3 opengl: add renderdata.forceIntrospection 2024-01-07 18:37:02 +01:00
vaxerski
44ee9915e3 renderer: overhaul renderModifData 2024-01-07 18:35:44 +01:00
vaxerski
9f2bde925b hyprpm: handle failed compilations gracefully 2024-01-07 18:15:51 +01:00
vaxerski
7904188de9 input: allow focusSurface when locked if surfase is sessionLock 2024-01-07 14:04:32 +01:00
Epilepsy Gatherings
666ee61c13 input: leave special on focus (#4358) 2024-01-07 12:06:33 +01:00
Jan Beich
7e033e48ac make: unbreak with non-GNU ln(1) after 78f9ba9fdd
ln -s -r -f /usr/local/bin/Hyprland /usr/local/bin/hyprland
ln: illegal option -- r
usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]
       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir
       link source_file target_file
*** Error code 1
2024-01-05 19:22:43 +02:00
Naksu
d8dbdc4a01 main: Fix typo in std::cerr (#4359) 2024-01-05 12:45:49 +01:00
vaxerski
d3acf8da3b CI: don't close stale issues 2024-01-05 12:40:18 +01:00
Mihai Fufezan
aeeeace102 flake.lock: update 2024-01-04 22:52:03 +02:00
thejch
880996b053 master: Add more null checks for rollnext (#4343)
* add more null check for rollnext

* remove pwindow check
2024-01-04 16:17:17 +01:00
rszyma
4d6d662c67 Makefile: force ln command to overwrite symlink (#4347)
When running `make install` twice ln shows an error:
```
ln -s -r /usr/local/bin/Hyprland /usr/local/bin/hyprland
ln: failed to create symbolic link '/usr/local/bin/hyprland': File exists
```
2024-01-04 10:20:17 +01:00
vaxerski
1512b81126 master: guard PNODE in roll*
fixes #4331
2024-01-02 22:18:38 +01:00
flicko
4f26c4e1eb config: variables update their value when set again (#4263)
* variables update their value when set again

* only sort if new variable is found

* clang-format
2024-01-02 16:38:30 +01:00
dranull
3c33d4b9dd keybinds: Refocus only if the silently moved window had the focus (#4328) 2024-01-02 14:50:30 +01:00
vaxerski
bd3ea8dcb5 examples: remove example plugin
closes #4329
2024-01-02 14:25:18 +01:00
vaxerski
813af393f1 layout: update rules before applying fullscreen nodes in layouts 2024-01-02 14:21:36 +01:00
dranull
583b05a8c6 groupbar: Drag single window instead of destroying group (#4327) 2024-01-02 13:37:03 +01:00
Zach DeCook
1607e96704 HookSystem: rename PAGESIZE_VAR from PAGESIZE to avoid conflict (#4321) 2024-01-01 23:05:26 +01:00
q234rty
1a4f23eb2f renderer: Only force nearest neighbor when the sizes are off by one or two (#4325)
Fixes rendering issues in arch's extra/telegram-desktop
2024-01-01 20:20:27 +01:00
bvr-yr
42ab06e7c8 meson: fix wlroots patch (#4324) 2024-01-01 19:58:01 +01:00
vaxerski
46753b1f22 CI: limit stalebot ops per run 2024-01-01 18:37:49 +01:00
Vaxry
d4e68ab602 CI: allow manual stale execution 2024-01-01 18:34:15 +01:00
rszyma
37b76cd1ca keybinds: fix keys getting stuck + minor refactor & optimizations to keybind handling (#4304) 2024-01-01 18:29:51 +01:00
vaxerski
0be36cd02d cmakelists: fix wlroots patch sed 2024-01-01 18:29:10 +01:00
Vaxry
4e0e8d933e CI: add stalebot 2024-01-01 18:26:48 +01:00
vaxerski
c7ba460687 wlroots: update version patches 2024-01-01 18:19:22 +01:00
vaxerski
3a189c265d issue templates: make versions spoiler'd 2024-01-01 18:05:49 +01:00
vaxerski
069880e374 hyprctl: add systeminfo 2024-01-01 17:53:03 +01:00
vaxerski
fa5e812304 [gha] Nix: update wlroots 2024-01-01 15:48:37 +00:00
vaxerski
33444e1e5e deps: update wlroots 2024-01-01 16:47:54 +01:00
92 changed files with 1690 additions and 1364 deletions

View File

@@ -9,12 +9,23 @@ body:
---
- type: input
- type: textarea
id: ver
attributes:
label: Hyprland Version
description: "Paste here the output of `hyprctl version`."
placeholder: Hyprland, built from branch main at commit...
description: "Paste here the output of `hyprctl version`. For hyprland after 0.34.0, paste `hyprctl systeminfo` instead."
value: "<details>
<summary>System/Version info</summary>
```sh
<Paste the output of the command here>
```
</details>"
validations:
required: true

28
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: '7 */4 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.STALEBOT_PAT }}
stale-issue-label: 'stale'
stale-pr-label: 'stale'
operations-per-run: 40
days-before-close: -1

View File

@@ -57,8 +57,8 @@ ExternalProject_Add(
wlroots
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots
PATCH_COMMAND sed -E -i -e "s/(soversion = 13)([^032]|$$)/soversion = 13032/g" meson.build
CONFIGURE_COMMAND meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none> && meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none> --reconfigure
PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/soversion = 13032/g" meson.build
CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
BUILD_COMMAND ninja -C build
BUILD_ALWAYS true
BUILD_IN_SOURCE true

View File

@@ -1,22 +1,22 @@
PREFIX = /usr/local
legacyrenderer:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
legacyrendererdebug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
release:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
debug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -S . -B ./build -G Ninja
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
@@ -42,10 +42,10 @@ install:
chmod 755 ${PREFIX}/bin/Hyprland
chmod 755 ${PREFIX}/bin/hyprctl
chmod 755 ${PREFIX}/bin/hyprpm
ln -s -r ${PREFIX}/bin/Hyprland ${PREFIX}/bin/hyprland
cd ${PREFIX}/bin && ln -sf Hyprland hyprland
if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland
cp ./assets/wall_* ${PREFIX}/share/hyprland
cp ./assets/wall* ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/xdg-desktop-portal
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal
@@ -60,6 +60,7 @@ install:
uninstall:
rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
rm -f ${PREFIX}/bin/Hyprland
rm -f ${PREFIX}/bin/hyprland
rm -f ${PREFIX}/bin/hyprctl
rm -f ${PREFIX}/bin/hyprpm
rm -f ${PREFIX}/lib/libwlroots.so.13032

View File

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

BIN
assets/wall0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

BIN
assets/wall1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

BIN
assets/wall2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1,8 +0,0 @@
# compile with HYPRLAND_HEADERS=<path_to_hl> make all
# make sure that the path above is to the root hl repo directory, NOT src/
# and that you have ran `make protocols` in the hl dir.
all:
$(CXX) -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b
clean:
rm ./examplePlugin.so

View File

@@ -1,74 +0,0 @@
#include "customDecoration.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
CCustomDecoration::CCustomDecoration(CWindow* pWindow) {
m_pWindow = pWindow;
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
}
CCustomDecoration::~CCustomDecoration() {
damageEntire();
}
SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() {
return m_seExtents;
}
void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
if (!g_pCompositor->windowValidMapped(m_pWindow))
return;
if (!m_pWindow->m_sSpecialRenderData.decorate)
return;
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue;
static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ?
0 :
(m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying());
// draw the border
CBox fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE),
(int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)};
fullBox.x -= pMonitor->vecPosition.x;
fullBox.y -= pMonitor->vecPosition.y;
m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2},
{fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2,
fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}};
fullBox.x += offset.x;
fullBox.y += offset.y;
if (fullBox.width < 1 || fullBox.height < 1)
return; // don't draw invisible shadows
g_pHyprOpenGL->scissor((CBox*)nullptr);
fullBox.scale(pMonitor->scale);
g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a);
}
eDecorationType CCustomDecoration::getDecorationType() {
return DECORATION_CUSTOM;
}
void CCustomDecoration::updateWindow(CWindow* pWindow) {
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
damageEntire();
}
void CCustomDecoration::damageEntire() {
CBox dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y),
(int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y};
g_pHyprRenderer->damageBox(&dm);
}

View File

@@ -1,29 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
class CCustomDecoration : public IHyprWindowDecoration {
public:
CCustomDecoration(CWindow*);
virtual ~CCustomDecoration();
virtual SWindowDecorationExtents getWindowDecorationExtents();
virtual void draw(CMonitor*, float a, const Vector2D& offset);
virtual eDecorationType getDecorationType();
virtual void updateWindow(CWindow*);
virtual void damageEntire();
private:
SWindowDecorationExtents m_seExtents;
CWindow* m_pWindow = nullptr;
Vector2D m_vLastWindowPos;
Vector2D m_vLastWindowSize;
};

View File

@@ -1,80 +0,0 @@
#include "customLayout.hpp"
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto SIZE = PMONITOR->vecSize;
// these are used for focus and move calculations, and are *required* to touch for moving focus to work properly.
pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)};
pWindow->m_vSize = SIZE / 2.0;
// this is the actual pos and size of the window (where it's rendered)
pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10};
pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20};
const auto PDATA = &m_vWindowData.emplace_back();
PDATA->pWindow = pWindow;
}
void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) {
std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; });
}
bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) {
return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end();
}
void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) {
; // empty
}
void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) {
; // empty
}
std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) {
return "";
}
SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) {
return {};
}
void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
; // empty
}
void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
; // empty
}
std::string CHyprCustomLayout::getLayoutName() {
return "custom";
}
void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
; // empty
}
void CHyprCustomLayout::onEnable() {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating)
continue;
onWindowCreatedTiling(w.get());
}
}
void CHyprCustomLayout::onDisable() {
m_vWindowData.clear();
}

View File

@@ -1,32 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/layout/IHyprLayout.hpp>
struct SWindowData {
CWindow* pWindow = nullptr;
};
class CHyprCustomLayout : public IHyprLayout {
public:
virtual void onWindowCreatedTiling(CWindow*);
virtual void onWindowRemovedTiling(CWindow*);
virtual bool isWindowTiled(CWindow*);
virtual void recalculateMonitor(const int&);
virtual void recalculateWindow(CWindow*);
virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
virtual void switchWindows(CWindow*, CWindow*);
virtual void alterSplitRatio(CWindow*, float, bool);
virtual std::string getLayoutName();
virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
virtual void onEnable();
virtual void onDisable();
private:
std::vector<SWindowData> m_vWindowData;
};

View File

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

View File

@@ -1,99 +0,0 @@
#define WLR_USE_UNSTABLE
#include "globals.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "customLayout.hpp"
#include "customDecoration.hpp"
#include <unistd.h>
#include <thread>
// Methods
inline std::unique_ptr<CHyprCustomLayout> g_pCustomLayout;
inline CFunctionHook* g_pFocusHook = nullptr;
inline CFunctionHook* g_pMotionHook = nullptr;
inline CFunctionHook* g_pMouseDownHook = nullptr;
typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
typedef void (*origMotion)(wlr_seat*, uint32_t, double, double);
typedef void (*origMouseDownNormal)(void*, wlr_pointer_button_event*);
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
static void onActiveWindowChange(void* self, std::any data) {
try {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: " + (PWINDOW ? PWINDOW->m_szTitle : "None"), CColor{0.f, 0.5f, 1.f, 1.f}, 5000);
} catch (std::bad_any_cast& e) { HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: None", CColor{0.f, 0.5f, 1.f, 1.f}, 5000); }
}
static void onNewWindow(void* self, std::any data) {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CCustomDecoration(PWINDOW));
}
void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("FocusWindow with %lx %lx", pWindow, pSurface), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origFocusWindow)g_pFocusHook->m_pOriginal)(thisptr, pWindow, pSurface);
}
void hkNotifyMotion(wlr_seat* wlr_seat, uint32_t time_msec, double sx, double sy) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("NotifyMotion with %lf %lf", sx, sy), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origMotion)g_pMotionHook->m_pOriginal)(wlr_seat, time_msec, sx, sy);
}
void hkProcessMouseDownNormal(void* thisptr, wlr_pointer_button_event* e) {
// HyprlandAPI::addNotification(PHANDLE, "Mouse down normal!", CColor{0.8f, 0.2f, 0.5f, 1.0f}, 5000);
(*(origMouseDownNormal)g_pMouseDownHook->m_pOriginal)(thisptr, e);
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
HyprlandAPI::addNotification(PHANDLE, "Hello World from an example plugin!", CColor{0.f, 1.f, 1.f, 1.f}, 5000);
HyprlandAPI::registerCallbackDynamic(PHANDLE, "activeWindow", [&](void* self, SCallbackInfo& info, std::any data) { onActiveWindowChange(self, data); });
HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, SCallbackInfo& info, std::any data) { onNewWindow(self, data); });
g_pCustomLayout = std::make_unique<CHyprCustomLayout>();
HyprlandAPI::addLayout(PHANDLE, "custom", g_pCustomLayout.get());
HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:border_color", SConfigValue{.intValue = configStringToInt("rgb(44ee44)")});
HyprlandAPI::addDispatcher(PHANDLE, "example", [](std::string arg) { HyprlandAPI::addNotification(PHANDLE, "Arg passed: " + arg, CColor{0.5f, 0.5f, 0.7f, 1.0f}, 5000); });
// Hook a public member
g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
// Hook a public non-member
g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
// Hook a private member
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "processMouseDownNormal");
g_pMouseDownHook = HyprlandAPI::createFunctionHook(PHANDLE, METHODS[0].address, (void*)&hkProcessMouseDownNormal);
static auto* const PBORDERCOLOR = HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color");
// fancy notifications
HyprlandAPI::addNotificationV2(
PHANDLE,
{{"text", "Example hint, color " + std::to_string(PBORDERCOLOR->intValue)}, {"time", (uint64_t)10000}, {"color", CColor{PBORDERCOLOR->intValue}}, {"icon", ICON_HINT}});
// Enable our hooks
g_pFocusHook->hook();
g_pMotionHook->hook();
g_pMouseDownHook->hook();
HyprlandAPI::reloadConfig();
return {"ExamplePlugin", "An example plugin", "Vaxry", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
HyprlandAPI::invokeHyprctlCommand("seterror", "disable");
}

View File

@@ -112,7 +112,7 @@ gestures {
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
}
# Example per-device config

42
flake.lock generated
View File

@@ -23,13 +23,34 @@
"type": "github"
}
},
"hyprlang": {
"inputs": {
"nixpkgs": [
"xdph",
"nixpkgs"
]
},
"locked": {
"lastModified": 1704287638,
"narHash": "sha256-TuRXJGwtK440AXQNl5eiqmQqY4LZ/9+z/R7xC0ie3iA=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "6624f2bb66d4d27975766e81f77174adbe58ec97",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703438236,
"narHash": "sha256-aqVBq1u09yFhL7bj1/xyUeJjzr92fXVvQSSEx6AdB1M=",
"lastModified": 1706191920,
"narHash": "sha256-eLihrZAPZX0R6RyM5fYAWeKVNuQPYjAkCUBr+JNvtdE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5f64a12a728902226210bf01d25ec6cbb9d9265b",
"rev": "ae5c332cbb5827f6b1f02572496b141021de335f",
"type": "github"
},
"original": {
@@ -67,18 +88,18 @@
"flake": false,
"locked": {
"host": "gitlab.freedesktop.org",
"lastModified": 1701368958,
"narHash": "sha256-7kvyoA91etzVEl9mkA/EJfB6z/PltxX7Xc4gcr7/xlo=",
"lastModified": 1706359063,
"narHash": "sha256-5HUTG0p+nCJv3cn73AmFHRZdfRV5AD5N43g8xAePSKM=",
"owner": "wlroots",
"repo": "wlroots",
"rev": "5d639394f3e83b01596dcd166a44a9a1a2583350",
"rev": "00b869c1a96f300a8f25da95d624524895e0ddf2",
"type": "gitlab"
},
"original": {
"host": "gitlab.freedesktop.org",
"owner": "wlroots",
"repo": "wlroots",
"rev": "5d639394f3e83b01596dcd166a44a9a1a2583350",
"rev": "00b869c1a96f300a8f25da95d624524895e0ddf2",
"type": "gitlab"
}
},
@@ -87,6 +108,7 @@
"hyprland-protocols": [
"hyprland-protocols"
],
"hyprlang": "hyprlang",
"nixpkgs": [
"nixpkgs"
],
@@ -95,11 +117,11 @@
]
},
"locked": {
"lastModified": 1703514399,
"narHash": "sha256-VRr5Xc4S/VPr/gU3fiOD3vSIL2+GJ+LUrmFTWTwnTz4=",
"lastModified": 1706145785,
"narHash": "sha256-j9MP4fv2U/vdRKAXXc2gyMTmYwVnHP6kHx1/y6jprrU=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "0a318a7a217a6402b0b705837cd5b50b0e94b31b",
"rev": "5a592647587cd20b9692a347df6939b6d371b3bb",
"type": "github"
},
"original": {

View File

@@ -12,7 +12,7 @@
host = "gitlab.freedesktop.org";
owner = "wlroots";
repo = "wlroots";
rev = "5d639394f3e83b01596dcd166a44a9a1a2583350";
rev = "00b869c1a96f300a8f25da95d624524895e0ddf2";
flake = false;
};
@@ -86,6 +86,7 @@
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [cmake python3];
buildInputs = [self.packages.${system}.wlroots-hyprland];
hardeningDisable = [ "fortify" ];
inputsFrom = [
self.packages.${system}.wlroots-hyprland
self.packages.${system}.hyprland

View File

@@ -51,6 +51,7 @@ commands:
setprop
splash
switchxkblayout
systeminfo
version
workspacerules
workspaces
@@ -388,6 +389,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/kill"))
request(fullRequest);
else if (fullRequest.contains("/systeminfo"))
request(fullRequest);
else if (fullRequest.contains("/splash"))
request(fullRequest);
else if (fullRequest.contains("/devices"))

View File

@@ -20,11 +20,18 @@ std::string DataState::getDataStatePath() {
return std::string{HOME} + "/.local/share/hyprpm";
}
std::string DataState::getHeadersPath() {
return getDataStatePath() + "/headersRoot";
}
void DataState::ensureStateStoreExists() {
const auto PATH = getDataStatePath();
if (!std::filesystem::exists(PATH))
std::filesystem::create_directories(PATH);
if (!std::filesystem::exists(getHeadersPath()))
std::filesystem::create_directories(getHeadersPath());
}
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
@@ -47,7 +54,8 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
DATA.emplace(p.name, toml::table{
{"filename", p.name + ".so"},
{"enabled", p.enabled}
{"enabled", p.enabled},
{"failed", p.failed}
});
}
// clang-format on
@@ -63,7 +71,10 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
@@ -84,7 +95,10 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
@@ -153,7 +167,10 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
std::vector<SPluginRepository> repos;
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
@@ -172,9 +189,10 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
continue;
const auto ENABLED = STATE[key]["enabled"].value_or(false);
const auto FAILED = STATE[key]["failed"].value_or(false);
const auto FILENAME = STATE[key]["filename"].value_or("");
repo.plugins.push_back(SPlugin{std::string{key.str()}, FILENAME, ENABLED});
repo.plugins.push_back(SPlugin{std::string{key.str()}, FILENAME, ENABLED, FAILED});
}
repos.push_back(repo);
@@ -189,7 +207,10 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
continue;
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
@@ -201,6 +222,11 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
if (key.str() != name)
continue;
const auto FAILED = STATE[key]["failed"].value_or(false);
if (FAILED)
return false;
(*STATE[key].as_table()).insert_or_assign("enabled", enabled);
std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc);

View File

@@ -10,6 +10,7 @@ struct SGlobalState {
namespace DataState {
std::string getDataStatePath();
std::string getHeadersPath();
void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo);
void removePluginRepo(const std::string& urlOrName);

View File

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

View File

@@ -6,7 +6,8 @@
struct SPlugin {
std::string name;
std::string filename;
bool enabled;
bool enabled = false;
bool failed = false;
};
struct SPluginRepository {

View File

@@ -10,6 +10,7 @@
#include <thread>
#include <fstream>
#include <algorithm>
#include <format>
#include <toml++/toml.hpp>
@@ -78,6 +79,8 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
bool CPluginManager::addNewPluginRepo(const std::string& url) {
const auto HLVER = getHyprlandVersion();
if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
return false;
@@ -170,6 +173,22 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) {
message += " version " + pl.version;
progress.printMessageAbove(message);
}
if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins
progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking");
for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd /tmp/hyprpm/new/ && git reset --hard --recurse-submodules " + plugin);
}
}
progress.m_szCurrentMessage = "Verifying headers";
progress.print();
@@ -191,16 +210,19 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
out += execAndGet("cd /tmp/hyprpm/new && " + bs) + "\n";
std::string cmd = std::format("cd /tmp/hyprpm/new && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n";
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " failed to build.\n");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
return false;
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
@@ -220,7 +242,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) {
repo.url = url;
repo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false});
repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false, p.failed});
}
DataState::addNewPluginRepo(repo);
@@ -263,8 +285,12 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
eHeadersErrors CPluginManager::headersValid() {
const auto HLVER = getHyprlandVersion();
if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc"))
return HEADERS_MISSING;
// find headers commit
auto headers = execAndGet("pkg-config --cflags --keep-system-cflags hyprland");
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkg-config --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str());
if (!headers.contains("-I/"))
return HEADERS_MISSING;
@@ -296,10 +322,6 @@ eHeadersErrors CPluginManager::headersValid() {
if (!ifs.good())
return HEADERS_CORRUPTED;
if ((std::filesystem::exists("/usr/include/hyprland/src/version.h") && verHeader != "/usr/include/hyprland/src/version.h") ||
(std::filesystem::exists("/usr/local/include/hyprland/src/version.h") && verHeader != "/usr/local/include/hyprland/src/version.h"))
return HEADERS_DUPLICATED;
std::string verHeaderContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
ifs.close();
@@ -314,7 +336,9 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_OK;
}
bool CPluginManager::updateHeaders() {
bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion();
@@ -323,11 +347,8 @@ bool CPluginManager::updateHeaders() {
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
if (headersValid() == HEADERS_OK) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Your headers are already up-to-date.\n";
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
if (!force && headersValid() == HEADERS_OK) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Headers up to date.\n";
return true;
}
@@ -369,23 +390,38 @@ bool CPluginManager::updateHeaders() {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland");
ret = execAndGet("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath());
ret = execAndGet(
std::format("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja",
DataState::getHeadersPath()));
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret);
// le hack. Wlroots has to generate its build/include
ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources";
progress.print();
progress.printMessageAbove(
std::string{Colors::YELLOW} + "!" + Colors::RESET +
" in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running.");
// progress.printMessageAbove(
// std::string{Colors::YELLOW} + "!" + Colors::RESET +
// " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running.");
ret = execAndGet("pkexec sh \"-c\" \"cd /tmp/hyprpm/hyprland && make installheaders\"");
std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" /tmp/hyprpm/hyprland/Makefile && cd /tmp/hyprpm/hyprland && make installheaders",
DataState::getHeadersPath());
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd);
ret = execAndGet(cmd);
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "pkexec returned: " << ret << "\n";
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
@@ -397,10 +433,6 @@ bool CPluginManager::updateHeaders() {
progress.m_szCurrentMessage = "Done!";
progress.print();
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
std::cout << "\n";
} else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID));
@@ -434,7 +466,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
const auto HLVER = getHyprlandVersion();
CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 1;
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Updating repositories";
progress.print();
@@ -528,7 +560,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
out += execAndGet("cd /tmp/hyprpm/update && " + bs) + "\n";
std::string cmd = std::format("cd /tmp/hyprpm/update && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) {
@@ -566,6 +599,14 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
}
progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating global state...";
progress.print();
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
progress.m_iSteps++;
progress.m_szCurrentMessage = "Done!";
progress.print();
@@ -696,8 +737,13 @@ void CPluginManager::listAllPlugins() {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n";
for (auto& p : r.plugins) {
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name + "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false")
<< Colors::RESET << "\n";
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name;
if (!p.failed)
std::cout << "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") << Colors::RESET << "\n";
else
std::cout << "\n └─ enabled: " << Colors::RED << "Plugin failed to build" << Colors::RESET << "\n";
}
}
}

View File

@@ -40,7 +40,7 @@ class CPluginManager {
bool removePluginRepo(const std::string& urlOrName);
eHeadersErrors headersValid();
bool updateHeaders();
bool updateHeaders(bool force = false);
bool updatePlugins(bool forceUpdateAll);
void listAllPlugins();

View File

@@ -1,6 +1,7 @@
#include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp"
#include "core/PluginManager.hpp"
#include "core/DataState.hpp"
#include <iostream>
#include <vector>
@@ -23,6 +24,7 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
--notify | -n Send a hyprland notification for important events (e.g. load fail)
--help | -h Show this menu
--verbose | -v Enable too much logging
--force | -f Force an operation ignoring checks (e.g. update -f)
)#";
@@ -38,7 +40,7 @@ int main(int argc, char** argv, char** envp) {
}
std::vector<std::string> command;
bool notify = false, verbose = false;
bool notify = false, verbose = false, force = false;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
@@ -49,6 +51,9 @@ int main(int argc, char** argv, char** envp) {
notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
} else {
std::cerr << "Unrecognized option " << ARGS[i];
return 1;
@@ -82,9 +87,13 @@ int main(int argc, char** argv, char** envp) {
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
} else if (command[0] == "update") {
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
bool headers = g_pPluginManager->updateHeaders();
bool headers = g_pPluginManager->updateHeaders(force);
if (headers) {
bool ret1 = g_pPluginManager->updatePlugins(!headersValid);
const auto HLVER = g_pPluginManager->getHyprlandVersion();
auto GLOBALSTATE = DataState::getGlobalState();
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
if (!ret1)
return 1;

View File

@@ -10,19 +10,16 @@
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in {
# Contains what a user is most likely to care about:
# Hyprland itself, XDPH and the Share Picker.
default = mkJoinedOverlays (with self.overlays; [
default = lib.composeManyExtensions (with self.overlays; [
hyprland-packages
hyprland-extras
]);
# Packages for variations of Hyprland, dependencies included.
hyprland-packages = mkJoinedOverlays [
hyprland-packages = lib.composeManyExtensions [
# Dependencies
inputs.hyprland-protocols.overlays.default
self.overlays.wlroots-hyprland
@@ -34,7 +31,7 @@ in {
hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
wlroots = final.wlroots-hyprland;
wlroots = prev.wlroots-hyprland;
commit = self.rev or "";
inherit (final) udis86 hyprland-protocols;
inherit date;
@@ -59,7 +56,7 @@ in {
# Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility.
hyprland-extras = mkJoinedOverlays [
hyprland-extras = lib.composeManyExtensions [
inputs.xdph.overlays.xdg-desktop-portal-hyprland
];

View File

@@ -8,7 +8,7 @@ CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }')
if [ "$SUB_REV" != "$CRT_REV" ]; then
echo "Updating wlroots..."
# update wlroots to submodule revision
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix
nix flake lock
echo "wlroots: $CRT_REV -> $SUB_REV"

View File

@@ -1,3 +1,3 @@
{
"version": "0.34.0"
"version": "0.35.0"
}

View File

@@ -1,5 +1,5 @@
wayland_protos = dependency('wayland-protocols',
version: '>=1.25',
version: '>=1.32',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)

View File

@@ -81,26 +81,6 @@ CCompositor::CCompositor() {
CCompositor::~CCompositor() {
cleanup();
g_pDecorationPositioner.reset();
g_pPluginSystem.reset();
g_pHyprNotificationOverlay.reset();
g_pDebugOverlay.reset();
g_pEventManager.reset();
g_pSessionLockManager.reset();
g_pProtocolManager.reset();
g_pXWaylandManager.reset();
g_pHyprRenderer.reset();
g_pHyprOpenGL.reset();
g_pInputManager.reset();
g_pThreadManager.reset();
g_pConfigManager.reset();
g_pLayoutManager.reset();
g_pHyprError.reset();
g_pConfigManager.reset();
g_pAnimationManager.reset();
g_pKeybindManager.reset();
g_pHookSystem.reset();
g_pWatchdog.reset();
}
void CCompositor::setRandomSplash() {
@@ -135,10 +115,10 @@ void CCompositor::initServer() {
else
wlr_log_init(WLR_ERROR, Debug::wlrLog);
m_sWLRBackend = wlr_backend_autocreate(m_sWLDisplay, &m_sWLRSession);
m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession);
if (!m_sWLRBackend) {
Debug::log(CRIT, "m_sWLRBackend was NULL!");
Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
throwError("wlr_backend_autocreate() failed!");
}
@@ -151,7 +131,7 @@ void CCompositor::initServer() {
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
if (!m_sWLRRenderer) {
Debug::log(CRIT, "m_sWLRRenderer was NULL!");
Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
throwError("wlr_gles2_renderer_create_with_drm_fd() failed!");
}
@@ -259,7 +239,7 @@ void CCompositor::initServer() {
m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay);
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay);
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay);
@@ -332,6 +312,59 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session");
}
void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_newOutput);
removeWLSignal(&Events::listen_newXDGToplevel);
removeWLSignal(&Events::listen_mouseMove);
removeWLSignal(&Events::listen_mouseMoveAbsolute);
removeWLSignal(&Events::listen_mouseButton);
removeWLSignal(&Events::listen_mouseAxis);
removeWLSignal(&Events::listen_mouseFrame);
removeWLSignal(&Events::listen_swipeBegin);
removeWLSignal(&Events::listen_swipeUpdate);
removeWLSignal(&Events::listen_swipeEnd);
removeWLSignal(&Events::listen_pinchBegin);
removeWLSignal(&Events::listen_pinchUpdate);
removeWLSignal(&Events::listen_pinchEnd);
removeWLSignal(&Events::listen_touchBegin);
removeWLSignal(&Events::listen_touchEnd);
removeWLSignal(&Events::listen_touchUpdate);
removeWLSignal(&Events::listen_touchFrame);
removeWLSignal(&Events::listen_holdBegin);
removeWLSignal(&Events::listen_holdEnd);
removeWLSignal(&Events::listen_newInput);
removeWLSignal(&Events::listen_requestMouse);
removeWLSignal(&Events::listen_requestSetSel);
removeWLSignal(&Events::listen_requestDrag);
removeWLSignal(&Events::listen_startDrag);
removeWLSignal(&Events::listen_requestSetSel);
removeWLSignal(&Events::listen_requestSetPrimarySel);
removeWLSignal(&Events::listen_newLayerSurface);
removeWLSignal(&Events::listen_change);
removeWLSignal(&Events::listen_outputMgrApply);
removeWLSignal(&Events::listen_outputMgrTest);
removeWLSignal(&Events::listen_newConstraint);
removeWLSignal(&Events::listen_NewXDGDeco);
removeWLSignal(&Events::listen_newVirtPtr);
removeWLSignal(&Events::listen_newVirtualKeyboard);
removeWLSignal(&Events::listen_RendererDestroy);
removeWLSignal(&Events::listen_newIdleInhibitor);
removeWLSignal(&Events::listen_powerMgrSetMode);
removeWLSignal(&Events::listen_newIME);
removeWLSignal(&Events::listen_newTextInput);
removeWLSignal(&Events::listen_activateXDG);
removeWLSignal(&Events::listen_newSessionLock);
removeWLSignal(&Events::listen_setGamma);
removeWLSignal(&Events::listen_setCursorShape);
removeWLSignal(&Events::listen_newTearingHint);
if (m_sWRLDRMLeaseMgr)
removeWLSignal(&Events::listen_leaseRequest);
if (m_sWLRSession)
removeWLSignal(&Events::listen_sessionActive);
}
void CCompositor::cleanup() {
if (!m_sWLDisplay || m_bIsShuttingDown)
return;
@@ -362,8 +395,8 @@ void CCompositor::cleanup() {
for (auto& m : m_vMonitors) {
g_pHyprOpenGL->destroyMonitorResources(m.get());
wlr_output_enable(m->output, false);
wlr_output_commit(m->output);
wlr_output_state_set_enabled(m->state.wlr(), false);
m->state.commit();
}
m_vMonitors.clear();
@@ -373,8 +406,31 @@ void CCompositor::cleanup() {
g_pXWaylandManager->m_sWLRXWayland = nullptr;
}
removeAllSignals();
wl_display_destroy_clients(g_pCompositor->m_sWLDisplay);
g_pDecorationPositioner.reset();
g_pPluginSystem.reset();
g_pHyprNotificationOverlay.reset();
g_pDebugOverlay.reset();
g_pEventManager.reset();
g_pSessionLockManager.reset();
g_pProtocolManager.reset();
g_pHyprRenderer.reset();
g_pHyprOpenGL.reset();
g_pInputManager.reset();
g_pThreadManager.reset();
g_pConfigManager.reset();
g_pLayoutManager.reset();
g_pHyprError.reset();
g_pConfigManager.reset();
g_pAnimationManager.reset();
g_pKeybindManager.reset();
g_pHookSystem.reset();
g_pWatchdog.reset();
g_pXWaylandManager.reset();
wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr;
@@ -408,6 +464,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the ThreadManager!");
g_pThreadManager = std::make_unique<CThreadManager>();
Debug::log(LOG, "Creating CHyprCtl");
g_pHyprCtl = std::make_unique<CHyprCtl>();
Debug::log(LOG, "Creating the InputManager!");
g_pInputManager = std::make_unique<CInputManager>();
@@ -629,36 +688,18 @@ bool CCompositor::windowExists(CWindow* pWindow) {
return false;
}
CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) {
const auto PMONITOR = getMonitorFromVector(pos);
if (PMONITOR->specialWorkspaceID) {
for (auto& w : m_vWindows) {
CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && box.containsPoint(pos) && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus)
return w.get();
}
}
for (auto& w : m_vWindows) {
CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_bIsMapped && box.containsPoint(pos) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus)
return w.get();
}
return nullptr;
}
CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreWindow) {
CWindow* CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, CWindow* pIgnoreWindow) {
const auto PMONITOR = getMonitorFromVector(pos);
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
static auto* const PSPECIALFALLTHRU = &g_pConfigManager->getConfigValuePtr("input:special_fallthrough")->intValue;
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
// pinned windows on top of floating regardless
if (properties & ALLOW_FLOATING) {
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
const auto BB = w->getWindowBoxUnified(properties);
CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_bNoFocus && w.get() != pIgnoreWindow) {
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}))
@@ -670,15 +711,17 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
}
}
}
}
auto windowForWorkspace = [&](bool special) -> CWindow* {
if (properties & ALLOW_FLOATING) {
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
for (auto& w : m_vWindows | std::views::reverse) {
if (special && !isWorkspaceSpecial(w->m_iWorkspaceID)) // because special floating may creep up into regular
continue;
const auto BB = w->getWindowInputBox();
const auto BB = w->getWindowBoxUnified(properties);
CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned && !w->m_bNoFocus && w.get() != pIgnoreWindow) {
// OR windows should add focus to parent
@@ -702,6 +745,10 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
}
}
}
}
if (properties & FLOATING_ONLY)
return nullptr;
const int64_t WORKSPACEID = special ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
const auto PWORKSPACE = getWorkspaceByID(WORKSPACEID);
@@ -725,7 +772,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
if (special != isWorkspaceSpecial(w->m_iWorkspaceID))
continue;
CBox box = {w->m_vPosition, w->m_vSize};
CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_vPosition, w->m_vSize};
if (!w->m_bIsFloating && w->m_bIsMapped && box.containsPoint(pos) && w->m_iWorkspaceID == WORKSPACEID && !w->isHidden() && !w->m_bX11ShouldntFocus && !w->m_bNoFocus &&
w.get() != pIgnoreWindow)
return w.get();
@@ -735,68 +782,17 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
};
// special workspace
if (PMONITOR->specialWorkspaceID)
if (PMONITOR->specialWorkspaceID && !*PSPECIALFALLTHRU)
return windowForWorkspace(true);
return windowForWorkspace(false);
}
CWindow* CCompositor::windowFromCursor() {
const auto PMONITOR = getMonitorFromCursor();
if (PMONITOR->specialWorkspaceID) {
for (auto& w : m_vWindows | std::views::reverse) {
CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) &&
!w->isHidden() && !w->m_bNoFocus)
return w.get();
const auto PWINDOW = windowForWorkspace(true);
if (PWINDOW)
return PWINDOW;
}
for (auto& w : m_vWindows) {
CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && !w->m_bNoFocus)
return w.get();
}
}
// pinned
for (auto& w : m_vWindows | std::views::reverse) {
CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
for (auto& w : m_vWindows | std::views::reverse) {
CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bNoFocus)
return w.get();
}
return nullptr;
}
CWindow* CCompositor::windowFloatingFromCursor() {
for (auto& w : m_vWindows | std::views::reverse) {
CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows | std::views::reverse) {
CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() &&
!w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
return nullptr;
return windowForWorkspace(false);
}
wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pWindow, Vector2D& sl) {
@@ -877,12 +873,18 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
static auto* const PSPECIALFALLTHROUGH = &g_pConfigManager->getConfigValuePtr("input:special_fallthrough")->intValue;
if (g_pCompositor->m_sSeat.exclusiveClient) {
Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer.");
return;
}
if (!g_pInputManager->m_dExclusiveLSes.empty()) {
Debug::log(LOG, "Refusing a keyboard focus to a window because of an exclusive ls");
return;
}
g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow);
if (!pWindow || !windowValidMapped(pWindow)) {
@@ -928,12 +930,13 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
if (pWindow->m_bPinned)
pWindow->m_iWorkspaceID = m_pLastMonitor->activeWorkspace;
const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID);
if (!isWorkspaceVisible(pWindow->m_iWorkspaceID)) {
// This is to fix incorrect feedback on the focus history.
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
// This is to fix incorrect feedback on the focus history.
PWORKSPACE->m_pLastFocusedWindow = pWindow;
PWORKSPACE->rememberPrevWorkspace(getWorkspaceByID(m_pLastMonitor->activeWorkspace));
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
PMONITOR->changeWorkspace(PWORKSPACE, false, true);
// changeworkspace already calls focusWindow
return;
@@ -942,6 +945,11 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
const auto PLASTWINDOW = m_pLastWindow;
m_pLastWindow = pWindow;
/* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which
window focuses are "via keybinds" and which ones aren't. */
if (PMONITOR->specialWorkspaceID && PMONITOR->specialWorkspaceID != pWindow->m_iWorkspaceID && !pWindow->m_bPinned && !*PSPECIALFALLTHROUGH)
PMONITOR->setSpecialWorkspace(nullptr);
// we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
if (windowValidMapped(PLASTWINDOW)) {
PLASTWINDOW->updateDynamicRules();
@@ -1015,10 +1023,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr()))
return; // Don't focus when already focused on this.
if (g_pSessionLockManager->isSessionLocked()) {
wlr_seat_keyboard_clear_focus(m_sSeat.seat);
m_pLastFocus = nullptr;
}
if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface))
return;
// Unfocus last surface if should
if (m_pLastFocus && !pWindowOwner)
@@ -1089,14 +1095,6 @@ wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<
auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y);
if (ls->layerSurface->current.keyboard_interactive && ls->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
if (!SURFACEAT)
SURFACEAT = ls->layerSurface->surface;
*ppLayerSurfaceFound = ls.get();
return SURFACEAT;
}
if (SURFACEAT) {
if (!pixman_region32_not_empty(&SURFACEAT->input_region))
continue;
@@ -1879,15 +1877,13 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
if (pWindow == m_pLastWindow) {
const auto* const ACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) :
*ACTIVECOLOR);
setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying().m_vColors.empty() ? *ACTIVECOLOR :
pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying());
} else {
const auto* const INACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) :
*INACTIVECOLOR);
setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying().m_vColors.empty() ? *INACTIVECOLOR :
pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying());
}
}
@@ -2111,7 +2107,7 @@ CMonitor* CCompositor::getMonitorFromString(const std::string& name) {
return nullptr;
}
void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor) {
void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor, bool noWarpCursor) {
// We trust the workspace and monitor to be correct.
@@ -2197,6 +2193,7 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
pWorkspace->startAnim(true, true, true);
if (!noWarpCursor)
wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2);
g_pInputManager->sendMotionEventsToFocused();
@@ -2284,7 +2281,6 @@ void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState());
pWindow->updateDynamicRules();
updateWindowAnimatedDecorationValues(pWindow);
// make all windows on the same workspace under the fullscreen window
@@ -2606,11 +2602,7 @@ int CCompositor::getNewSpecialID() {
}
void CCompositor::performUserChecks() {
if (g_pConfigManager->getInt("general:allow_tearing") == 1 && !envEnabled("WLR_DRM_NO_ATOMIC")) {
g_pHyprNotificationOverlay->addNotification("You have enabled tearing, but immediate presentations are not available on your configuration. Try adding "
"env = WLR_DRM_NO_ATOMIC,1 to your config.",
CColor(0), 15000, ICON_WARNING);
}
; // intentional
}
void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) {
@@ -2785,10 +2777,27 @@ void CCompositor::leaveUnsafeState() {
void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) {
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(pSurface, scale);
wlr_surface_set_preferred_buffer_scale(pSurface, static_cast<int32_t>(std::ceil(scale)));
const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface);
if (!PSURFACE) {
Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface);
return;
}
PSURFACE->m_fLastScale = scale;
PSURFACE->m_iLastScale = static_cast<int32_t>(std::ceil(scale));
}
void CCompositor::setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform) {
wlr_surface_set_preferred_buffer_transform(pSurface, transform);
const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface);
if (!PSURFACE) {
Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface);
return;
}
PSURFACE->m_eLastTransform = transform;
}
void CCompositor::updateSuspendedStates() {

View File

@@ -135,14 +135,11 @@ class CCompositor {
void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr);
bool windowExists(CWindow*);
bool windowValidMapped(CWindow*);
CWindow* vectorToWindowIdeal(const Vector2D&, CWindow* pIgnoreWindow = nullptr); // used only for finding a window to focus on, basically a "findFocusableWindow"
CWindow* vectorToWindowTiled(const Vector2D&);
CWindow* vectorToWindowUnified(const Vector2D&, uint8_t properties, CWindow* pIgnoreWindow = nullptr);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**);
SIMEPopup* vectorToIMEPopup(const Vector2D& pos, std::list<SIMEPopup>& popups);
wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*);
CWindow* windowFromCursor();
CWindow* windowFloatingFromCursor();
CMonitor* getMonitorFromOutput(wlr_output*);
CWindow* getWindowForPopup(wlr_xdg_popup*);
CWindow* getWindowFromSurface(wlr_surface*);
@@ -176,7 +173,7 @@ class CCompositor {
void updateWorkspaceWindows(const int64_t& id);
void updateWindowAnimatedDecorationValues(CWindow*);
int getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&);
@@ -214,6 +211,7 @@ class CCompositor {
private:
void initAllSignals();
void removeAllSignals();
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();

View File

@@ -1,6 +1,7 @@
#pragma once
#include "helpers/Vector2D.hpp"
#include <functional>
enum eIcons {
ICON_WARNING = 0,
@@ -52,4 +53,20 @@ struct SWindowDecorationExtents {
bool operator==(const SWindowDecorationExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight;
}
void addExtents(const SWindowDecorationExtents& other) {
topLeft = topLeft.getComponentMax(other.topLeft);
bottomRight = bottomRight.getComponentMax(other.bottomRight);
}
};
enum eHyprCtlOutputFormat {
FORMAT_NORMAL = 0,
FORMAT_JSON
};
struct SHyprCtlCommand {
std::string name = "";
bool exact = true;
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
};

View File

@@ -139,35 +139,25 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
}
CBox CWindow::getWindowInputBox() {
const int BORDERSIZE = getRealBorderSize();
CBox CWindow::getWindowBoxUnified(uint64_t properties) {
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
SWindowDecorationExtents EXTENTS = {{0, 0}, {0, 0}};
if (properties & RESERVED_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(this));
if (properties & INPUT_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, true));
if (properties & FULL_EXTENTS)
EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, false));
const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this, true);
CBox box = {m_vRealPosition.vec().x, m_vRealPosition.vec().y, m_vRealSize.vec().x, m_vRealSize.vec().y};
box.addExtents(EXTENTS);
if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
// Add extents to the real base BB and return
CBox finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
return finalBox;
return box;
}
CBox CWindow::getWindowMainSurfaceBox() {
@@ -318,7 +308,7 @@ void CWindow::destroyToplevelHandle() {
}
void CWindow::updateToplevel() {
updateSurfaceOutputs();
updateSurfaceScaleTransformDetails();
if (!m_phForeignToplevel)
return;
@@ -345,8 +335,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) {
wlr_surface_send_leave(pSurface, OUTPUT);
}
void CWindow::updateSurfaceOutputs() {
if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden)
void CWindow::updateSurfaceScaleTransformDetails() {
if (!m_bIsMapped || m_bHidden)
return;
const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID);
@@ -355,16 +345,23 @@ void CWindow::updateSurfaceOutputs() {
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PNEWMONITOR != PLASTMONITOR) {
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled)
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output);
}
wlr_surface_for_each_surface(
m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID);
g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR ? PMONITOR->scale : 1.f);
const auto PSURFACE = CWLSurface::surfaceFromWlr(surf);
if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale)
return;
g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform);
},
this);
@@ -511,6 +508,14 @@ void CWindow::onMap() {
"CWindow");
m_vReportedSize = m_vPendingReportedSize;
for (const auto& ctrl : g_pHyprRenderer->m_vTearingControllers) {
if (ctrl->pWlrHint->surface != m_pWLSurface.wlr())
continue;
m_bTearingHint = ctrl->pWlrHint->current;
break;
}
}
void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -607,14 +612,42 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.animationStyle = STYLE;
} else if (r.szRule.starts_with("bordercolor")) {
try {
std::string colorPart = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
if (colorPart.contains(' ')) {
// we have a space, 2 values
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' ')));
m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1));
} else {
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0])));
m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1])));
return;
}
for (auto& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_vColors.push_back(configStringToInt(token));
else
inactiveBorderGradient.m_vColors.push_back(configStringToInt(token));
}
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule);
else if (activeBorderGradient.m_vColors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule);
else if (inactiveBorderGradient.m_vColors.empty())
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
else {
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient;
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "dimaround") {
@@ -644,8 +677,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
}
void CWindow::updateDynamicRules() {
m_sSpecialRenderData.activeBorderColor = -1;
m_sSpecialRenderData.inactiveBorderColor = -1;
m_sSpecialRenderData.activeBorderColor = CGradientValueData();
m_sSpecialRenderData.inactiveBorderColor = CGradientValueData();
m_sSpecialRenderData.alpha = 1.f;
m_sSpecialRenderData.alphaInactive = -1.f;
m_sAdditionalConfigData.forceNoBlur = false;
@@ -1026,7 +1059,7 @@ int CWindow::getRealBorderSize() {
}
bool CWindow::canBeTorn() {
return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied;
return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint);
}
bool CWindow::shouldSendFullscreenState() {

View File

@@ -30,6 +30,16 @@ enum eGroupRules {
GROUP_OVERRIDE = 1 << 6, // Override other rules
};
enum eGetWindowProperties {
WINDOW_ONLY = 0,
RESERVED_EXTENTS = 1 << 0,
INPUT_EXTENTS = 1 << 1,
FULL_EXTENTS = 1 << 2,
FLOATING_ONLY = 1 << 3,
ALLOW_FLOATING = 1 << 4,
USE_PROP_TILED = 1 << 5
};
class IWindowTransformer;
template <typename T>
@@ -111,8 +121,8 @@ struct SWindowSpecialRenderData {
CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<int64_t> activeBorderColor = -1; // -1 means unset
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset
CWindowOverridableVar<CGradientValueData> activeBorderColor = CGradientValueData(); // empty color vector means unset
CWindowOverridableVar<CGradientValueData> inactiveBorderColor = CGradientValueData(); // empty color vector means unset
// set by the layout
CWindowOverridableVar<int> borderSize = -1; // -1 means unset
@@ -342,7 +352,7 @@ class CWindow {
// methods
CBox getFullWindowBoundingBox();
SWindowDecorationExtents getFullWindowExtents();
CBox getWindowInputBox();
CBox getWindowBoxUnified(uint64_t props);
CBox getWindowMainSurfaceBox();
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
@@ -356,7 +366,7 @@ class CWindow {
void createToplevelHandle();
void destroyToplevelHandle();
void updateToplevel();
void updateSurfaceOutputs();
void updateSurfaceScaleTransformDetails();
void moveToWorkspace(int);
CWindow* X11TransientFor();
void onUnmap();

View File

@@ -16,6 +16,7 @@ class ICustomConfigValueData {
class CGradientValueData : public ICustomConfigValueData {
public:
CGradientValueData(){};
CGradientValueData(CColor col) {
m_vColors.push_back(col);
};

View File

@@ -99,7 +99,6 @@ void CConfigManager::setDefaultVars() {
configValues["misc:disable_hyprland_logo"].intValue = 0;
configValues["misc:disable_splash_rendering"].intValue = 0;
configValues["misc:force_hypr_chan"].intValue = 0;
configValues["misc:force_default_wallpaper"].intValue = -1;
configValues["misc:vfr"].intValue = 1;
configValues["misc:vrr"].intValue = 0;
@@ -138,6 +137,7 @@ void CConfigManager::setDefaultVars() {
configValues["group:groupbar:font_family"].strValue = "Sans";
configValues["group:groupbar:font_size"].intValue = 8;
configValues["group:groupbar:gradients"].intValue = 1;
configValues["group:groupbar:height"].intValue = 14;
configValues["group:groupbar:priority"].intValue = 3;
configValues["group:groupbar:render_titles"].intValue = 1;
configValues["group:groupbar:scrolling"].intValue = 1;
@@ -223,6 +223,7 @@ void CConfigManager::setDefaultVars() {
configValues["input:follow_mouse"].intValue = 1;
configValues["input:mouse_refocus"].intValue = 1;
configValues["input:special_fallthrough"].intValue = 0;
configValues["input:sensitivity"].floatValue = 0.f;
configValues["input:accel_profile"].strValue = STRVAL_EMPTY;
configValues["input:kb_file"].strValue = STRVAL_EMPTY;
@@ -253,6 +254,7 @@ void CConfigManager::setDefaultVars() {
configValues["input:touchpad:scroll_factor"].floatValue = 1.f;
configValues["input:touchdevice:transform"].intValue = 0;
configValues["input:touchdevice:output"].strValue = STRVAL_EMPTY;
configValues["input:touchdevice:enabled"].intValue = 1;
configValues["input:tablet:transform"].intValue = 0;
configValues["input:tablet:output"].strValue = STRVAL_EMPTY;
configValues["input:tablet:region_position"].vecValue = Vector2D();
@@ -318,7 +320,7 @@ void CConfigManager::setDeviceDefaultVars(const std::string& dev) {
cfgValues["scroll_points"].strValue = STRVAL_EMPTY;
cfgValues["transform"].intValue = 0;
cfgValues["output"].strValue = STRVAL_EMPTY;
cfgValues["enabled"].intValue = 1; // only for mice / touchpads
cfgValues["enabled"].intValue = 1; // only for mice, touchpads, and touchdevices
cfgValues["region_position"].vecValue = Vector2D(); // only for tablets
cfgValues["region_size"].vecValue = Vector2D(); // only for tablets
cfgValues["relative_input"].intValue = 0; // only for tablets
@@ -395,10 +397,20 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s
if (!COMMAND.starts_with("device:") /* devices parsed later */ && !COMMAND.starts_with("plugin:") /* plugins parsed later */) {
if (COMMAND[0] == '$') {
// register a dynamic var
bool found = false;
for (auto& [var, val] : configDynamicVars) {
if (var == COMMAND.substr(1)) {
Debug::log(LOG, "Registered new value for dynamic var \"{}\" -> {}", COMMAND, VALUE);
val = VALUE;
found = true;
}
}
if (!found) {
Debug::log(LOG, "Registered dynamic var \"{}\" -> {}", COMMAND, VALUE);
configDynamicVars.emplace_back(std::make_pair<>(COMMAND.substr(1), VALUE));
std::sort(configDynamicVars.begin(), configDynamicVars.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); });
}
} else {
parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field.";
}
@@ -539,7 +551,7 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s
}
}
if (COMMAND == "decoration:screen_shader") {
if (COMMAND == "decoration:screen_shader" && VALUE != STRVAL_EMPTY) {
const auto PATH = absolutePath(VALUE, configCurrentPath);
configPaths.push_back(PATH);
@@ -588,12 +600,15 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) {
mode.vtotal = std::stoi(args[argno++]);
mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal;
// clang-format off
static std::unordered_map<std::string, uint32_t> flagsmap = {
{"+hsync", DRM_MODE_FLAG_PHSYNC},
{"-hsync", DRM_MODE_FLAG_NHSYNC},
{"+vsync", DRM_MODE_FLAG_PVSYNC},
{"-vsync", DRM_MODE_FLAG_NVSYNC},
{"Interlace", DRM_MODE_FLAG_INTERLACE},
};
// clang-format on
for (; argno < static_cast<int>(args.size()); argno++) {
auto key = args[argno];
@@ -935,7 +950,7 @@ void CConfigManager::handleBind(const std::string& command, const std::string& v
g_pKeybindManager->addKeybind(
SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
else
g_pKeybindManager->addKeybind(SKeybind{KEY, -1, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
g_pKeybindManager->addKeybind(SKeybind{KEY, 0, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
}
}
@@ -2297,30 +2312,30 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue;
static auto ensureVRRForDisplay = [&](CMonitor* m) -> void {
if (!m->output)
if (!m->output || m->createdByUser)
return;
const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : *PVRR;
if (USEVRR == 0) {
if (m->vrrActive) {
wlr_output_enable_adaptive_sync(m->output, 0);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
if (!wlr_output_commit(m->output))
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name);
}
m->vrrActive = false;
return;
} else if (USEVRR == 1) {
if (!m->vrrActive) {
wlr_output_enable_adaptive_sync(m->output, 1);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1);
if (!wlr_output_test(m->output)) {
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
wlr_output_enable_adaptive_sync(m->output, 0);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
}
if (!wlr_output_commit(m->output))
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
}
m->vrrActive = true;
@@ -2337,20 +2352,20 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) {
wlr_output_enable_adaptive_sync(m->output, 1);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1);
if (!wlr_output_test(m->output)) {
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
wlr_output_enable_adaptive_sync(m->output, 0);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
}
if (!wlr_output_commit(m->output))
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
} else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) {
wlr_output_enable_adaptive_sync(m->output, 0);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
if (!wlr_output_commit(m->output))
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name);
}
}

View File

@@ -119,7 +119,7 @@ gestures {
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
}
# Example per-device config

View File

@@ -119,7 +119,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += Debug::rollingLog;
finalCrashReport += Debug::rollingLog.substr(Debug::rollingLog.find("\n") + 1);
const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME");

View File

@@ -7,6 +7,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
@@ -28,7 +29,7 @@ static std::string getWorkspaceNameFromSpecialID(const int workspaceID) {
return workspace->m_szName;
}
std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
auto allMonitors = false;
@@ -39,7 +40,7 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f
allMonitors = true;
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
@@ -109,8 +110,8 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f
return result;
}
static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) {
const bool isJson = format == HyprCtl::FORMAT_JSON;
static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) {
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
if (!w->m_sGroupData.pNextWindow)
return isJson ? "" : "0";
@@ -133,7 +134,7 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
return result.str();
}
static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) {
static std::string getWindowData(CWindow* w, eHyprCtlOutputFormat format) {
auto getFocusHistoryID = [](CWindow* wnd) -> int {
for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) {
if (g_pCompositor->m_vWindowFocusHistory[i] == wnd)
@@ -142,7 +143,7 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
return -1;
};
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(
R"#({{
"address": "0x{:x}",
@@ -198,9 +199,9 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
}
}
std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWindows) {
@@ -218,10 +219,10 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat format) {
static std::string getWorkspaceData(CWorkspace* w, eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#({{
"id": {},
"name": "{}",
@@ -242,9 +243,9 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat
}
}
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprCtlOutputFormat format) {
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputFormat format) {
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor));
const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : "";
const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : "";
@@ -280,7 +281,7 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprC
return result;
}
}
std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
if (!g_pCompositor->m_pLastMonitor)
return "unsafe state";
@@ -293,10 +294,10 @@ std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
return getWorkspaceData(w, format);
}
std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format);
@@ -314,9 +315,9 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
@@ -334,24 +335,24 @@ std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_pLastWindow;
if (!g_pCompositor->windowValidMapped(PWINDOW))
return format == HyprCtl::FORMAT_JSON ? "{}" : "Invalid";
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
auto result = getWindowData(PWINDOW, format);
if (format == HyprCtl::FORMAT_JSON)
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
result.pop_back();
return result;
}
std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) {
@@ -422,9 +423,9 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
@@ -444,10 +445,10 @@ std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
result += "\"mice\": [\n";
@@ -603,9 +604,9 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
@@ -656,10 +657,10 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[\n\"log\":\"";
result += escapeJSONStrings(Debug::rollingLog);
result += "\"]";
@@ -670,10 +671,10 @@ std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else {
@@ -693,9 +694,9 @@ std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& kb : g_pKeybindManager->m_lKeybinds) {
ret += "bind";
if (kb.locked)
@@ -741,12 +742,12 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
@@ -793,7 +794,40 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ""; // make the compiler happy
}
std::string dispatchRequest(std::string in) {
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "");
result += "\n\nSystem Information:\n";
struct utsname unameInfo;
uname(&unameInfo);
result += "System name: " + std::string{unameInfo.sysname} + "\n";
result += "Node name: " + std::string{unameInfo.nodename} + "\n";
result += "Release: " + std::string{unameInfo.release} + "\n";
result += "Version: " + std::string{unameInfo.version} + "\n";
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
#endif
result += "GPU information: \n" + GPUINFO + "\n\n";
result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n";
result += "plugins:\n";
for (auto& pl : g_pPluginSystem->getAllPlugins()) {
result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version);
}
return result;
}
std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
// get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1);
@@ -814,7 +848,7 @@ std::string dispatchRequest(std::string in) {
return "ok";
}
std::string dispatchKeyword(std::string in) {
std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// get rid of the keyword keyword
in = in.substr(in.find_first_of(' ') + 1);
@@ -862,7 +896,7 @@ std::string dispatchKeyword(std::string in) {
return retval;
}
std::string reloadRequest(const std::string& request) {
std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {
const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
@@ -877,20 +911,20 @@ std::string reloadRequest(const std::string& request) {
return "ok";
}
std::string killRequest() {
std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
g_pInputManager->setClickMode(CLICKMODE_KILL);
return "ok";
}
std::string splashRequest() {
std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
return g_pCompositor->m_szCurrentSplash;
}
std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();
if (format == HyprCtl::FORMAT_NORMAL) {
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y);
} else {
return std::format(R"#(
@@ -905,9 +939,7 @@ std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
return "error";
}
std::string getReply(std::string);
std::string dispatchBatch(std::string request) {
std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
// split by ;
request = request.substr(9);
@@ -931,7 +963,7 @@ std::string dispatchBatch(std::string request) {
nextItem();
while (curitem != "") {
reply += getReply(curitem);
reply += g_pHyprCtl->getReply(curitem);
nextItem();
}
@@ -939,7 +971,7 @@ std::string dispatchBatch(std::string request) {
return reply;
}
std::string dispatchSetCursor(std::string request) {
std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto SIZESTR = vars[vars.size() - 1];
@@ -971,7 +1003,7 @@ std::string dispatchSetCursor(std::string request) {
return "ok";
}
std::string switchXKBLayoutRequest(const std::string& request) {
std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto KB = vars[1];
@@ -1017,7 +1049,7 @@ std::string switchXKBLayoutRequest(const std::string& request) {
return "ok";
}
std::string dispatchSeterror(std::string request) {
std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
std::string errorMessage = "";
@@ -1046,7 +1078,7 @@ std::string dispatchSeterror(std::string request) {
return "ok";
}
std::string dispatchSetProp(std::string request) {
std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 4)
@@ -1104,9 +1136,9 @@ std::string dispatchSetProp(std::string request) {
} else if (PROP == "alphainactive") {
PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor") {
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "inactivebordercolor") {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") {
@@ -1130,7 +1162,7 @@ std::string dispatchSetProp(std::string request) {
return "ok";
}
std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = "";
auto nextItem = [&]() {
@@ -1155,7 +1187,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
if (!PCFGOPT)
return "no such option";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL)
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL)
return std::format("option {}\n\tint: {}\n\tfloat: {:.5f}\n\tstr: \"{}\"\n\tdata: {:x}\n\tset: {}", curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue,
(uintptr_t)PCFGOPT->data.get(), PCFGOPT->set);
else {
@@ -1174,7 +1206,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
}
}
std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
@@ -1182,7 +1214,7 @@ std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat
return "none";
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
@@ -1231,7 +1263,7 @@ void createOutputIter(wlr_backend* backend, void* data) {
}
}
std::string dispatchOutput(std::string request) {
std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = "";
auto nextItem = [&]() {
@@ -1280,7 +1312,7 @@ std::string dispatchOutput(std::string request) {
return "ok";
}
std::string dispatchPlugin(std::string request) {
std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
@@ -1323,7 +1355,7 @@ std::string dispatchPlugin(std::string request) {
return "ok";
}
std::string dispatchNotify(std::string request) {
std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 5)
@@ -1364,92 +1396,100 @@ std::string dispatchNotify(std::string request) {
return "ok";
}
std::string getReply(std::string request) {
auto format = HyprCtl::FORMAT_NORMAL;
CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
registerCommand(SHyprCtlCommand{"activeworkspace", true, activeWorkspaceRequest});
registerCommand(SHyprCtlCommand{"clients", true, clientsRequest});
registerCommand(SHyprCtlCommand{"kill", true, killRequest});
registerCommand(SHyprCtlCommand{"activewindow", true, activeWindowRequest});
registerCommand(SHyprCtlCommand{"layers", true, layersRequest});
registerCommand(SHyprCtlCommand{"version", true, versionRequest});
registerCommand(SHyprCtlCommand{"devices", true, devicesRequest});
registerCommand(SHyprCtlCommand{"splash", true, splashRequest});
registerCommand(SHyprCtlCommand{"cursorpos", true, cursorPosRequest});
registerCommand(SHyprCtlCommand{"binds", true, bindsRequest});
registerCommand(SHyprCtlCommand{"globalshortcuts", true, globalShortcutsRequest});
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
registerCommand(SHyprCtlCommand{"output", false, dispatchOutput});
registerCommand(SHyprCtlCommand{"dispatch", false, dispatchRequest});
registerCommand(SHyprCtlCommand{"keyword", false, dispatchKeyword});
registerCommand(SHyprCtlCommand{"setcursor", false, dispatchSetCursor});
registerCommand(SHyprCtlCommand{"getoption", false, dispatchGetOption});
registerCommand(SHyprCtlCommand{"decorations", false, decorationRequest});
registerCommand(SHyprCtlCommand{"[[BATCH]]", false, dispatchBatch});
startHyprCtlSocket();
}
std::shared_ptr<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
return m_vCommands.emplace_back(std::make_shared<SHyprCtlCommand>(cmd));
}
void CHyprCtl::unregisterCommand(const std::shared_ptr<SHyprCtlCommand>& cmd) {
std::erase(m_vCommands, cmd);
}
std::string CHyprCtl::getReply(std::string request) {
auto format = eHyprCtlOutputFormat::FORMAT_NORMAL;
// process flags for non-batch requests
if (!request.contains("[[BATCH]]") && request.contains("/")) {
if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
long unsigned int sepIndex = 0;
for (const auto& c : request) {
if (c == '/') { // stop at separator
break;
}
// after whitespace assume the first word as a keyword,
// so its value can have slashes (e.g., a path)
if (c == ' ') {
sepIndex = request.size();
break;
}
sepIndex++;
if (c == 'j')
format = HyprCtl::FORMAT_JSON;
format = eHyprCtlOutputFormat::FORMAT_JSON;
}
if (sepIndex < request.size())
request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
}
if (request.starts_with("monitors"))
return monitorsRequest(request, format);
else if (request == "workspaces")
return workspacesRequest(format);
else if (request == "workspacerules")
return workspaceRulesRequest(format);
else if (request == "activeworkspace")
return activeWorkspaceRequest(format);
else if (request == "clients")
return clientsRequest(format);
else if (request == "kill")
return killRequest();
else if (request == "activewindow")
return activeWindowRequest(format);
else if (request == "layers")
return layersRequest(format);
else if (request == "version")
return versionRequest(format);
else if (request.starts_with("reload"))
return reloadRequest(request);
else if (request == "devices")
return devicesRequest(format);
else if (request == "splash")
return splashRequest();
else if (request == "cursorpos")
return cursorPosRequest(format);
else if (request == "binds")
return bindsRequest(format);
else if (request == "globalshortcuts")
return globalShortcutsRequest(format);
else if (request == "animations")
return animationsRequest(format);
else if (request == "rollinglog")
return rollinglogRequest(format);
else if (request == "layouts")
return layoutsRequest(format);
else if (request.starts_with("plugin"))
return dispatchPlugin(request);
else if (request.starts_with("notify"))
return dispatchNotify(request);
else if (request.starts_with("setprop"))
return dispatchSetProp(request);
else if (request.starts_with("seterror"))
return dispatchSeterror(request);
else if (request.starts_with("switchxkblayout"))
return switchXKBLayoutRequest(request);
else if (request.starts_with("output"))
return dispatchOutput(request);
else if (request.starts_with("dispatch"))
return dispatchRequest(request);
else if (request.starts_with("keyword"))
return dispatchKeyword(request);
else if (request.starts_with("setcursor"))
return dispatchSetCursor(request);
else if (request.starts_with("getoption"))
return dispatchGetOption(request, format);
else if (request.starts_with("decorations"))
return decorationRequest(request, format);
else if (request.starts_with("[[BATCH]]"))
return dispatchBatch(request);
// parse exact cmds first, then non-exact.
for (auto& cmd : m_vCommands) {
if (!cmd->exact)
continue;
if (cmd->name == request)
return cmd->fn(format, request);
}
for (auto& cmd : m_vCommands) {
if (cmd->exact)
continue;
if (request.starts_with(cmd->name))
return cmd->fn(format, request);
}
return "unknown request";
}
std::string HyprCtl::makeDynamicCall(const std::string& input) {
std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input);
}
@@ -1460,7 +1500,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
std::array<char, 1024> readBuffer;
@@ -1490,7 +1530,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
std::string reply = "";
try {
reply = getReply(request);
reply = g_pHyprCtl->getReply(request);
} catch (std::exception& e) {
Debug::log(ERR, "Error in request: {}", e.what());
reply = "Err: " + std::string(e.what());
@@ -1500,18 +1540,17 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_bWantsMonitorReload) {
if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->ensureMonitorStatus();
}
return 0;
}
void HyprCtl::startHyprCtlSocket() {
void CHyprCtl::startHyprCtlSocket() {
iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (iSocketFD < 0) {
if (m_iSocketFD < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return;
}
@@ -1522,15 +1561,15 @@ void HyprCtl::startHyprCtlSocket() {
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());
if (bind(iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
if (bind(m_iSocketFD, (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(iSocketFD, 10);
listen(m_iSocketFD, 10);
Debug::log(LOG, "Hypr socket started at {}", socketPath);
wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View File

@@ -3,24 +3,23 @@
#include "../Compositor.hpp"
#include <fstream>
#include "../helpers/MiscFunctions.hpp"
#include <functional>
class CHyprCtl {
public:
CHyprCtl();
namespace HyprCtl {
void startHyprCtlSocket();
std::string makeDynamicCall(const std::string& input);
std::shared_ptr<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
void unregisterCommand(const std::shared_ptr<SHyprCtlCommand>& cmd);
std::string getReply(std::string);
// very simple thread-safe request method
inline bool requestMade = false;
inline bool requestReady = false;
inline std::string request = "";
int m_iSocketFD = -1;
inline std::ifstream requestStream;
private:
void startHyprCtlSocket();
inline wl_event_source* hyprCtlTickSource = nullptr;
inline int iSocketFD = -1;
enum eHyprCtlOutputFormat {
FORMAT_NORMAL = 0,
FORMAT_JSON
};
std::vector<std::shared_ptr<SHyprCtlCommand>> m_vCommands;
};
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;

View File

@@ -142,6 +142,9 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output);
if (layersurface->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
g_pInputManager->m_dExclusiveLSes.push_back(layersurface);
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint);
@@ -183,6 +186,11 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", layersurface);
std::erase(g_pInputManager->m_dExclusiveLSes, layersurface);
if (!g_pInputManager->m_dExclusiveLSes.empty())
g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface);
if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");

View File

@@ -176,10 +176,16 @@ void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) {
Debug::log(LOG, "PowerMgr set mode!");
const auto EVENT = (wlr_output_power_v1_set_mode_event*)data;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(EVENT->output);
wlr_output_enable(EVENT->output, EVENT->mode == 1);
if (!PMONITOR) {
Debug::log(ERR, "Invalid powerMgrSetMode output");
return;
}
if (!wlr_output_commit(EVENT->output))
wlr_output_state_set_enabled(PMONITOR->state.wlr(), EVENT->mode == 1);
if (!PMONITOR->state.commit())
Debug::log(ERR, "Couldn't set power mode");
}
@@ -227,14 +233,7 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) {
void Events::listener_newTearingHint(wl_listener* listener, void* data) {
const auto TCTL = (wlr_tearing_control_v1*)data;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TCTL->surface);
if (!PWINDOW) {
Debug::log(ERR, "Tearing hint {} was attached to an unknown surface", (uintptr_t)data);
return;
}
Debug::log(LOG, "New tearing hint for window {} at {}", PWINDOW, (uintptr_t)data);
Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data);
const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get();
NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data;
@@ -242,7 +241,7 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) {
NEWCTRL->hyprListener_destroy.initCallback(
&NEWCTRL->pWlrHint->events.destroy,
[&](void* owner, void* data) {
Debug::log(LOG, "Destroyed {} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint);
Debug::log(LOG, "Destroyed {:x} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint);
std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; });
},
@@ -256,9 +255,9 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) {
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface);
if (PWINDOW) {
PWINDOW->m_bTearingHint = TEARINGHINT->pWlrHint->hint;
PWINDOW->m_bTearingHint = (bool)TEARINGHINT->pWlrHint->current;
Debug::log(LOG, "Hint {} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->hint);
Debug::log(LOG, "Hint {:x} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current);
}
},
NEWCTRL, "TearingController");

View File

@@ -109,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceOutputs();
w->updateSurfaceScaleTransformDetails();
}
}
}
@@ -209,10 +209,10 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
}
void Events::listener_monitorStateRequest(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_request_state*)data;
//const auto PMONITOR = (CMonitor*)owner;
//const auto E = (wlr_output_event_request_state*)data;
wlr_output_commit_state(PMONITOR->output, E->state);
// TODO: maybe don't ignore?
}
void Events::listener_monitorDamage(void* owner, void* data) {

View File

@@ -639,6 +639,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform);
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->constraintActive)
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely
@@ -803,8 +804,6 @@ void Events::listener_commitWindow(void* owner, void* data) {
PWINDOW->m_pPendingSizeAck.reset();
}
PWINDOW->updateSurfaceOutputs();
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
@@ -1054,11 +1053,8 @@ void Events::listener_configureX11(void* owner, void* data) {
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) {
const Vector2D DELTA = PWINDOW->m_vRealSize.goalv() - PWINDOW->m_vRealSize.goalv() / PMONITOR->scale;
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR)
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale);
PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0);
}
}
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec();
@@ -1139,8 +1135,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goalv();
PWINDOW->m_vReportedSize = PWINDOW->m_vRealSize.goalv();
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vReportedSize;
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goalv();
}
}

View File

@@ -1,4 +1,8 @@
#include "Box.hpp"
#include <limits>
#include <algorithm>
wlr_box CBox::wlr() {
CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h};
@@ -105,6 +109,13 @@ CBox& CBox::expand(const double& value) {
return *this;
}
CBox& CBox::noNegativeSize() {
std::clamp(w, 0.0, std::numeric_limits<double>::infinity());
std::clamp(h, 0.0, std::numeric_limits<double>::infinity());
return *this;
}
CBox CBox::roundInternal() {
float newW = x + w - std::floor(x);
float newH = y + h - std::floor(y);

View File

@@ -51,6 +51,7 @@ class CBox {
CBox& transform(const wl_output_transform t, double w, double h);
CBox& addExtents(const SWindowDecorationExtents& e);
CBox& expand(const double& value);
CBox& noNegativeSize();
CBox copy() const;
@@ -73,6 +74,8 @@ class CBox {
double height;
};
double rot = 0; /* rad, ccw */
//
bool operator==(const CBox& rhs) const {
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;

View File

@@ -159,6 +159,13 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const
Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString);
}
void removeWLSignal(wl_listener* pListener) {
wl_list_remove(&pListener->link);
wl_list_init(&pListener->link);
Debug::log(LOG, "Removed listener {:x}", (uintptr_t)pListener);
}
void handleNoop(struct wl_listener* listener, void* data) {
// Do nothing
}

View File

@@ -15,6 +15,7 @@ struct SCallstackFrameInfo {
std::string absolutePath(const std::string&, const std::string&);
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
void removeWLSignal(wl_listener*);
std::string escapeJSONStrings(const std::string& str);
std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false);

View File

@@ -8,7 +8,7 @@ int ratHandler(void* data) {
return 1;
}
CMonitor::CMonitor() {
CMonitor::CMonitor() : state(this) {
wlr_damage_ring_init(&damage);
}
@@ -43,8 +43,8 @@ void CMonitor::onConnect(bool noRule) {
tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
if (m_bEnabled) {
wlr_output_enable(output, 1);
wlr_output_commit(output);
wlr_output_state_set_enabled(state.wlr(), true);
state.commit();
return;
}
@@ -63,8 +63,8 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore
if (monitorRule.disabled) {
wlr_output_set_scale(output, 1);
wlr_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL);
wlr_output_state_set_scale(state.wlr(), 1);
wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL);
auto PREFSTATE = wlr_output_preferred_mode(output);
@@ -72,9 +72,9 @@ void CMonitor::onConnect(bool noRule) {
wlr_output_mode* mode;
wl_list_for_each(mode, &output->modes, link) {
wlr_output_set_mode(output, PREFSTATE);
wlr_output_state_set_mode(state.wlr(), mode);
if (!wlr_output_test(output))
if (!wlr_output_test_state(output, state.wlr()))
continue;
PREFSTATE = mode;
@@ -83,13 +83,13 @@ void CMonitor::onConnect(bool noRule) {
}
if (PREFSTATE)
wlr_output_set_mode(output, PREFSTATE);
wlr_output_state_set_mode(state.wlr(), PREFSTATE);
else
Debug::log(WARN, "No mode found for disabled output {}", output->name);
wlr_output_enable(output, 0);
wlr_output_state_set_enabled(state.wlr(), 0);
if (!wlr_output_commit(output))
if (!state.commit())
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false;
@@ -130,13 +130,14 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true;
wlr_output_enable(output, 1);
wlr_output_state_set_enabled(state.wlr(), 1);
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
wlr_output_commit(output);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
@@ -283,9 +284,10 @@ void CMonitor::onDisconnect(bool destroy) {
if (!destroy)
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output);
wlr_output_enable(output, false);
wlr_output_state_set_enabled(state.wlr(), false);
wlr_output_commit(output);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect");
if (g_pCompositor->m_pLastMonitor == this)
g_pCompositor->setActiveMonitor(BACKUPMON);
@@ -502,7 +504,7 @@ float CMonitor::getDefaultScale() {
return 1;
}
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove) {
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove, bool noFocus) {
if (!pWorkspace)
return;
@@ -533,13 +535,13 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool
}
}
if (!g_pCompositor->m_pLastMonitor->specialWorkspaceID) {
if (!noFocus && !g_pCompositor->m_pLastMonitor->specialWorkspaceID) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
CWindow* pWindow = pWorkspace->getLastFocusedWindow();
if (!pWindow) {
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal());
pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
@@ -621,7 +623,7 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
w->m_iMonitorID = ID;
w->updateSurfaceOutputs();
w->updateSurfaceScaleTransformDetails();
const auto MIDDLE = w->middle();
if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && w->m_iX11Type != 2) {
@@ -678,3 +680,32 @@ void CMonitor::updateMatrix() {
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
wlr_output_state_init(&m_state);
}
CMonitorState::~CMonitorState() {
wlr_output_state_finish(&m_state);
}
wlr_output_state* CMonitorState::wlr() {
return &m_state;
}
void CMonitorState::clear() {
wlr_output_state_finish(&m_state);
m_state = {0};
wlr_output_state_init(&m_state);
}
bool CMonitorState::commit() {
bool ret = wlr_output_commit_state(m_pOwner->output, &m_state);
clear();
return ret;
}
bool CMonitorState::test() {
return wlr_output_test_state(m_pOwner->output, &m_state);
}

View File

@@ -25,6 +25,25 @@ struct SMonitorRule {
std::optional<int> vrr;
};
class CMonitor;
// Class for wrapping the wlr state
class CMonitorState {
public:
CMonitorState(CMonitor* owner);
~CMonitorState();
wlr_output_state* wlr();
void clear();
// commit() will also clear()
bool commit();
bool test();
private:
wlr_output_state m_state = {0};
CMonitor* m_pOwner;
};
class CMonitor {
public:
CMonitor();
@@ -51,6 +70,8 @@ class CMonitor {
drmModeModeInfo customDrmMode = {};
CMonitorState state;
// WLR stuff
wlr_damage_ring damage;
wlr_output* output = nullptr;
@@ -120,7 +141,7 @@ class CMonitor {
void setMirror(const std::string&);
bool isMirror();
float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false);
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id);

View File

@@ -19,6 +19,21 @@ inline const std::vector<std::string> SPLASHES = {
"Compile, wait for 20 minutes, notice a new commit, compile again.",
"To rice, or not to rice, that is the question.",
"Now available on Fedora!",
"\"Hyprland is so good it starts with a capital letter\" - Hazel",
"\"please make this message a splash\" - eriedaberrie",
"\"the only wayland compositor powered by fried chicken\" - raf",
"\"This will never get into Hyprland\" - Flafy",
"\"Hyprland only gives you up on -git\" - fazzi",
"Segmentation fault (core dumped)",
"\"disabling hyprland logo is a war crime\" - vaxry",
"some basic startup code",
"\"I think I am addicted to hyprland\" - mathisbuilder",
"\"hyprland is the most important package in the arch repos\" - jacekpoz",
"Thanks Brodie!",
"Thanks fufexan!",
"Thanks raf!",
"You can't use --splash to change this message :)",
"Hyprland will overtake Gnome in popularity by [insert year]",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",

View File

@@ -178,6 +178,9 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface);
subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner);
if (subsurface->pWindowOwner)
subsurface->pWindowOwner->updateSurfaceScaleTransformDetails();
}
void Events::listener_unmapSubsurface(void* owner, void* data) {
@@ -259,13 +262,11 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
// tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(pNode->pWindowOwner->m_iMonitorID);
if (PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear &&
if (PMONITOR && PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear &&
pNode->pSurface->wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
CRegion damageBox;
wlr_surface_get_effective_damage(pNode->pSurface->wlr(), damageBox.pixman());
CRegion damageBox{&pNode->pSurface->wlr()->buffer_damage};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {
PMONITOR->tearingState.frameScheduledWhileBusy = true;
} else {

View File

@@ -42,14 +42,10 @@ double Vector2D::distance(const Vector2D& other) const {
return std::sqrt(dx * dx + dy * dy);
}
bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const {
const auto a = ((p2.y - p3.y) * (x - p3.x) + (p3.x - p2.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));
const auto b = ((p3.y - p1.y) * (x - p3.x) + (p1.x - p3.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));
const auto c = 1 - a - b;
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1;
}
double Vector2D::size() const {
return std::sqrt(x * x + y * y);
}
Vector2D Vector2D::getComponentMax(const Vector2D& other) const {
return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y));
}

View File

@@ -93,7 +93,7 @@ class Vector2D {
Vector2D floor() const;
Vector2D round() const;
bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const;
Vector2D getComponentMax(const Vector2D& other) const;
};
/**

View File

@@ -7,12 +7,14 @@
void handleWrapped(wl_listener* listener, void* data) {
CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener);
if (g_pWatchdog)
g_pWatchdog->startWatching();
try {
pWrap->m_pSelf->emit(data);
} catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); }
if (g_pWatchdog)
g_pWatchdog->endWatching();
}

View File

@@ -30,6 +30,12 @@ class CWLSurface {
// if present, means this is a base surface of a window. Cleaned on unassign()
CWindow* m_pOwner = nullptr;
// track surface data and avoid dupes
float m_fLastScale = 0;
int m_iLastScale = 0;
wl_output_transform m_eLastTransform = (wl_output_transform)-1;
//
CWLSurface& operator=(wlr_surface* pSurface) {
destroy();
m_pWLRSurface = pSurface;

View File

@@ -22,13 +22,13 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid
if (SPLITSIDE) {
// split left/right
const float FIRSTSIZE = box.w / 2.0 * splitRatio;
children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h};
children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h};
children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize();
children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize();
} else {
// split top/bottom
const float FIRSTSIZE = box.h / 2.0 * splitRatio;
children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE};
children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE};
children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize();
children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize();
}
children[0]->recalcSizePosRecursive(force);
@@ -64,6 +64,21 @@ SDwindleNodeData* CHyprDwindleLayout::getFirstNodeOnWorkspace(const int& id) {
return nullptr;
}
SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const int& id, const Vector2D& point) {
SDwindleNodeData* res = nullptr;
double distClosest = -1;
for (auto& n : m_lDwindleNodesData) {
if (n.workspaceID == id && n.pWindow && g_pCompositor->windowValidMapped(n.pWindow)) {
auto distAnother = vecToRectDistanceSquared(point, n.box.pos(), n.box.pos() + n.box.size());
if (!res || distAnother < distClosest) {
res = &n;
distClosest = distAnother;
}
}
}
return res;
}
SDwindleNodeData* CHyprDwindleLayout::getNodeFromWindow(CWindow* pWindow) {
for (auto& n : m_lDwindleNodesData) {
if (n.pWindow == pWindow && !n.isNode)
@@ -254,26 +269,25 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire
const auto MOUSECOORDS = m_vOverrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal());
const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS);
const auto TARGETCOORDS = PMONITOR->ID == MONFROMCURSOR->ID && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR) ? pWindow->middle() : MOUSECOORDS;
if (PMONITOR->ID == MONFROMCURSOR->ID &&
(PNODE->workspaceID == PMONITOR->activeWorkspace || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->specialWorkspaceID)) && !*PUSEACTIVE) {
OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS));
OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS));
// happens on reserved area
if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0)
OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace);
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR))
OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS);
} else if (*PUSEACTIVE) {
if (g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow != pWindow &&
g_pCompositor->m_pLastWindow->m_iWorkspaceID == pWindow->m_iWorkspaceID && g_pCompositor->m_pLastWindow->m_bIsMapped) {
OPENINGON = getNodeFromWindow(g_pCompositor->m_pLastWindow);
} else {
OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS));
OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS));
}
if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0)
OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace);
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR))
OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS);
} else
OPENINGON = getFirstNodeOnWorkspace(pWindow->m_iWorkspaceID);
@@ -386,34 +400,41 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire
if (*PERMANENTDIRECTIONOVERRIDE == 0)
overrideDirection = DIRECTION_DEFAULT;
} else if (*PSMARTSPLIT == 1) {
const auto tl = NEWPARENT->box.pos();
const auto tr = NEWPARENT->box.pos() + Vector2D(NEWPARENT->box.w, 0);
const auto bl = NEWPARENT->box.pos() + Vector2D(0, NEWPARENT->box.h);
const auto br = NEWPARENT->box.pos() + NEWPARENT->box.size();
const auto cc = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2;
const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2;
const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w;
const auto DELTA = MOUSECOORDS - PARENT_CENTER;
const auto DELTA_SLOPE = DELTA.y / DELTA.x;
if (TARGETCOORDS.inTriangle(tl, tr, cc)) {
NEWPARENT->splitTop = true;
NEWPARENT->children[0] = PNODE;
NEWPARENT->children[1] = OPENINGON;
} else if (TARGETCOORDS.inTriangle(tr, cc, br)) {
if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) {
if (DELTA.x > 0) {
// right
NEWPARENT->splitTop = false;
NEWPARENT->children[0] = OPENINGON;
NEWPARENT->children[1] = PNODE;
} else if (TARGETCOORDS.inTriangle(br, bl, cc)) {
NEWPARENT->splitTop = true;
NEWPARENT->children[0] = OPENINGON;
NEWPARENT->children[1] = PNODE;
} else {
// left
NEWPARENT->splitTop = false;
NEWPARENT->children[0] = PNODE;
NEWPARENT->children[1] = OPENINGON;
}
} else {
if (DELTA.y > 0) {
// bottom
NEWPARENT->splitTop = true;
NEWPARENT->children[0] = OPENINGON;
NEWPARENT->children[1] = PNODE;
} else {
// top
NEWPARENT->splitTop = true;
NEWPARENT->children[0] = PNODE;
NEWPARENT->children[1] = OPENINGON;
}
}
} else if (*PFORCESPLIT == 0 || !pWindow->m_bFirstMap) {
if ((SIDEBYSIDE &&
VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) ||
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) ||
(!SIDEBYSIDE &&
VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) {
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) {
// we are hovering over the first node, make PNODE first.
NEWPARENT->children[1] = OPENINGON;
NEWPARENT->children[0] = PNODE;
@@ -776,10 +797,21 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree
return;
}
// save position and size if floating
if (pWindow->m_bIsFloating && on) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
}
// otherwise, accept it.
pWindow->m_bIsFullscreen = on;
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
pWindow->updateDynamicRules();
pWindow->updateWindowDecos();
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
EMIT_HOOK_EVENT("fullscreen", pWindow);
@@ -800,14 +832,6 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
// save position and size if floating
if (pWindow->m_bIsFloating) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
}
// apply new pos and size being monitors' box
if (fullscreenMode == FULLSCREEN_FULL) {
pWindow->m_vRealPosition = PMONITOR->vecPosition;
@@ -824,7 +848,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree
pWindow->m_vPosition = fakeNode.box.pos();
pWindow->m_vSize = fakeNode.box.size();
applyNodeDataToWindow(&fakeNode);
applyNodeDataToWindow(&fakeNode, true);
}
}

View File

@@ -79,6 +79,7 @@ class CHyprDwindleLayout : public IHyprLayout {
void applyNodeDataToWindow(SDwindleNodeData*, bool force = false);
SDwindleNodeData* getNodeFromWindow(CWindow*);
SDwindleNodeData* getFirstNodeOnWorkspace(const int&);
SDwindleNodeData* getClosestNodeOnWorkspace(const int&, const Vector2D&);
SDwindleNodeData* getMasterNodeOnWorkspace(const int&);
void toggleSplit(CWindow*);

View File

@@ -268,9 +268,9 @@ void IHyprLayout::onEndDragWindow() {
} else if (g_pInputManager->dragMode == MBIND_MOVE) {
g_pHyprRenderer->damageWindow(DRAGGINGWINDOW);
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
CWindow* pWindow = g_pCompositor->vectorToWindowIdeal(MOUSECOORDS, DRAGGINGWINDOW);
CWindow* pWindow = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING | FLOATING_ONLY, DRAGGINGWINDOW);
if (pWindow && pWindow->m_bIsFloating) {
if (pWindow) {
if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGINGWINDOW))
return;
@@ -314,8 +314,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) ||
(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - TIMER).count() <
1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate &&
(*PANIMATEMOUSE || *PANIMATE)))
1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate))
return;
TIMER = std::chrono::high_resolution_clock::now();
@@ -469,16 +468,18 @@ void IHyprLayout::changeWindowFloatingMode(CWindow* pWindow) {
g_pCompositor->changeWindowZOrder(pWindow, true);
CBox wb = {pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize};
wb.round();
if (DELTALESSTHAN(pWindow->m_vRealSize.vec().x, pWindow->m_vLastFloatingSize.x, 10) && DELTALESSTHAN(pWindow->m_vRealSize.vec().y, pWindow->m_vLastFloatingSize.y, 10)) {
pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f + Vector2D{10, 10};
pWindow->m_vRealSize = pWindow->m_vLastFloatingSize - Vector2D{20, 20};
wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}};
}
pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f;
pWindow->m_vRealSize = pWindow->m_vLastFloatingSize;
pWindow->m_vRealPosition = wb.pos();
pWindow->m_vRealSize = wb.size();
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vSize = wb.pos();
pWindow->m_vPosition = wb.size();
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID));
@@ -542,7 +543,8 @@ CWindow* IHyprLayout::getNextWindowCandidate(CWindow* pWindow) {
return m_pLastTiledWindow;
// if we don't, let's try to find any window that is in the middle
if (const auto PWINDOWCANDIDATE = g_pCompositor->vectorToWindowIdeal(pWindow->middle()); PWINDOWCANDIDATE && PWINDOWCANDIDATE != pWindow)
if (const auto PWINDOWCANDIDATE = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
PWINDOWCANDIDATE && PWINDOWCANDIDATE != pWindow)
return PWINDOWCANDIDATE;
// if not, floating window
@@ -557,7 +559,7 @@ CWindow* IHyprLayout::getNextWindowCandidate(CWindow* pWindow) {
}
// if it was a tiled window, we first try to find the window that will replace it.
auto pWindowCandidate = g_pCompositor->vectorToWindowIdeal(pWindow->middle());
auto pWindowCandidate = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!pWindowCandidate)
pWindowCandidate = g_pCompositor->getTopLeftWindowOnWorkspace(pWindow->m_iWorkspaceID);

View File

@@ -879,10 +879,21 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen
return;
}
// save position and size if floating
if (pWindow->m_bIsFloating && on) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
}
// otherwise, accept it.
pWindow->m_bIsFullscreen = on;
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
pWindow->updateDynamicRules();
pWindow->updateWindowDecos();
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
EMIT_HOOK_EVENT("fullscreen", pWindow);
@@ -903,14 +914,6 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
// save position and size if floating
if (pWindow->m_bIsFloating) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
}
// apply new pos and size being monitors' box
if (fullscreenMode == FULLSCREEN_FULL) {
pWindow->m_vRealPosition = PMONITOR->vecPosition;
@@ -1328,7 +1331,13 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
const auto PWINDOW = header.pWindow;
const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE)
return 0;
const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID);
if (!OLDMASTER)
return 0;
const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER);
for (auto& nd : m_lMasterNodesData) {
@@ -1350,7 +1359,13 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
const auto PWINDOW = header.pWindow;
const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE)
return 0;
const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID);
if (!OLDMASTER)
return 0;
const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER);
for (auto& nd : m_lMasterNodesData | std::views::reverse) {

View File

@@ -85,7 +85,7 @@ int main(int argc, char** argv) {
}
if (!ignoreSudo && Init::isSudo()) {
std::cerr << "[ ERROR ] Hyprland was launched with superuser priveleges, but the privileges check is not omitted.\n";
std::cerr << "[ ERROR ] Hyprland was launched with superuser privileges, but the privileges check is not omitted.\n";
std::cerr << " Hint: Use the --i-am-really-stupid flag to omit that check.\n";
return 1;

View File

@@ -7,6 +7,7 @@
#include <sys/ioctl.h>
#include <fcntl.h>
#include <vector>
#if defined(__linux__)
#include <linux/vt.h>
#elif defined(__NetBSD__) || defined(__OpenBSD__)
@@ -45,6 +46,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["workspaceopt"] = workspaceOpt;
m_mDispatchers["exit"] = exitHyprland;
m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor;
m_mDispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor;
m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor;
m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace;
m_mDispatchers["forcerendererreload"] = forceRendererReload;
@@ -95,7 +97,7 @@ void CKeybindManager::addKeybind(SKeybind kb) {
void CKeybindManager::removeKeybind(uint32_t mod, const std::string& key) {
for (auto it = m_lKeybinds.begin(); it != m_lKeybinds.end(); ++it) {
if (isNumber(key) && std::stoi(key) > 9) {
const auto KEYNUM = std::stoi(key);
const uint32_t KEYNUM = std::stoi(key);
if (it->modmask == mod && it->keycode == KEYNUM) {
it = m_lKeybinds.erase(it);
@@ -137,6 +139,22 @@ uint32_t CKeybindManager::stringToModMask(std::string mods) {
return modMask;
}
uint32_t CKeybindManager::keycodeToModifier(xkb_keycode_t keycode) {
switch (keycode - 8) {
case KEY_LEFTMETA: return WLR_MODIFIER_LOGO;
case KEY_RIGHTMETA: return WLR_MODIFIER_LOGO;
case KEY_LEFTSHIFT: return WLR_MODIFIER_SHIFT;
case KEY_RIGHTSHIFT: return WLR_MODIFIER_SHIFT;
case KEY_LEFTCTRL: return WLR_MODIFIER_CTRL;
case KEY_RIGHTCTRL: return WLR_MODIFIER_CTRL;
case KEY_LEFTALT: return WLR_MODIFIER_ALT;
case KEY_RIGHTALT: return WLR_MODIFIER_ALT;
case KEY_CAPSLOCK: return WLR_MODIFIER_CAPS;
case KEY_NUMLOCK: return WLR_MODIFIER_MOD2;
default: return 0;
}
}
void CKeybindManager::updateXKBTranslationState() {
if (m_pXKBTranslationState) {
xkb_keymap_unref(xkb_state_get_keymap(m_pXKBTranslationState));
@@ -261,8 +279,7 @@ void CKeybindManager::switchToWindow(CWindow* PWINDOWTOCHANGETO) {
bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard) {
if (!g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
m_dPressedKeycodes.clear();
m_dPressedKeysyms.clear();
m_dPressedKeys.clear();
return true;
}
@@ -293,7 +310,14 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
bool mouseBindWasActive = ensureMouseBindState();
bool found = false;
const auto KEY = SPressedKeyWithMods{
.keysym = keysym,
.keycode = KEYCODE,
.modmaskAtPressTime = MODS,
.sent = true,
};
bool suppressEvent = false;
if (e->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
// clean repeat
if (m_pActiveKeybindEventSource) {
@@ -302,16 +326,15 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
m_pActiveKeybind = nullptr;
}
m_dPressedKeycodes.push_back(KEYCODE);
m_dPressedKeysyms.push_back(keysym);
m_dPressedKeys.push_back(KEY);
found = handleKeybinds(MODS, "", keysym, 0, true, e->time_msec) || found;
suppressEvent = handleKeybinds(MODS, KEY, true);
found = handleKeybinds(MODS, "", 0, KEYCODE, true, e->time_msec) || found;
if (found)
if (suppressEvent)
shadowKeybinds(keysym, KEYCODE);
} else if (e->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
m_dPressedKeys.back().sent = !suppressEvent;
} else { // key release
// clean repeat
if (m_pActiveKeybindEventSource) {
wl_event_source_remove(m_pActiveKeybindEventSource);
@@ -319,17 +342,27 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
m_pActiveKeybind = nullptr;
}
m_dPressedKeycodes.erase(std::remove(m_dPressedKeycodes.begin(), m_dPressedKeycodes.end(), KEYCODE), m_dPressedKeycodes.end());
m_dPressedKeysyms.erase(std::remove(m_dPressedKeysyms.begin(), m_dPressedKeysyms.end(), keysym), m_dPressedKeysyms.end());
found = handleKeybinds(MODS, "", keysym, 0, false, e->time_msec) || found;
found = handleKeybinds(MODS, "", 0, KEYCODE, false, e->time_msec) || found;
bool foundInPressedKeys = false;
for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) {
if (it->keycode == KEYCODE) {
suppressEvent = handleKeybinds(MODS, *it, false);
foundInPressedKeys = true;
suppressEvent = !it->sent;
it = m_dPressedKeys.erase(it);
} else {
++it;
}
}
if (!foundInPressedKeys) {
Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys");
// fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy
suppressEvent = handleKeybinds(MODS, KEY, false);
}
shadowKeybinds();
}
return !found && !mouseBindWasActive;
return !suppressEvent && !mouseBindWasActive;
}
bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
@@ -347,14 +380,14 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
bool found = false;
if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_VERTICAL) {
if (e->delta < 0)
found = handleKeybinds(MODS, "mouse_down", 0, 0, true, 0);
found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_down"}, true);
else
found = handleKeybinds(MODS, "mouse_up", 0, 0, true, 0);
found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_up"}, true);
} else if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) {
if (e->delta < 0)
found = handleKeybinds(MODS, "mouse_left", 0, 0, true, 0);
found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_left"}, true);
else
found = handleKeybinds(MODS, "mouse_right", 0, 0, true, 0);
found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_right"}, true);
}
if (found)
@@ -366,7 +399,7 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) {
const auto MODS = g_pInputManager->accumulateModsFromAllKBs();
bool found = false;
bool suppressEvent = false;
m_uLastMouseCode = e->button;
m_uLastCode = 0;
@@ -374,18 +407,44 @@ bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) {
bool mouseBindWasActive = ensureMouseBindState();
if (e->state == WLR_BUTTON_PRESSED) {
found = handleKeybinds(MODS, "mouse:" + std::to_string(e->button), 0, 0, true, 0);
const auto KEY_NAME = "mouse:" + std::to_string(e->button);
if (found)
const auto KEY = SPressedKeyWithMods{
.keyName = KEY_NAME,
.modmaskAtPressTime = MODS,
};
if (e->state == WLR_BUTTON_PRESSED) {
m_dPressedKeys.push_back(KEY);
suppressEvent = handleKeybinds(MODS, KEY, true);
if (suppressEvent)
shadowKeybinds();
m_dPressedKeys.back().sent = !suppressEvent;
} else {
found = handleKeybinds(MODS, "mouse:" + std::to_string(e->button), 0, 0, false, 0);
bool foundInPressedKeys = false;
for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) {
if (it->keyName == KEY_NAME) {
suppressEvent = handleKeybinds(MODS, *it, false);
foundInPressedKeys = true;
suppressEvent = !it->sent;
it = m_dPressedKeys.erase(it);
} else {
++it;
}
}
if (!foundInPressedKeys) {
Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys (2)");
// fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy
suppressEvent = handleKeybinds(MODS, KEY, false);
}
shadowKeybinds();
}
return !found && !mouseBindWasActive;
return !suppressEvent && !mouseBindWasActive;
}
void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) {
@@ -397,15 +456,15 @@ void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) {
}
void CKeybindManager::onSwitchEvent(const std::string& switchName) {
handleKeybinds(0, "switch:" + switchName, 0, 0, true, 0);
handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:" + switchName}, true);
}
void CKeybindManager::onSwitchOnEvent(const std::string& switchName) {
handleKeybinds(0, "switch:on:" + switchName, 0, 0, true, 0);
handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:on:" + switchName}, true);
}
void CKeybindManager::onSwitchOffEvent(const std::string& switchName) {
handleKeybinds(0, "switch:off:" + switchName, 0, 0, true, 0);
handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true);
}
int repeatKeyHandler(void* data) {
@@ -424,7 +483,7 @@ int repeatKeyHandler(void* data) {
return 0;
}
bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& key, const xkb_keysym_t& keysym, const int& keycode, bool pressed, uint32_t time) {
bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
bool found = false;
if (g_pCompositor->m_sSeat.exclusiveClient)
@@ -441,22 +500,19 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
continue;
if (!key.empty()) {
if (key != k.key)
if (!key.keyName.empty()) {
if (key.keyName != k.key)
continue;
} else if (k.keycode != -1) {
if (keycode != k.keycode)
} else if (k.keycode != 0) {
if (key.keycode != k.keycode)
continue;
} else {
if (keysym == 0)
continue; // this is a keycode check run
// oMg such performance hit!!11!
// this little maneouver is gonna cost us 4µs
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
if (keysym != KBKEY && keysym != KBKEYUPPER)
if (key.keysym != KBKEY && key.keysym != KBKEYUPPER)
continue;
}
@@ -468,13 +524,25 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
continue;
}
if (!pressed && !k.release && !SPECIALDISPATCHER) {
if (!pressed) {
// Require mods to be matching when the key was first pressed.
if (key.modmaskAtPressTime != modmask && !k.ignoreMods) {
// Handle properly `bindr` where a key is itself a bind mod for example:
// "bindr = SUPER, SUPER_L, exec, $launcher".
// This needs to be handled separately for the above case, because `key.modmaskAtPressTime` is set
// from currently pressed keys as programs see them, but it doesn't yet include the currently
// pressed mod key, which is still being handled internally.
if (keycodeToModifier(key.keycode) == key.modmaskAtPressTime)
continue;
} else if (!k.release && !SPECIALDISPATCHER) {
if (k.nonConsuming)
continue;
found = true; // suppress the event
continue;
}
}
const auto DISPATCHER = m_mDispatchers.find(k.mouse ? "mouse" : k.handler);
@@ -488,7 +556,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
Debug::log(ERR, "Invalid handler in a keybind! (handler {} does not exist)", k.handler);
} else {
// call the dispatcher
Debug::log(LOG, "Keybind triggered, calling dispatcher ({}, {}, {})", modmask, key, keysym);
Debug::log(LOG, "Keybind triggered, calling dispatcher ({}, {}, {})", modmask, key.keyName, key.keysym);
m_iPassPressed = (int)pressed;
@@ -521,7 +589,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
return found;
}
void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const int& doesntHaveCode) {
void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint32_t doesntHaveCode) {
// shadow disables keybinds after one has been triggered
for (auto& k : m_lKeybinds) {
@@ -534,22 +602,20 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const int&
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
for (auto& pk : m_dPressedKeysyms) {
if ((pk == KBKEY || pk == KBKEYUPPER)) {
for (auto& pk : m_dPressedKeys) {
if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
shadow = true;
if (pk == doesntHave && doesntHave != 0) {
if (pk.keysym == doesntHave && doesntHave != 0) {
shadow = false;
break;
}
}
}
for (auto& pk : m_dPressedKeycodes) {
if (pk == k.keycode) {
if (pk.keycode != 0 && pk.keycode == k.keycode) {
shadow = true;
if (pk == doesntHaveCode && doesntHaveCode != 0 && doesntHaveCode != -1) {
if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
shadow = false;
break;
}
@@ -1000,10 +1066,12 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
}
if (const auto PATCOORDS = g_pCompositor->vectorToWindowIdeal(OLDMIDDLE); PATCOORDS && PATCOORDS != PWINDOW)
if (PWINDOW == g_pCompositor->m_pLastWindow) {
if (const auto PATCOORDS = g_pCompositor->vectorToWindowUnified(OLDMIDDLE, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING, PWINDOW); PATCOORDS)
g_pCompositor->focusWindow(PATCOORDS);
else
g_pInputManager->refocus();
}
}
void CKeybindManager::moveFocusTo(std::string args) {
@@ -1385,7 +1453,7 @@ void CKeybindManager::renameWorkspace(std::string args) {
}
void CKeybindManager::exitHyprland(std::string argz) {
g_pCompositor->cleanup();
g_pInputManager->m_bExitTriggered = true;
}
void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) {
@@ -1435,6 +1503,48 @@ void CKeybindManager::moveWorkspaceToMonitor(std::string args) {
g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PMONITOR);
}
void CKeybindManager::focusWorkspaceOnCurrentMonitor(std::string args) {
std::string workspaceName;
const int WORKSPACEID = getWorkspaceIDFromString(args, workspaceName);
if (WORKSPACEID == WORKSPACE_INVALID) {
Debug::log(ERR, "focusWorkspaceOnCurrentMonitor invalid workspace!");
return;
}
const auto PCURRMONITOR = g_pCompositor->getMonitorFromCursor();
if (!PCURRMONITOR) {
Debug::log(ERR, "focusWorkspaceOnCurrentMonitor monitor doesn't exist!");
return;
}
auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID);
if (!PWORKSPACE) {
PWORKSPACE = g_pCompositor->createNewWorkspace(WORKSPACEID, PCURRMONITOR->ID);
// we can skip the moving, since it's already on the current monitor
changeworkspace(PWORKSPACE->getConfigName());
return;
}
if (PWORKSPACE->m_iMonitorID != PCURRMONITOR->ID) {
const auto POLDMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID);
if (!POLDMONITOR) { // wat
Debug::log(ERR, "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!");
return;
}
if (POLDMONITOR->activeWorkspace == WORKSPACEID) {
g_pCompositor->swapActiveWorkspaces(POLDMONITOR, PCURRMONITOR);
return;
} else {
g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PCURRMONITOR, true);
}
}
changeworkspace(PWORKSPACE->getConfigName());
}
void CKeybindManager::toggleSpecialWorkspace(std::string args) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
@@ -1742,11 +1852,11 @@ void CKeybindManager::dpms(std::string arg) {
if (!port.empty() && m->szName != port)
continue;
wlr_output_enable(m->output, enable);
wlr_output_state_set_enabled(m->state.wlr(), enable);
m->dpmsStatus = enable;
if (!wlr_output_commit(m->output)) {
if (!m->state.commit()) {
Debug::log(ERR, "Couldn't commit output {}", m->szName);
}
@@ -1829,7 +1939,7 @@ void CKeybindManager::pinActive(std::string args) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
PWORKSPACE->m_pLastFocusedWindow = g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal());
PWORKSPACE->m_pLastFocusedWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS);
}
void CKeybindManager::mouse(std::string args) {
@@ -1841,7 +1951,7 @@ void CKeybindManager::mouse(std::string args) {
g_pKeybindManager->m_bIsMouseBindActive = true;
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal();
CWindow* pWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords);
CWindow* pWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (pWindow && !pWindow->m_bIsFullscreen)
pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_START, mouseCoords);
@@ -1864,7 +1974,8 @@ void CKeybindManager::mouse(std::string args) {
if (PRESSED) {
g_pKeybindManager->m_bIsMouseBindActive = true;
g_pInputManager->currentlyDraggedWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal());
g_pInputManager->currentlyDraggedWindow =
g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
try {
switch (std::stoi(ARGS[1])) {

View File

@@ -12,7 +12,7 @@ class CPluginSystem;
struct SKeybind {
std::string key = "";
int keycode = -1;
uint32_t keycode = 0;
uint32_t modmask = 0;
std::string handler = "";
std::string arg = "";
@@ -36,6 +36,14 @@ enum eFocusWindowMode {
MODE_PID
};
struct SPressedKeyWithMods {
std::string keyName = "";
xkb_keysym_t keysym = 0;
uint32_t keycode = 0;
uint32_t modmaskAtPressTime = 0;
bool sent = false;
};
class CKeybindManager {
public:
CKeybindManager();
@@ -51,8 +59,9 @@ class CKeybindManager {
void addKeybind(SKeybind);
void removeKeybind(uint32_t, const std::string&);
uint32_t stringToModMask(std::string);
uint32_t keycodeToModifier(xkb_keycode_t);
void clearKeybinds();
void shadowKeybinds(const xkb_keysym_t& doesntHave = 0, const int& doesntHaveCode = 0);
void shadowKeybinds(const xkb_keysym_t& doesntHave = 0, const uint32_t doesntHaveCode = 0);
std::unordered_map<std::string, std::function<void(std::string)>> m_mDispatchers;
@@ -63,8 +72,7 @@ class CKeybindManager {
std::list<SKeybind> m_lKeybinds;
private:
std::deque<xkb_keysym_t> m_dPressedKeysyms;
std::deque<int> m_dPressedKeycodes;
std::deque<SPressedKeyWithMods> m_dPressedKeys;
inline static std::string m_szCurrentSelectedSubmap = "";
@@ -81,7 +89,7 @@ class CKeybindManager {
CTimer m_tScrollTimer;
bool handleKeybinds(const uint32_t&, const std::string&, const xkb_keysym_t&, const int&, bool, uint32_t);
bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool);
bool handleInternalKeybinds(xkb_keysym_t);
bool handleVT(xkb_keysym_t);
@@ -126,6 +134,7 @@ class CKeybindManager {
static void exitHyprland(std::string);
static void moveCurrentWorkspaceToMonitor(std::string);
static void moveWorkspaceToMonitor(std::string);
static void focusWorkspaceOnCurrentMonitor(std::string);
static void toggleSpecialWorkspace(std::string);
static void forceRendererReload(std::string);
static void resizeActive(std::string);

View File

@@ -18,8 +18,6 @@ int handleTimer(void* data) {
}
CThreadManager::CThreadManager() {
HyprCtl::startHyprCtlSocket();
m_esConfigTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, handleTimer, this);
wl_event_source_timer_update(m_esConfigTimer, 1000);

View File

@@ -261,7 +261,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
return;
}
const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowIdeal(mouseCoords);
const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (PWINDOWIDEAL &&
((PWINDOWIDEAL->m_bIsFloating && PWINDOWIDEAL->m_bCreatedOverFullscreen) /* floating over fullscreen */
@@ -282,19 +282,19 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
if (PMONITOR->specialWorkspaceID) {
pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords);
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (pFoundWindow && !g_pCompositor->isWorkspaceSpecial(pFoundWindow->m_iWorkspaceID)) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
}
} else {
pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords);
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!(pFoundWindow && pFoundWindow->m_bIsFloating && pFoundWindow->m_bCreatedOverFullscreen))
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
}
} else {
pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords);
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
}
if (pFoundWindow) {
@@ -421,7 +421,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
m_bLastFocusOnLS = false;
return; // don't enter any new surfaces
} else {
if (((FOLLOWMOUSE != 3 && allowKeyboardRefocus) && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus) {
if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus)) {
m_pLastMouseFocus = pFoundWindow;
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
}
@@ -599,10 +599,14 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
return;
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal();
const auto w = g_pCompositor->vectorToWindowIdeal(mouseCoords);
const auto w = g_pCompositor->vectorToWindowUnified(mouseCoords, ALLOW_FLOATING | RESERVED_EXTENTS | INPUT_EXTENTS);
if (w && !m_bLastFocusOnLS && w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e))
if (w && !m_bLastFocusOnLS) {
if (g_pCompositor->m_pLastWindow != w && *PFOLLOWMOUSE != 3)
g_pCompositor->focusWindow(w);
if (w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e))
return;
}
// clicking on border triggers resize
// TODO detect click on LS properly
@@ -653,7 +657,7 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
void CInputManager::processMouseDownKill(wlr_pointer_button_event* e) {
switch (e->state) {
case WLR_BUTTON_PRESSED: {
const auto PWINDOW = g_pCompositor->vectorToWindowIdeal(getMouseCoordsInternal());
const auto PWINDOW = g_pCompositor->vectorToWindowUnified(getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!PWINDOW) {
Debug::log(ERR, "Cannot kill invalid window!");
@@ -689,13 +693,14 @@ void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) {
if (!m_bLastFocusOnLS) {
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
const auto PWINDOW = g_pCompositor->vectorToWindowIdeal(MOUSECOORDS);
const auto PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (PWINDOW && PWINDOW->checkInputOnDecos(INPUT_TYPE_AXIS, MOUSECOORDS, e))
return;
}
wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source);
wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source,
WLR_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}
Vector2D CInputManager::getMouseCoordsInternal() {
@@ -1185,6 +1190,9 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
updateKeyboardsLeds(pKeyboard->keyboard);
}
if (m_bExitTriggered)
g_pCompositor->cleanup();
}
void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
@@ -1457,11 +1465,15 @@ void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
}
void CInputManager::setTouchDeviceConfigs(STouchDevice* dev) {
auto setConfig = [&](STouchDevice* const PTOUCHDEV) -> void {
if (wlr_input_device_is_libinput(PTOUCHDEV->pWlrDevice)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(PTOUCHDEV->pWlrDevice);
const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "enabled", "input:touchdevice:enabled");
const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
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->name, "transform", "input:touchdevice:transform"), 0, 7);
if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV))
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);

View File

@@ -142,6 +142,9 @@ class CInputManager {
// Switches
std::list<SSwitchDevice> m_lSwitches;
// Exclusive layer surfaces
std::deque<SLayerSurface*> m_dExclusiveLSes;
void newTabletTool(wlr_input_device*);
void newTabletPad(wlr_input_device*);
void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
@@ -243,6 +246,8 @@ class CInputManager {
void restoreCursorIconToApp(); // no-op if restored
bool m_bExitTriggered = false; // for exit dispatcher
friend class CKeybindManager;
friend class CWLSurface;
};

View File

@@ -265,7 +265,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) {
PWORKSPACE->m_bForceRendering = true;
PWORKSPACE->m_fAlpha.setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight) {
if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_sActiveSwipe.pWorkspaceBegin->m_iID) {
const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (PWORKSPACER) {
@@ -305,7 +305,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) {
PWORKSPACE->m_bForceRendering = true;
PWORKSPACE->m_fAlpha.setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight) {
if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_sActiveSwipe.pWorkspaceBegin->m_iID) {
const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (PWORKSPACEL) {

View File

@@ -75,8 +75,9 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr
if (code.contains("%rip")) {
CVarList tokens{code, 0, 's'};
size_t plusPresent = tokens[1][0] == '+' ? 1 : 0;
std::string addr = tokens[1].substr(plusPresent, tokens[1].find("(%rip)") - plusPresent);
const uint64_t OFFSET = configStringToInt(addr);
size_t minusPresent = tokens[1][0] == '-' ? 1 : 0;
std::string addr = tokens[1].substr((plusPresent || minusPresent), tokens[1].find("(%rip)") - (plusPresent || minusPresent));
const uint64_t OFFSET = (minusPresent ? -1 : 1) * configStringToInt(addr);
if (OFFSET == 0)
return {};
const uint64_t DESTINATION = currentAddress + OFFSET + len;
@@ -190,9 +191,9 @@ bool CFunctionHook::hook() {
(uint64_t)((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS));
// make jump to hk
const auto PAGESIZE = sysconf(_SC_PAGE_SIZE);
const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE);
const size_t PROTLEN = std::ceil((float)(ORIGSIZE + ((uint64_t)m_pSource - (uint64_t)PROTSTART)) / (float)PAGESIZE) * PAGESIZE;
const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE);
const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE_VAR);
const size_t PROTLEN = std::ceil((float)(ORIGSIZE + ((uint64_t)m_pSource - (uint64_t)PROTSTART)) / (float)PAGESIZE_VAR) * PAGESIZE_VAR;
mprotect((uint8_t*)PROTSTART, PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC);
memcpy((uint8_t*)m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS));

View File

@@ -50,9 +50,9 @@ APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn
APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format) {
if (args.empty())
return HyprCtl::makeDynamicCall(format + "/" + call);
return g_pHyprCtl->makeDynamicCall(format + "/" + call);
else
return HyprCtl::makeDynamicCall(format + "/" + call + " " + args);
return g_pHyprCtl->makeDynamicCall(format + "/" + call + " " + args);
}
APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) {
@@ -363,3 +363,27 @@ APICALL SVersionInfo HyprlandAPI::getHyprlandVersion(HANDLE handle) {
return {GIT_COMMIT_HASH, GIT_TAG, GIT_DIRTY != std::string(""), GIT_BRANCH, GIT_COMMIT_MESSAGE};
}
APICALL std::shared_ptr<SHyprCtlCommand> HyprlandAPI::registerHyprCtlCommand(HANDLE handle, SHyprCtlCommand cmd) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return nullptr;
auto PTR = g_pHyprCtl->registerCommand(cmd);
PLUGIN->registeredHyprctlCommands.push_back(PTR);
return PTR;
}
APICALL bool HyprlandAPI::unregisterHyprCtlCommand(HANDLE handle, std::shared_ptr<SHyprCtlCommand> cmd) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
std::erase(PLUGIN->registeredHyprctlCommands, cmd);
g_pHyprCtl->unregisterCommand(cmd);
return true;
}

View File

@@ -273,6 +273,20 @@ namespace HyprlandAPI {
for a different hash.
*/
APICALL SVersionInfo getHyprlandVersion(HANDLE handle);
/*
Registers a hyprctl command
returns: Pointer. Nullptr on fail.
*/
APICALL std::shared_ptr<SHyprCtlCommand> registerHyprCtlCommand(HANDLE handle, SHyprCtlCommand cmd);
/*
Unregisters a hyprctl command
returns: true on success. False otherwise.
*/
APICALL bool unregisterHyprCtlCommand(HANDLE handle, std::shared_ptr<SHyprCtlCommand> cmd);
};
/*

View File

@@ -113,6 +113,10 @@ void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) {
for (auto& d : rdi)
HyprlandAPI::removeDispatcher(plugin->m_pHandle, d);
const auto rhc = plugin->registeredHyprctlCommands;
for (auto& c : rhc)
HyprlandAPI::unregisterHyprCtlCommand(plugin->m_pHandle, c);
g_pConfigManager->removePluginConfig(plugin->m_pHandle);
dlclose(plugin->m_pHandle);

View File

@@ -23,6 +23,7 @@ class CPlugin {
std::vector<IHyprWindowDecoration*> registeredDecorations;
std::vector<std::pair<std::string, HOOK_CALLBACK_FN*>> registeredCallbacks;
std::vector<std::string> registeredDispatchers;
std::vector<std::shared_ptr<SHyprCtlCommand>> registeredHyprctlCommands;
};
class CPluginSystem {

View File

@@ -189,16 +189,39 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) {
void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* fb) {
m_RenderData.pMonitor = pMonitor;
#ifndef GLES2
const GLenum RESETSTATUS = glGetGraphicsResetStatus();
if (RESETSTATUS != GL_NO_ERROR) {
std::string errStr = "";
switch (RESETSTATUS) {
case GL_GUILTY_CONTEXT_RESET: errStr = "GL_GUILTY_CONTEXT_RESET"; break;
case GL_INNOCENT_CONTEXT_RESET: errStr = "GL_INNOCENT_CONTEXT_RESET"; break;
case GL_UNKNOWN_CONTEXT_RESET: errStr = "GL_UNKNOWN_CONTEXT_RESET"; break;
default: errStr = "UNKNOWN??"; break;
}
RASSERT(false, "Aborting, glGetGraphicsResetStatus returned {}. Cannot continue until proper GPU reset handling is implemented.", errStr);
return;
}
#endif
TRACY_GPU_ZONE("RenderBegin");
glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
matrixProjection(m_RenderData.projection, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, WL_OUTPUT_TRANSFORM_NORMAL);
if (m_mMonitorRenderResources.contains(pMonitor) && m_mMonitorRenderResources.at(pMonitor).offloadFB.m_vSize != pMonitor->vecPixelSize)
destroyMonitorResources(pMonitor);
m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor];
if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
initShaders();
// ensure a framebuffer for the monitor exists
if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) {
if (m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) {
m_RenderData.pCurrentMonData->stencilTex.allocate();
m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
@@ -210,16 +233,11 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer*
m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->offMainFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
createBGTextureForMonitor(pMonitor);
}
if (m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_RenderData.pMonitor->mirrors.empty())
m_RenderData.pCurrentMonData->monitorMirrorFB.release();
if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
initShaders();
m_RenderData.damage.set(*pDamage);
m_bFakeFrame = fb;
@@ -232,7 +250,8 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer*
const auto PRBO = g_pHyprRenderer->getCurrentRBO();
const bool FBPROPERSIZE = fb && fb->m_vSize == pMonitor->vecPixelSize;
if (!FBPROPERSIZE || m_sFinalScreenShader.program > 0 || (PRBO && pMonitor->vecPixelSize != PRBO->getFB()->m_vSize) || passRequiresIntrospection(pMonitor)) {
if (m_RenderData.forceIntrospection || !FBPROPERSIZE || m_sFinalScreenShader.program > 0 || (PRBO && pMonitor->vecPixelSize != PRBO->getFB()->m_vSize) ||
passRequiresIntrospection(pMonitor)) {
// we have to offload
// bind the offload Hypr Framebuffer
m_RenderData.pCurrentMonData->offloadFB.bind();
@@ -310,6 +329,16 @@ void CHyprOpenGLImpl::end() {
m_RenderData.pMonitor = nullptr;
m_RenderData.mouseZoomFactor = 1.f;
m_RenderData.mouseZoomUseMouse = true;
m_RenderData.forceIntrospection = false;
m_RenderData.currentFB = nullptr;
m_RenderData.mainFB = nullptr;
m_RenderData.outFB = nullptr;
// check for gl errors
const GLenum ERR = glGetError();
if (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */
RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented.");
}
void CHyprOpenGLImpl::initShaders() {
@@ -500,7 +529,7 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) {
m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj");
m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex");
m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time");
m_sFinalScreenShader.output = glGetUniformLocation(m_sFinalScreenShader.program, "output");
m_sFinalScreenShader.wl_output = glGetUniformLocation(m_sFinalScreenShader.program, "wl_output");
if (m_sFinalScreenShader.time != -1 && g_pConfigManager->getInt("debug:damage_tracking") != 0 && !g_pHyprRenderer->m_bCrashingInProgress) {
// The screen shader uses the "time" uniform
// Since the screen shader could change every frame, damage tracking *needs* to be disabled
@@ -637,12 +666,12 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CColor& col, CRegion
TRACY_GPU_ZONE("RenderRectWithDamage");
CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
box = &newBox;
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot,
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
@@ -719,18 +748,18 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
alpha = std::clamp(alpha, 0.f, 1.f);
if (m_RenderData.damage.empty())
if (damage->empty())
return;
CBox newBox = *pBox;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
static auto* const PDIMINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue;
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -792,8 +821,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
glUniform1f(shader->time, 0.f);
}
if (usingFinalShader && shader->output != -1)
glUniform1i(shader->output, m_RenderData.pMonitor->ID);
if (usingFinalShader && shader->wl_output != -1)
glUniform1i(shader->wl_output, m_RenderData.pMonitor->ID);
if (CRASHING) {
glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort);
@@ -886,12 +915,12 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) {
return;
CBox newBox = *pBox;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -940,12 +969,12 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame
return;
CBox newBox = *pBox;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -1475,14 +1504,14 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
return;
CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
box = &newBox;
if (borderSize < 1)
return;
int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale * m_RenderData.renderModif.scale);
int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale);
// adjust box
box->x -= scaledBorderSize;
@@ -1493,7 +1522,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
round += round == 0 ? 0 : scaledBorderSize;
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot,
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
@@ -1779,7 +1808,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
TRACY_GPU_ZONE("RenderShadow");
CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate);
m_RenderData.renderModif.applyToBox(newBox);
box = &newBox;
@@ -1790,7 +1819,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
const auto col = color;
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot,
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
@@ -1876,10 +1905,10 @@ void CHyprOpenGLImpl::renderMirrored() {
renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false);
}
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY) {
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) {
cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
const auto FONTSIZE = (int)(m_RenderData.pMonitor->vecPixelSize.y / 76);
const auto FONTSIZE = (int)(size.y / 76);
cairo_set_font_size(CAIRO, FONTSIZE);
cairo_set_source_rgba(CAIRO, 1.0, 1.0, 1.0, 0.32);
@@ -1887,7 +1916,7 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const
cairo_text_extents_t textExtents;
cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &textExtents);
cairo_move_to(CAIRO, (m_RenderData.pMonitor->vecPixelSize.x - textExtents.width) / 2.0, m_RenderData.pMonitor->vecPixelSize.y - textExtents.height + offsetY);
cairo_move_to(CAIRO, (size.x - textExtents.width) / 2.0, size.y - textExtents.height + offsetY);
cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str());
@@ -1899,51 +1928,37 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) {
static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue;
static auto* const PNOSPLASH = &g_pConfigManager->getConfigValuePtr("misc:disable_splash_rendering")->intValue;
static auto* const PFORCEHYPRCHAN = &g_pConfigManager->getConfigValuePtr("misc:force_hypr_chan")->intValue;
static auto* const PFORCEWALLPAPER = &g_pConfigManager->getConfigValuePtr("misc:force_default_wallpaper")->intValue;
const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast<int64_t>(-1L), static_cast<int64_t>(2L));
static std::string texPath = "";
if (*PRENDERTEX)
return;
// release the last tex if exists
const auto PTEX = &m_mMonitorBGTextures[pMonitor];
PTEX->destroyTexture();
const auto PFB = &m_mMonitorBGFBs[pMonitor];
PFB->release();
PTEX->allocate();
PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
Debug::log(LOG, "Allocated texture for BGTex");
// TODO: use relative paths to the installation
// or configure the paths at build time
std::string texPath = "/usr/share/hyprland/wall_";
std::string prefixes[] = {"", "anime_", "anime2_"};
if (texPath.empty()) {
texPath = "/usr/share/hyprland/wall";
// get the adequate tex
if (FORCEWALLPAPER == -1) {
std::random_device dev;
std::mt19937 engine(dev());
std::mt19937_64 engine(time(nullptr));
std::uniform_int_distribution<> distribution(0, 2);
std::uniform_int_distribution<> distribution_anime(1, 2);
if (PFORCEHYPRCHAN)
texPath += prefixes[distribution_anime(engine)];
else
texPath += prefixes[distribution(engine)];
texPath += std::to_string(distribution(engine));
} else
texPath += prefixes[FORCEWALLPAPER];
texPath += std::to_string(std::clamp(*PFORCEWALLPAPER, (int64_t)0, (int64_t)2));
Vector2D textureSize;
if (pMonitor->vecTransformedSize.x > 3850) {
textureSize = Vector2D(7680, 4320);
texPath += "8K.png";
} else if (pMonitor->vecTransformedSize.x > 1930) {
textureSize = Vector2D(3840, 2160);
texPath += "4K.png";
} else {
textureSize = Vector2D(1920, 1080);
texPath += "2K.png";
}
texPath += ".png";
// check if wallpapers exist
if (!std::filesystem::exists(texPath)) {
@@ -1953,55 +1968,83 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) {
if (!std::filesystem::exists(texPath))
return; // the texture will be empty, oh well. We'll clear with a solid color anyways.
}
}
PTEX->m_vSize = textureSize;
// create a new one with cairo
CTexture tex;
const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str());
const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE);
tex.allocate();
const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
// calc the target box
const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y;
const double WPRATIO = 1.77;
const double WPRATIO = IMAGESIZE.x / IMAGESIZE.y;
Vector2D origin;
double scale;
if (MONRATIO > WPRATIO) {
scale = m_RenderData.pMonitor->vecTransformedSize.x / PTEX->m_vSize.x;
scale = m_RenderData.pMonitor->vecTransformedSize.x / IMAGESIZE.x;
origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - PTEX->m_vSize.y * scale) / 2.0;
origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - IMAGESIZE.y * scale) / 2.0;
} else {
scale = m_RenderData.pMonitor->vecTransformedSize.y / PTEX->m_vSize.y;
scale = m_RenderData.pMonitor->vecTransformedSize.y / IMAGESIZE.y;
origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - PTEX->m_vSize.x * scale) / 2.0;
origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - IMAGESIZE.x * scale) / 2.0;
}
CBox box = {origin.x, origin.y, PTEX->m_vSize.x * scale, PTEX->m_vSize.y * scale};
const Vector2D scaledSize = IMAGESIZE * scale;
m_mMonitorRenderResources[pMonitor].backgroundTexBox = box;
// create a new one with cairo
const auto CAIROSURFACE = cairo_image_surface_create_from_png(texPath.c_str());
const auto CAIROSURFACE = cairo_image_surface_create(CAIROFORMAT, scaledSize.x, scaledSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
// scale it to fit the current monitor
cairo_scale(CAIRO, textureSize.x / pMonitor->vecTransformedSize.x, textureSize.y / pMonitor->vecTransformedSize.y);
cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD);
cairo_scale(CAIRO, scale, scale);
cairo_rectangle(CAIRO, 0, 0, 100, 100);
cairo_set_source_surface(CAIRO, CAIROISURFACE, 0, 0);
cairo_paint(CAIRO);
// render splash on wallpaper
if (!*PNOSPLASH)
renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO);
renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO * scale, IMAGESIZE);
cairo_surface_flush(CAIROSURFACE);
CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale};
tex.m_vSize = IMAGESIZE * scale;
// copy the data to an OpenGL texture we have
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
glBindTexture(GL_TEXTURE_2D, PTEX->m_iTexID);
glBindTexture(GL_TEXTURE_2D, tex.m_iTexID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#ifndef GLES2
if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
}
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.x, textureSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex.m_vSize.x, tex.m_vSize.y, 0, glFormat, glType, DATA);
cairo_surface_destroy(CAIROSURFACE);
cairo_surface_destroy(CAIROISURFACE);
cairo_destroy(CAIRO);
// render the texture to our fb
PFB->bind();
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
renderTextureInternalWithDamage(tex, &box, 1.0, &fakeDamage);
// bind back
if (m_RenderData.currentFB)
m_RenderData.currentFB->bind();
Debug::log(LOG, "Background created for monitor {}", pMonitor->szName);
}
@@ -2010,15 +2053,19 @@ void CHyprOpenGLImpl::clearWithTex() {
TRACY_GPU_ZONE("RenderClearWithTex");
auto TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor);
auto TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor);
if (TEXIT == m_mMonitorBGTextures.end()) {
if (TEXIT == m_mMonitorBGFBs.end()) {
createBGTextureForMonitor(m_RenderData.pMonitor);
TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor);
TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor);
}
if (TEXIT != m_mMonitorBGTextures.end())
renderTexturePrimitive(TEXIT->second, &m_mMonitorRenderResources[m_RenderData.pMonitor].backgroundTexBox);
if (TEXIT != m_mMonitorBGFBs.end()) {
CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
m_bEndFrame = true;
renderTexture(TEXIT->second.m_cTex, &monbox, 1);
m_bEndFrame = false;
}
}
void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
@@ -2036,10 +2083,10 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT);
}
auto TEXIT = g_pHyprOpenGL->m_mMonitorBGTextures.find(pMonitor);
if (TEXIT != g_pHyprOpenGL->m_mMonitorBGTextures.end()) {
TEXIT->second.destroyTexture();
g_pHyprOpenGL->m_mMonitorBGTextures.erase(TEXIT);
auto TEXIT = g_pHyprOpenGL->m_mMonitorBGFBs.find(pMonitor);
if (TEXIT != g_pHyprOpenGL->m_mMonitorBGFBs.end()) {
TEXIT->second.release();
g_pHyprOpenGL->m_mMonitorBGFBs.erase(TEXIT);
}
Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName);
@@ -2211,3 +2258,25 @@ const SGLPixelFormat* CHyprOpenGLImpl::getPixelFormatFromDRM(uint32_t drmFormat)
return nullptr;
}
void SRenderModifData::applyToBox(CBox& box) {
for (auto& [type, val] : modifs) {
try {
switch (type) {
case RMOD_TYPE_SCALE: box.scale(std::any_cast<float>(val)); break;
case RMOD_TYPE_SCALECENTER: box.scaleFromCenter(std::any_cast<float>(val)); break;
case RMOD_TYPE_TRANSLATE: box.translate(std::any_cast<Vector2D>(val)); break;
case RMOD_TYPE_ROTATE: box.rot += std::any_cast<float>(val); break;
case RMOD_TYPE_ROTATECENTER: {
const auto THETA = std::any_cast<float>(val);
const double COS = std::cos(THETA);
const double SIN = std::sin(THETA);
box.rot += THETA;
const auto OLDPOS = box.pos();
box.x = OLDPOS.x * COS - OLDPOS.y * SIN;
box.y = OLDPOS.y * COS + OLDPOS.x * SIN;
}
}
} catch (std::bad_any_cast& e) { Debug::log(ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToBox!"); }
}
}

View File

@@ -36,8 +36,17 @@ enum eDiscardMode {
};
struct SRenderModifData {
Vector2D translate = {};
float scale = 1.f;
enum eRenderModifType {
RMOD_TYPE_SCALE, /* scale by a float */
RMOD_TYPE_SCALECENTER, /* scale by a float from the center */
RMOD_TYPE_TRANSLATE, /* translate by a Vector2D */
RMOD_TYPE_ROTATE, /* rotate by a float in rad from top left */
RMOD_TYPE_ROTATECENTER, /* rotate by a float in rad from center */
};
std::vector<std::pair<eRenderModifType, std::any>> modifs;
void applyToBox(CBox& box);
};
struct SGLPixelFormat {
@@ -62,8 +71,6 @@ struct SMonitorRenderData {
bool blurFBDirty = true;
bool blurFBShouldRender = false;
CBox backgroundTexBox;
// Shaders
bool m_bShadersInitialized = false;
CShader m_shQUAD;
@@ -99,6 +106,7 @@ struct SCurrentRenderData {
float mouseZoomFactor = 1.f;
bool mouseZoomUseMouse = true; // true by default
bool useNearestNeighbor = false;
bool forceIntrospection = false; // cleaned in ::end()
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
@@ -181,7 +189,7 @@ class CHyprOpenGLImpl {
std::unordered_map<CWindow*, CFramebuffer> m_mWindowFramebuffers;
std::unordered_map<SLayerSurface*, CFramebuffer> m_mLayerFramebuffers;
std::unordered_map<CMonitor*, SMonitorRenderData> m_mMonitorRenderResources;
std::unordered_map<CMonitor*, CTexture> m_mMonitorBGTextures;
std::unordered_map<CMonitor*, CFramebuffer> m_mMonitorBGFBs;
struct {
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
@@ -219,7 +227,7 @@ class CHyprOpenGLImpl {
void renderTextureInternalWithDamage(const CTexture&, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false,
bool allowCustomUV = false, bool allowDim = false);
void renderTexturePrimitive(const CTexture& tex, CBox* pBox);
void renderSplash(cairo_t* const, cairo_surface_t* const, double);
void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size);
void preBlurForCurrentMonitor();

View File

@@ -9,8 +9,6 @@ extern "C" {
}
CHyprRenderer::CHyprRenderer() {
if (envEnabled("WLR_DRM_NO_ATOMIC"))
m_bTearingEnvSatisfied = true;
if (g_pCompositor->m_sWLRSession) {
wlr_device* dev;
@@ -107,18 +105,22 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data)
if (windowBox.width <= 1 || windowBox.height <= 1)
return; // invisible
g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface);
windowBox.scale(RDATA->pMonitor->scale);
windowBox.round();
const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ &&
windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ &&
DELTALESSTHAN(windowBox.width, surface->current.buffer_width, 3) && DELTALESSTHAN(windowBox.height, surface->current.buffer_height, 3) /* off by one-or-two */ &&
(!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1);
// check for fractional scale surfaces misaligning the buffer size
// in those cases it's better to just force nearest neighbor
// as long as the window is not animated. During those it'd look weird
// as long as the window is not animated. During those it'd look weird.
// UV will fixup it as well
const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor;
if (std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ &&
windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ &&
(!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */)
if (MISALIGNEDFSV1)
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
float rounding = RDATA->rounding;
@@ -161,7 +163,7 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data)
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
wlr_surface_send_frame_done(surface, RDATA->when);
wlr_presentation_surface_textured_on_output(g_pCompositor->m_sWLRPresentation, surface, RDATA->pMonitor->output);
wlr_presentation_surface_textured_on_output(surface, RDATA->pMonitor->output);
}
g_pHyprOpenGL->blend(true);
@@ -289,11 +291,14 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
continue;
}
if (w->m_iWorkspaceID != pMonitor->activeWorkspace || !w->m_bIsFullscreen)
if (!w->m_bIsFullscreen)
continue;
renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL);
if (w->m_iWorkspaceID != pWorkspace->m_iID)
continue;
pWorkspaceWindow = w.get();
}
@@ -452,7 +457,7 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec*
// clip box for animated offsets
const Vector2D PREOFFSETPOS = {renderdata.x, renderdata.y};
if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned) {
if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned && !pWindow->m_bIsFullscreen) {
Vector2D offset;
if (PWORKSPACE->m_vRenderOffset.vec().x != 0) {
@@ -658,7 +663,11 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace*
static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue;
static auto* const PBACKGROUNDCOLOR = &g_pConfigManager->getConfigValuePtr("misc:background_color")->intValue;
const SRenderModifData RENDERMODIFDATA = {translate, scale};
SRenderModifData RENDERMODIFDATA;
if (translate != Vector2D{0, 0})
RENDERMODIFDATA.modifs.push_back({SRenderModifData::eRenderModifType::RMOD_TYPE_TRANSLATE, translate});
if (scale != 1.f)
RENDERMODIFDATA.modifs.push_back({SRenderModifData::eRenderModifType::RMOD_TYPE_SCALE, scale});
if (!pMonitor)
return;
@@ -800,7 +809,7 @@ void CHyprRenderer::renderLockscreen(CMonitor* pMonitor, timespec* now) {
}
}
void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main) {
void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main, const Vector2D& projSize, bool fixMisalignedFSV1) {
if (!pWindow || !pWindow->m_bIsX11) {
Vector2D uvTL;
Vector2D uvBR = Vector2D(1, 1);
@@ -822,6 +831,15 @@ void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurfac
}
}
if (projSize != Vector2D{} && fixMisalignedFSV1) {
// instead of nearest_neighbor (we will repeat / skip)
// just cut off / expand surface
const Vector2D PIXELASUV = Vector2D{1, 1} / Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height};
const Vector2D MISALIGNMENT = Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height} - projSize;
if (MISALIGNMENT != Vector2D{})
uvBR -= MISALIGNMENT * PIXELASUV;
}
g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL;
g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR;
@@ -897,17 +915,17 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
return false;
// finally, we should be GTG.
wlr_output_attach_buffer(pMonitor->output, &PSURFACE->buffer->base);
wlr_output_state_set_buffer(pMonitor->state.wlr(), &PSURFACE->buffer->base);
if (!wlr_output_test(pMonitor->output))
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr()))
return false;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_surface_send_frame_done(PSURFACE, &now);
wlr_presentation_surface_scanned_out_on_output(g_pCompositor->m_sWLRPresentation, PSURFACE, pMonitor->output);
wlr_presentation_surface_scanned_out_on_output(PSURFACE, pMonitor->output);
if (wlr_output_commit(pMonitor->output)) {
if (pMonitor->state.commit()) {
if (!m_pLastScanout) {
m_pLastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE, PCANDIDATE->m_szTitle);
@@ -1008,14 +1026,15 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
const auto PGAMMACTRL = wlr_gamma_control_manager_v1_get_control(g_pCompositor->m_sWLRGammaCtrlMgr, pMonitor->output);
if (!wlr_gamma_control_v1_apply(PGAMMACTRL, &pMonitor->output->pending)) {
if (!wlr_gamma_control_v1_apply(PGAMMACTRL, pMonitor->state.wlr())) {
Debug::log(ERR, "Could not apply gamma control to {}", pMonitor->szName);
return;
}
if (!wlr_output_test(pMonitor->output)) {
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(ERR, "Output test failed for setting gamma to {}", pMonitor->szName);
wlr_output_rollback(pMonitor->output);
// aka rollback
wlr_gamma_control_v1_apply(nullptr, pMonitor->state.wlr());
wlr_gamma_control_v1_send_failed_and_destroy(PGAMMACTRL);
}
}
@@ -1144,6 +1163,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false);
pMonitor->state.clear();
return;
}
@@ -1230,9 +1251,9 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
EMIT_HOOK_EVENT("render", RENDER_POST);
pMonitor->output->pending.tearing_page_flip = shouldTear;
pMonitor->state.wlr()->tearing_page_flip = shouldTear;
if (!wlr_output_commit(pMonitor->output)) {
if (!pMonitor->state.commit()) {
if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false);
@@ -1319,6 +1340,8 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
std::string commandForCfg = "";
const auto OUTPUT = head->state.output;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT);
RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest");
commandForCfg += std::string(OUTPUT->name) + ",";
@@ -1329,7 +1352,7 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
continue;
}
wlr_output_enable(OUTPUT, head->state.enabled);
wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled);
if (head->state.mode)
commandForCfg +=
@@ -1343,10 +1366,10 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
if (!test) {
g_pConfigManager->parseKeyword("monitor", commandForCfg, true);
wlr_output_state_set_adaptive_sync_enabled(&OUTPUT->pending, head->state.adaptive_sync_enabled);
wlr_output_state_set_adaptive_sync_enabled(PMONITOR->state.wlr(), head->state.adaptive_sync_enabled);
}
ok = wlr_output_test(OUTPUT);
ok = wlr_output_test_state(OUTPUT, PMONITOR->state.wlr());
if (!ok)
break;
@@ -1567,8 +1590,7 @@ void CHyprRenderer::damageSurface(wlr_surface* pSurface, double x, double y, dou
y += CORRECTION.y;
}
CRegion damageBox;
wlr_surface_get_effective_damage(pSurface, damageBox.pixman());
CRegion damageBox{&pSurface->buffer_damage};
if (scale != 1.0)
wlr_region_scale(damageBox.pixman(), damageBox.pixman(), scale);
@@ -1745,6 +1767,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
return true;
}
const auto WAS10B = pMonitor->enabled10bit;
const auto OLDRES = pMonitor->vecPixelSize;
// Needed in case we are switching from a custom modeline to a standard mode
pMonitor->customDrmMode = {};
bool autoScale = false;
@@ -1757,10 +1782,10 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->scale = DEFAULTSCALE;
}
wlr_output_set_scale(pMonitor->output, pMonitor->scale);
wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale);
pMonitor->setScale = pMonitor->scale;
wlr_output_set_transform(pMonitor->output, pMonitorRule->transform);
wlr_output_state_set_transform(pMonitor->state.wlr(), pMonitorRule->transform);
pMonitor->transform = pMonitorRule->transform;
const auto WLRREFRESHRATE = (wlr_backend_is_wl(pMonitor->output->backend) || wlr_backend_is_x11(pMonitor->output->backend)) ? 0 : pMonitorRule->refreshRate * 1000;
@@ -1775,9 +1800,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
// if delta of refresh rate, w and h chosen and mode is < 1 we accept it
if (DELTALESSTHAN(mode->width, pMonitorRule->resolution.x, 1) && DELTALESSTHAN(mode->height, pMonitorRule->resolution.y, 1) &&
DELTALESSTHAN(mode->refresh / 1000.f, pMonitorRule->refreshRate, 1)) {
wlr_output_set_mode(pMonitor->output, mode);
wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
if (!wlr_output_test(pMonitor->output)) {
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f);
continue;
}
@@ -1795,11 +1820,11 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
if (!found) {
wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE);
wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE);
pMonitor->vecSize = pMonitorRule->resolution;
pMonitor->refreshRate = pMonitorRule->refreshRate;
if (!wlr_output_test(pMonitor->output)) {
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output);
@@ -1811,7 +1836,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
// Preferred is valid
wlr_output_set_mode(pMonitor->output, PREFERREDMODE);
wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE);
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name,
pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f);
@@ -1833,7 +1858,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} else {
auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &pMonitorRule->drmMode);
if (mode) {
wlr_output_set_mode(pMonitor->output, mode);
wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
pMonitor->customDrmMode = pMonitorRule->drmMode;
} else {
Debug::log(ERR, "wlr_drm_connector_add_mode failed");
@@ -1841,13 +1866,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
}
} else {
wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE);
wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE);
}
pMonitor->vecSize = pMonitorRule->resolution;
pMonitor->refreshRate = pMonitorRule->refreshRate;
if (fail || !wlr_output_test(pMonitor->output)) {
if (fail || !wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output);
@@ -1859,7 +1884,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
// Preferred is valid
wlr_output_set_mode(pMonitor->output, PREFERREDMODE);
wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE);
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name,
pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f);
@@ -1883,8 +1908,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
if (pMonitorRule->resolution == Vector2D(-1, -1)) {
wl_list_for_each(mode, &pMonitor->output->modes, link) {
if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || mode->refresh > (currentRefresh + 3000.f)) {
wlr_output_set_mode(pMonitor->output, mode);
if (wlr_output_test(pMonitor->output)) {
wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
currentWidth = mode->width;
currentHeight = mode->height;
currentRefresh = mode->refresh;
@@ -1896,8 +1921,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
wl_list_for_each(mode, &pMonitor->output->modes, link) {
if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) ||
(mode->width > currentWidth && mode->height > currentHeight)) {
wlr_output_set_mode(pMonitor->output, mode);
if (wlr_output_test(pMonitor->output)) {
wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
currentWidth = mode->width;
currentHeight = mode->height;
currentRefresh = mode->refresh;
@@ -1920,7 +1945,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
// Preferred is valid
wlr_output_set_mode(pMonitor->output, PREFERREDMODE);
wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE);
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name,
pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f);
@@ -1945,9 +1970,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
wlr_output_mode* mode;
wl_list_for_each(mode, &pMonitor->output->modes, link) {
wlr_output_set_mode(pMonitor->output, mode);
wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
if (!wlr_output_test(pMonitor->output)) {
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f);
continue;
}
@@ -1963,7 +1988,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
} else {
// Preferred is valid
wlr_output_set_mode(pMonitor->output, PREFERREDMODE);
wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE);
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
@@ -1972,7 +1997,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
}
}
pMonitor->vrrActive = pMonitor->output->pending.adaptive_sync_enabled; // disabled here, will be tested in CConfigManager::ensureVRR()
pMonitor->vrrActive = pMonitor->state.wlr()->adaptive_sync_enabled // disabled here, will be tested in CConfigManager::ensureVRR()
|| pMonitor->createdByUser; // wayland backend doesn't allow for disabling adaptive_sync
pMonitor->vecPixelSize = pMonitor->vecSize;
@@ -1989,6 +2015,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero;
if (logicalZero == logicalZero.round()) {
pMonitor->scale = scaleZero;
wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale);
} else {
for (size_t i = 1; i < 90; ++i) {
double scaleUp = (searchScale + i) / 120.0;
@@ -2026,10 +2053,14 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} else
pMonitor->scale = searchScale;
}
}
}
wlr_output_set_scale(pMonitor->output, pMonitor->scale);
// for wlroots, that likes flooring, we have to do this.
double logicalX = std::round(pMonitor->vecPixelSize.x / pMonitor->scale);
logicalX += 0.1;
wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->vecPixelSize.x / logicalX);
}
}
// clang-format off
static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{
@@ -2046,9 +2077,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->drmFormat = DRM_FORMAT_INVALID;
for (auto& fmt : formats[(int)!pMonitorRule->enable10bit]) {
wlr_output_set_render_format(pMonitor->output, fmt.second);
wlr_output_state_set_render_format(pMonitor->state.wlr(), fmt.second);
if (!wlr_output_test(pMonitor->output)) {
if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) {
Debug::log(ERR, "output {} failed basic test on format {}", pMonitor->szName, fmt.first);
} else {
Debug::log(LOG, "output {} succeeded basic test on format {}", pMonitor->szName, fmt.first);
@@ -2062,9 +2093,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->enabled10bit = set10bit;
if (!wlr_output_commit(pMonitor->output)) {
if (!pMonitor->state.commit())
Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name);
}
int x, y;
wlr_output_transformed_resolution(pMonitor->output, &x, &y);
@@ -2080,7 +2110,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->updateMatrix();
// update renderer (here because it will call rollback, so we cannot do this before committing)
if (WAS10B != pMonitor->enabled10bit || OLDRES != pMonitor->vecPixelSize)
g_pHyprOpenGL->destroyMonitorResources(pMonitor);
// updato wlroots
@@ -2088,6 +2118,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
wlr_damage_ring_set_bounds(&pMonitor->damage, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y);
// Set scale for all surfaces on this monitor, needed for some clients
// but not on unsafe state to avoid crashes
if (!g_pCompositor->m_bUnsafeState) {
for (auto& w : g_pCompositor->m_vWindows) {
w->updateSurfaceScaleTransformDetails();
}
}
// updato us
arrangeLayersForMonitor(pMonitor->ID);
@@ -2422,7 +2459,7 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode
}
if (!buffer) {
if (!wlr_output_configure_primary_swapchain(pMonitor->output, &pMonitor->output->pending, &pMonitor->output->swapchain))
if (!wlr_output_configure_primary_swapchain(pMonitor->output, pMonitor->state.wlr(), &pMonitor->output->swapchain))
return false;
m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge);
@@ -2466,7 +2503,7 @@ void CHyprRenderer::endRender() {
glFlush();
if (m_eRenderMode == RENDER_MODE_NORMAL) {
wlr_output_state_set_buffer(&PMONITOR->output->pending, m_pCurrentWlrBuffer);
wlr_output_state_set_buffer(PMONITOR->state.wlr(), m_pCurrentWlrBuffer);
unsetEGL(); // flush the context
}

View File

@@ -57,7 +57,7 @@ class CHyprRenderer {
void ensureCursorRenderingMode();
bool shouldRenderCursor();
void setCursorHidden(bool hide);
void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false);
void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false, const Vector2D& projSize = {}, bool fixMisalignedFSV1 = false);
std::tuple<float, float, float> getRenderTimes(CMonitor* pMonitor); // avg max min
void renderLockscreen(CMonitor* pMonitor, timespec* now);
void setOccludedForBackLayers(CRegion& region, CWorkspace* pWorkspace);
@@ -82,7 +82,6 @@ class CHyprRenderer {
CMonitor* m_pMostHzMonitor = nullptr;
bool m_bDirectScanoutBlocked = false;
bool m_bSoftwareCursorsLocked = false;
bool m_bTearingEnvSatisfied = false;
DAMAGETRACKINGMODES
damageTrackingModeFromStr(const std::string&);

View File

@@ -44,7 +44,7 @@ class CShader {
GLint time = -1;
GLint distort = -1;
GLint output = -1;
GLint wl_output = -1;
// Blur prepare
GLint contrast = -1;

View File

@@ -104,5 +104,5 @@ std::string CHyprBorderDecoration::getDisplayName() {
}
bool CHyprBorderDecoration::doesntWantBorders() {
return !m_pWindow->m_sSpecialRenderData.border || m_pWindow->m_bX11DoesntWantBorders;
return !m_pWindow->m_sSpecialRenderData.border || m_pWindow->m_bX11DoesntWantBorders || m_pWindow->getRealBorderSize() == 0;
}

View File

@@ -26,9 +26,10 @@ CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) : IHyprWindow
CHyprGroupBarDecoration::~CHyprGroupBarDecoration() {}
SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue;
static auto* const PHEIGHT = &g_pConfigManager->getConfigValuePtr("group:groupbar:height")->intValue;
static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue;
static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue;
SDecorationPositioningInfo info;
info.policy = DECORATION_POSITION_STICKY;
@@ -37,7 +38,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
info.reserved = true;
if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate)
info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2}, {0, 0}};
info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}};
else
info.desiredExtents = {{0, 0}, {0, 0}};
@@ -90,6 +91,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue;
static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue;
static auto* const PHEIGHT = &g_pConfigManager->getConfigValuePtr("group:groupbar:height")->intValue;
static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue;
if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate)
@@ -99,7 +101,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2;
const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2;
if (DESIREDHEIGHT != ASSIGNEDBOX.h)
g_pDecorationPositioner->repositionDeco(this);
@@ -128,12 +130,18 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
color.a *= a;
g_pHyprOpenGL->renderRect(&rect, color);
// render title if necessary
if (*PRENDERTITLES) {
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth,
rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth,
ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2};
rect.scale(pMonitor->scale);
if (*PGRADIENTS) {
const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) :
(GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive));
if (GRADIENTTEX.m_iTexID != 0)
g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0);
}
if (*PRENDERTITLES) {
CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle);
if (!pTitleTex)
@@ -142,12 +150,6 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}))
.get();
const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) :
(GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive));
if (*PGRADIENTS && GRADIENTTEX.m_iTexID != 0)
g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0);
rect.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale;
rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale;
@@ -243,7 +245,7 @@ CTitleTex::~CTitleTex() {
tex.destroyTexture();
}
void renderGradientTo(CTexture& tex, const CColor& grad) {
void renderGradientTo(CTexture& tex, CGradientValueData* grad) {
if (!g_pCompositor->m_pLastMonitor)
return;
@@ -261,8 +263,12 @@ void renderGradientTo(CTexture& tex, const CColor& grad) {
cairo_pattern_t* pattern;
pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y);
cairo_pattern_add_color_stop_rgba(pattern, 1, grad.r, grad.g, grad.b, grad.a);
cairo_pattern_add_color_stop_rgba(pattern, 0, grad.r, grad.g, grad.b, 0);
for (unsigned long i = 0; i < grad->m_vColors.size(); i++) {
cairo_pattern_add_color_stop_rgba(pattern, 1 - (double)(i + 1) / (grad->m_vColors.size() + 1), grad->m_vColors[i].r, grad->m_vColors[i].g, grad->m_vColors[i].b,
grad->m_vColors[i].a);
}
cairo_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y);
cairo_set_source(CAIRO, pattern);
cairo_fill(CAIRO);
@@ -293,10 +299,10 @@ void refreshGroupBarGradients() {
static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue;
static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue;
static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data;
static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data;
static auto* const PGROUPCOLACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data;
static auto* const PGROUPCOLINACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data;
CGradientValueData* PGROUPCOLACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data.get();
CGradientValueData* PGROUPCOLINACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data.get();
CGradientValueData* PGROUPCOLACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data.get();
CGradientValueData* PGROUPCOLINACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data.get();
g_pHyprRenderer->makeEGLCurrent();
@@ -310,13 +316,16 @@ void refreshGroupBarGradients() {
if (!*PENABLED || !*PGRADIENTS)
return;
renderGradientTo(m_tGradientActive, ((CGradientValueData*)PGROUPCOLACTIVE->get())->m_vColors[0]);
renderGradientTo(m_tGradientInactive, ((CGradientValueData*)PGROUPCOLINACTIVE->get())->m_vColors[0]);
renderGradientTo(m_tGradientLockedActive, ((CGradientValueData*)PGROUPCOLACTIVELOCKED->get())->m_vColors[0]);
renderGradientTo(m_tGradientLockedInactive, ((CGradientValueData*)PGROUPCOLINACTIVELOCKED->get())->m_vColors[0]);
renderGradientTo(m_tGradientActive, PGROUPCOLACTIVE);
renderGradientTo(m_tGradientInactive, PGROUPCOLINACTIVE);
renderGradientTo(m_tGradientLockedActive, PGROUPCOLACTIVELOCKED);
renderGradientTo(m_tGradientLockedInactive, PGROUPCOLINACTIVELOCKED);
}
bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
if (m_pWindow == m_pWindow->m_sGroupData.pNextWindow)
return false;
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);

View File

@@ -1,45 +0,0 @@
diff --git a/include/meson.build b/include/meson.build
index e669800..687786b 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -1,4 +1,5 @@
-subdir('wlr')
+run_command('ln', '-sf', join_paths(meson.project_source_root(), 'include', 'wlr'), join_paths(meson.project_source_root(), 'include', 'wlroots'), check: true)
+subdir('wlroots')
exclude_files = ['meson.build', 'config.h.in', 'version.h.in']
if not features.get('drm-backend')
@@ -24,8 +25,8 @@ if not features.get('session')
exclude_files += 'backend/session.h'
endif
-install_subdir('wlr',
- install_dir: get_option('includedir'),
+install_subdir('wlroots',
+ install_dir: join_paths(get_option('includedir'), 'hyprland'),
exclude_files: exclude_files,
)
diff --git a/include/wlr/meson.build b/include/wlr/meson.build
index f7ca413..0a86d54 100644
--- a/include/wlr/meson.build
+++ b/include/wlr/meson.build
@@ -22,4 +22,4 @@ ver_h = configure_file(
configuration: version_data,
)
-install_headers(conf_h, ver_h, subdir: 'wlr')
+install_headers(conf_h, ver_h, subdir: join_paths('hyprland', 'wlroots'))
diff --git a/meson.build b/meson.build
index 29b103a..0b6e5a4 100644
--- a/meson.build
+++ b/meson.build
@@ -15,7 +15,7 @@ project(
# necessary for bugfix releases. Increasing soversion is required because
# wlroots never guarantees ABI stability -- only API stability is guaranteed
# between minor releases.
-soversion = 13
+soversion = 13032
little_endian = target_machine.endian() == 'little'
big_endian = target_machine.endian() == 'big'

View File

@@ -1,7 +0,0 @@
[wrap-git]
directory = wlroots
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
revision = 5d639394f3e83b01596dcd166a44a9a1a2583350
depth = 1
diff_files = wlroots-meson-build.patch