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 id: ver
attributes: attributes:
label: Hyprland Version label: Hyprland Version
description: "Paste here the output of `hyprctl version`." description: "Paste here the output of `hyprctl version`. For hyprland after 0.34.0, paste `hyprctl systeminfo` instead."
placeholder: Hyprland, built from branch main at commit... value: "<details>
<summary>System/Version info</summary>
```sh
<Paste the output of the command here>
```
</details>"
validations: validations:
required: true 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 wlroots
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
SOURCE_DIR ${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 PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/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 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_COMMAND ninja -C build
BUILD_ALWAYS true BUILD_ALWAYS true
BUILD_IN_SOURCE true BUILD_IN_SOURCE true

View File

@@ -1,22 +1,22 @@
PREFIX = /usr/local PREFIX = /usr/local
legacyrenderer: 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` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build chmod -R 777 ./build
legacyrendererdebug: 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` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build chmod -R 777 ./build
release: 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` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build chmod -R 777 ./build
debug: 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` cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build chmod -R 777 ./build
@@ -42,10 +42,10 @@ install:
chmod 755 ${PREFIX}/bin/Hyprland chmod 755 ${PREFIX}/bin/Hyprland
chmod 755 ${PREFIX}/bin/hyprctl chmod 755 ${PREFIX}/bin/hyprctl
chmod 755 ${PREFIX}/bin/hyprpm 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 if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland mkdir -p ${PREFIX}/share/hyprland
cp ./assets/wall_* ${PREFIX}/share/hyprland cp ./assets/wall* ${PREFIX}/share/hyprland
mkdir -p ${PREFIX}/share/xdg-desktop-portal mkdir -p ${PREFIX}/share/xdg-desktop-portal
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal
@@ -60,6 +60,7 @@ install:
uninstall: uninstall:
rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
rm -f ${PREFIX}/bin/Hyprland rm -f ${PREFIX}/bin/Hyprland
rm -f ${PREFIX}/bin/hyprland
rm -f ${PREFIX}/bin/hyprctl rm -f ${PREFIX}/bin/hyprctl
rm -f ${PREFIX}/bin/hyprpm rm -f ${PREFIX}/bin/hyprpm
rm -f ${PREFIX}/lib/libwlroots.so.13032 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 type : wallpapers
foreach size : [2, 4, 8] install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
install_data(f'wall_@type@@size@K.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
endforeach
endforeach endforeach
install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime') 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 { misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more # 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 # Example per-device config

42
flake.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,7 @@
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
#include <format>
#include <toml++/toml.hpp> #include <toml++/toml.hpp>
@@ -78,6 +79,8 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
bool CPluginManager::addNewPluginRepo(const std::string& url) { bool CPluginManager::addNewPluginRepo(const std::string& url) {
const auto HLVER = getHyprlandVersion();
if (DataState::pluginRepoExists(url)) { if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n"; std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
return false; return false;
@@ -170,6 +173,22 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) {
message += " version " + pl.version; message += " version " + pl.version;
progress.printMessageAbove(message); 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.m_szCurrentMessage = "Verifying headers";
progress.print(); progress.print();
@@ -191,16 +210,19 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) { 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)) { 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) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n"; 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); 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.url = url;
repo.hash = repohash; repo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) { 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); DataState::addNewPluginRepo(repo);
@@ -263,8 +285,12 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
eHeadersErrors CPluginManager::headersValid() { eHeadersErrors CPluginManager::headersValid() {
const auto HLVER = getHyprlandVersion(); const auto HLVER = getHyprlandVersion();
if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc"))
return HEADERS_MISSING;
// find headers commit // 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/")) if (!headers.contains("-I/"))
return HEADERS_MISSING; return HEADERS_MISSING;
@@ -296,10 +322,6 @@ eHeadersErrors CPluginManager::headersValid() {
if (!ifs.good()) if (!ifs.good())
return HEADERS_CORRUPTED; 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>())); std::string verHeaderContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
ifs.close(); ifs.close();
@@ -314,7 +336,9 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_OK; return HEADERS_OK;
} }
bool CPluginManager::updateHeaders() { bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion(); 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); std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
} }
if (headersValid() == HEADERS_OK) { if (!force && headersValid() == HEADERS_OK) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Your headers are already up-to-date.\n"; std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Headers up to date.\n";
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
return true; return true;
} }
@@ -369,23 +390,38 @@ bool CPluginManager::updateHeaders() {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland"); 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 // le hack. Wlroots has to generate its build/include
ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build"); ret = execAndGet("cd /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.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.m_iSteps = 4; progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources"; progress.m_szCurrentMessage = "Installing sources";
progress.print(); progress.print();
progress.printMessageAbove( // progress.printMessageAbove(
std::string{Colors::YELLOW} + "!" + Colors::RESET + // 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."); // " 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) 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 // remove build files
std::filesystem::remove_all("/tmp/hyprpm/hyprland"); std::filesystem::remove_all("/tmp/hyprpm/hyprland");
@@ -397,10 +433,6 @@ bool CPluginManager::updateHeaders() {
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
auto GLOBALSTATE = DataState::getGlobalState();
GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
std::cout << "\n"; std::cout << "\n";
} else { } else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID)); 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(); const auto HLVER = getHyprlandVersion();
CProgressBar progress; CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 1; progress.m_iMaxSteps = REPOS.size() * 2 + 2;
progress.m_iSteps = 0; progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Updating repositories"; progress.m_szCurrentMessage = "Updating repositories";
progress.print(); progress.print();
@@ -528,7 +560,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) { 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)) { 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.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_iSteps++;
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
@@ -696,8 +737,13 @@ void CPluginManager::listAllPlugins() {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n"; std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n";
for (auto& p : r.plugins) { 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); bool removePluginRepo(const std::string& urlOrName);
eHeadersErrors headersValid(); eHeadersErrors headersValid();
bool updateHeaders(); bool updateHeaders(bool force = false);
bool updatePlugins(bool forceUpdateAll); bool updatePlugins(bool forceUpdateAll);
void listAllPlugins(); void listAllPlugins();

View File

@@ -1,6 +1,7 @@
#include "progress/CProgressBar.hpp" #include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp" #include "helpers/Colors.hpp"
#include "core/PluginManager.hpp" #include "core/PluginManager.hpp"
#include "core/DataState.hpp"
#include <iostream> #include <iostream>
#include <vector> #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) --notify | -n Send a hyprland notification for important events (e.g. load fail)
--help | -h Show this menu --help | -h Show this menu
--verbose | -v Enable too much logging --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; std::vector<std::string> command;
bool notify = false, verbose = false; bool notify = false, verbose = false, force = false;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) { if (ARGS[i].starts_with("-")) {
@@ -49,6 +51,9 @@ int main(int argc, char** argv, char** envp) {
notify = true; notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true; 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 { } else {
std::cerr << "Unrecognized option " << ARGS[i]; std::cerr << "Unrecognized option " << ARGS[i];
return 1; return 1;
@@ -82,9 +87,13 @@ int main(int argc, char** argv, char** envp) {
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
} else if (command[0] == "update") { } else if (command[0] == "update") {
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
bool headers = g_pPluginManager->updateHeaders(); bool headers = g_pPluginManager->updateHeaders(force);
if (headers) { 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) if (!ret1)
return 1; return 1;

View File

@@ -10,19 +10,16 @@
(builtins.substring 4 2 longDate) (builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate) (builtins.substring 6 2 longDate)
]); ]);
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in { in {
# Contains what a user is most likely to care about: # Contains what a user is most likely to care about:
# Hyprland itself, XDPH and the Share Picker. # Hyprland itself, XDPH and the Share Picker.
default = mkJoinedOverlays (with self.overlays; [ default = lib.composeManyExtensions (with self.overlays; [
hyprland-packages hyprland-packages
hyprland-extras hyprland-extras
]); ]);
# Packages for variations of Hyprland, dependencies included. # Packages for variations of Hyprland, dependencies included.
hyprland-packages = mkJoinedOverlays [ hyprland-packages = lib.composeManyExtensions [
# Dependencies # Dependencies
inputs.hyprland-protocols.overlays.default inputs.hyprland-protocols.overlays.default
self.overlays.wlroots-hyprland self.overlays.wlroots-hyprland
@@ -34,7 +31,7 @@ in {
hyprland = final.callPackage ./default.nix { hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv; stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${date}_${self.shortRev or "dirty"}"; version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
wlroots = final.wlroots-hyprland; wlroots = prev.wlroots-hyprland;
commit = self.rev or ""; commit = self.rev or "";
inherit (final) udis86 hyprland-protocols; inherit (final) udis86 hyprland-protocols;
inherit date; inherit date;
@@ -59,7 +56,7 @@ in {
# Packages for extra software recommended for usage with Hyprland, # Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility. # including forked or patched packages for compatibility.
hyprland-extras = mkJoinedOverlays [ hyprland-extras = lib.composeManyExtensions [
inputs.xdph.overlays.xdg-desktop-portal-hyprland 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 if [ "$SUB_REV" != "$CRT_REV" ]; then
echo "Updating wlroots..." echo "Updating wlroots..."
# update wlroots to submodule revision # 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 nix flake lock
echo "wlroots: $CRT_REV -> $SUB_REV" 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', wayland_protos = dependency('wayland-protocols',
version: '>=1.25', version: '>=1.32',
fallback: 'wayland-protocols', fallback: 'wayland-protocols',
default_options: ['tests=false'], default_options: ['tests=false'],
) )

View File

@@ -81,26 +81,6 @@ CCompositor::CCompositor() {
CCompositor::~CCompositor() { CCompositor::~CCompositor() {
cleanup(); 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() { void CCompositor::setRandomSplash() {
@@ -135,10 +115,10 @@ void CCompositor::initServer() {
else else
wlr_log_init(WLR_ERROR, Debug::wlrLog); 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) { 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!"); throwError("wlr_backend_autocreate() failed!");
} }
@@ -151,7 +131,7 @@ void CCompositor::initServer() {
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD); m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
if (!m_sWLRRenderer) { 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!"); 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_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); 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"); 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() { void CCompositor::cleanup() {
if (!m_sWLDisplay || m_bIsShuttingDown) if (!m_sWLDisplay || m_bIsShuttingDown)
return; return;
@@ -362,8 +395,8 @@ void CCompositor::cleanup() {
for (auto& m : m_vMonitors) { for (auto& m : m_vMonitors) {
g_pHyprOpenGL->destroyMonitorResources(m.get()); g_pHyprOpenGL->destroyMonitorResources(m.get());
wlr_output_enable(m->output, false); wlr_output_state_set_enabled(m->state.wlr(), false);
wlr_output_commit(m->output); m->state.commit();
} }
m_vMonitors.clear(); m_vMonitors.clear();
@@ -373,8 +406,31 @@ void CCompositor::cleanup() {
g_pXWaylandManager->m_sWLRXWayland = nullptr; g_pXWaylandManager->m_sWLRXWayland = nullptr;
} }
removeAllSignals();
wl_display_destroy_clients(g_pCompositor->m_sWLDisplay); 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); wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr; m_sWLDisplay = nullptr;
@@ -408,6 +464,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the ThreadManager!"); Debug::log(LOG, "Creating the ThreadManager!");
g_pThreadManager = std::make_unique<CThreadManager>(); g_pThreadManager = std::make_unique<CThreadManager>();
Debug::log(LOG, "Creating CHyprCtl");
g_pHyprCtl = std::make_unique<CHyprCtl>();
Debug::log(LOG, "Creating the InputManager!"); Debug::log(LOG, "Creating the InputManager!");
g_pInputManager = std::make_unique<CInputManager>(); g_pInputManager = std::make_unique<CInputManager>();
@@ -629,72 +688,22 @@ bool CCompositor::windowExists(CWindow* pWindow) {
return false; return false;
} }
CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) { CWindow* CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, CWindow* pIgnoreWindow) {
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) {
const auto PMONITOR = getMonitorFromVector(pos); const auto PMONITOR = getMonitorFromVector(pos);
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue; 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 PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->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; const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
// pinned windows on top of floating regardless // pinned windows on top of floating regardless
for (auto& w : m_vWindows | std::views::reverse) { if (properties & ALLOW_FLOATING) {
const auto BB = w->getWindowInputBox();
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}))
return w.get();
if (!w->m_bIsX11) {
if (w->hasPopupAt(pos))
return w.get();
}
}
}
auto windowForWorkspace = [&](bool special) -> CWindow* {
// 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) { for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowBoxUnified(properties);
if (special && !isWorkspaceSpecial(w->m_iWorkspaceID)) // because special floating may creep up into regular
continue;
const auto BB = w->getWindowInputBox();
CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; 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) { if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_bNoFocus && w.get() != pIgnoreWindow) {
// OR windows should add focus to parent if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}))
if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2)
continue;
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) {
if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) {
// Override Redirect
return g_pCompositor->m_pLastWindow; // we kinda trick everything here.
// TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases.
}
return w.get(); return w.get();
}
if (!w->m_bIsX11) { if (!w->m_bIsX11) {
if (w->hasPopupAt(pos)) if (w->hasPopupAt(pos))
@@ -702,6 +711,44 @@ 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->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
if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2)
continue;
if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) {
if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) {
// Override Redirect
return g_pCompositor->m_pLastWindow; // we kinda trick everything here.
// TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases.
}
return w.get();
}
if (!w->m_bIsX11) {
if (w->hasPopupAt(pos))
return w.get();
}
}
}
}
if (properties & FLOATING_ONLY)
return nullptr;
const int64_t WORKSPACEID = special ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace; const int64_t WORKSPACEID = special ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
const auto PWORKSPACE = getWorkspaceByID(WORKSPACEID); const auto PWORKSPACE = getWorkspaceByID(WORKSPACEID);
@@ -725,7 +772,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
if (special != isWorkspaceSpecial(w->m_iWorkspaceID)) if (special != isWorkspaceSpecial(w->m_iWorkspaceID))
continue; 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 && 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) w.get() != pIgnoreWindow)
return w.get(); return w.get();
@@ -735,68 +782,17 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW
}; };
// special workspace // special workspace
if (PMONITOR->specialWorkspaceID) if (PMONITOR->specialWorkspaceID && !*PSPECIALFALLTHRU)
return windowForWorkspace(true); return windowForWorkspace(true);
return windowForWorkspace(false);
}
CWindow* CCompositor::windowFromCursor() {
const auto PMONITOR = getMonitorFromCursor();
if (PMONITOR->specialWorkspaceID) { if (PMONITOR->specialWorkspaceID) {
for (auto& w : m_vWindows | std::views::reverse) { const auto PWINDOW = windowForWorkspace(true);
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();
}
for (auto& w : m_vWindows) { if (PWINDOW)
CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y}; return PWINDOW;
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && !w->m_bNoFocus)
return w.get();
}
} }
// pinned return windowForWorkspace(false);
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;
} }
wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pWindow, Vector2D& sl) { wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pWindow, Vector2D& sl) {
@@ -876,13 +872,19 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; 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) { if (g_pCompositor->m_sSeat.exclusiveClient) {
Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer."); Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer.");
return; 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); g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow);
if (!pWindow || !windowValidMapped(pWindow)) { if (!pWindow || !windowValidMapped(pWindow)) {
@@ -928,12 +930,13 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
if (pWindow->m_bPinned) if (pWindow->m_bPinned)
pWindow->m_iWorkspaceID = m_pLastMonitor->activeWorkspace; pWindow->m_iWorkspaceID = m_pLastMonitor->activeWorkspace;
const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID);
if (!isWorkspaceVisible(pWindow->m_iWorkspaceID)) { if (!isWorkspaceVisible(pWindow->m_iWorkspaceID)) {
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
// This is to fix incorrect feedback on the focus history. // This is to fix incorrect feedback on the focus history.
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
PWORKSPACE->m_pLastFocusedWindow = pWindow; PWORKSPACE->m_pLastFocusedWindow = pWindow;
PWORKSPACE->rememberPrevWorkspace(getWorkspaceByID(m_pLastMonitor->activeWorkspace)); PWORKSPACE->rememberPrevWorkspace(getWorkspaceByID(m_pLastMonitor->activeWorkspace));
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
PMONITOR->changeWorkspace(PWORKSPACE, false, true); PMONITOR->changeWorkspace(PWORKSPACE, false, true);
// changeworkspace already calls focusWindow // changeworkspace already calls focusWindow
return; return;
@@ -942,6 +945,11 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
const auto PLASTWINDOW = m_pLastWindow; const auto PLASTWINDOW = m_pLastWindow;
m_pLastWindow = pWindow; 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 // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
if (windowValidMapped(PLASTWINDOW)) { if (windowValidMapped(PLASTWINDOW)) {
PLASTWINDOW->updateDynamicRules(); 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())) 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. return; // Don't focus when already focused on this.
if (g_pSessionLockManager->isSessionLocked()) { if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface))
wlr_seat_keyboard_clear_focus(m_sSeat.seat); return;
m_pLastFocus = nullptr;
}
// Unfocus last surface if should // Unfocus last surface if should
if (m_pLastFocus && !pWindowOwner) 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); 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 (SURFACEAT) {
if (!pixman_region32_not_empty(&SURFACEAT->input_region)) if (!pixman_region32_not_empty(&SURFACEAT->input_region))
continue; continue;
@@ -1879,15 +1877,13 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
if (pWindow == m_pLastWindow) { if (pWindow == m_pLastWindow) {
const auto* const ACTIVECOLOR = const auto* const ACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ? setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying().m_vColors.empty() ? *ACTIVECOLOR :
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) : pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying());
*ACTIVECOLOR);
} else { } else {
const auto* const INACTIVECOLOR = const auto* const INACTIVECOLOR =
!pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ? setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying().m_vColors.empty() ? *INACTIVECOLOR :
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) : pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying());
*INACTIVECOLOR);
} }
} }
@@ -2111,7 +2107,7 @@ CMonitor* CCompositor::getMonitorFromString(const std::string& name) {
return nullptr; 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. // We trust the workspace and monitor to be correct.
@@ -2197,7 +2193,8 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
pWorkspace->startAnim(true, true, true); pWorkspace->startAnim(true, true, true);
wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2); 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(); g_pInputManager->sendMotionEventsToFocused();
} }
@@ -2284,7 +2281,6 @@ void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState()); g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState());
pWindow->updateDynamicRules();
updateWindowAnimatedDecorationValues(pWindow); updateWindowAnimatedDecorationValues(pWindow);
// make all windows on the same workspace under the fullscreen window // make all windows on the same workspace under the fullscreen window
@@ -2606,11 +2602,7 @@ int CCompositor::getNewSpecialID() {
} }
void CCompositor::performUserChecks() { void CCompositor::performUserChecks() {
if (g_pConfigManager->getInt("general:allow_tearing") == 1 && !envEnabled("WLR_DRM_NO_ATOMIC")) { ; // intentional
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);
}
} }
void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) { void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) {
@@ -2785,10 +2777,27 @@ void CCompositor::leaveUnsafeState() {
void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) { void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) {
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(pSurface, scale); g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(pSurface, scale);
wlr_surface_set_preferred_buffer_scale(pSurface, static_cast<int32_t>(std::ceil(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) { void CCompositor::setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform) {
wlr_surface_set_preferred_buffer_transform(pSurface, 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() { void CCompositor::updateSuspendedStates() {

View File

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

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "helpers/Vector2D.hpp" #include "helpers/Vector2D.hpp"
#include <functional>
enum eIcons { enum eIcons {
ICON_WARNING = 0, ICON_WARNING = 0,
@@ -52,4 +53,20 @@ struct SWindowDecorationExtents {
bool operator==(const SWindowDecorationExtents& other) const { bool operator==(const SWindowDecorationExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight; 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}; return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
} }
CBox CWindow::getWindowInputBox() { CBox CWindow::getWindowBoxUnified(uint64_t properties) {
const int BORDERSIZE = getRealBorderSize();
if (m_sAdditionalConfigData.dimAround) { if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; 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) return box;
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;
} }
CBox CWindow::getWindowMainSurfaceBox() { CBox CWindow::getWindowMainSurfaceBox() {
@@ -318,7 +308,7 @@ void CWindow::destroyToplevelHandle() {
} }
void CWindow::updateToplevel() { void CWindow::updateToplevel() {
updateSurfaceOutputs(); updateSurfaceScaleTransformDetails();
if (!m_phForeignToplevel) if (!m_phForeignToplevel)
return; return;
@@ -345,8 +335,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) {
wlr_surface_send_leave(pSurface, OUTPUT); wlr_surface_send_leave(pSurface, OUTPUT);
} }
void CWindow::updateSurfaceOutputs() { void CWindow::updateSurfaceScaleTransformDetails() {
if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden) if (!m_bIsMapped || m_bHidden)
return; return;
const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID); const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID);
@@ -355,16 +345,23 @@ void CWindow::updateSurfaceOutputs() {
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) if (PNEWMONITOR != PLASTMONITOR) {
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); 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(), sendEnterIter, PNEWMONITOR->output);
}
wlr_surface_for_each_surface( wlr_surface_for_each_surface(
m_pWLSurface.wlr(), m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) { [](wlr_surface* surf, int x, int y, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID); 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); g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform);
}, },
this); this);
@@ -511,6 +508,14 @@ void CWindow::onMap() {
"CWindow"); "CWindow");
m_vReportedSize = m_vPendingReportedSize; 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) { void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -607,14 +612,42 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.animationStyle = STYLE; m_sAdditionalConfigData.animationStyle = STYLE;
} else if (r.szRule.starts_with("bordercolor")) { } else if (r.szRule.starts_with("bordercolor")) {
try { 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(' ')) { // Basic form has only two colors, everything else can be parsed as a gradient
// we have a space, 2 values if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' '))); m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0])));
m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1)); m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1])));
} else { return;
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart); }
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()); } } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "dimaround") { } else if (r.szRule == "dimaround") {
@@ -644,8 +677,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} }
void CWindow::updateDynamicRules() { void CWindow::updateDynamicRules() {
m_sSpecialRenderData.activeBorderColor = -1; m_sSpecialRenderData.activeBorderColor = CGradientValueData();
m_sSpecialRenderData.inactiveBorderColor = -1; m_sSpecialRenderData.inactiveBorderColor = CGradientValueData();
m_sSpecialRenderData.alpha = 1.f; m_sSpecialRenderData.alpha = 1.f;
m_sSpecialRenderData.alphaInactive = -1.f; m_sSpecialRenderData.alphaInactive = -1.f;
m_sAdditionalConfigData.forceNoBlur = false; m_sAdditionalConfigData.forceNoBlur = false;
@@ -1026,7 +1059,7 @@ int CWindow::getRealBorderSize() {
} }
bool CWindow::canBeTorn() { bool CWindow::canBeTorn() {
return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied; return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint);
} }
bool CWindow::shouldSendFullscreenState() { bool CWindow::shouldSendFullscreenState() {

View File

@@ -30,6 +30,16 @@ enum eGroupRules {
GROUP_OVERRIDE = 1 << 6, // Override other rules 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; class IWindowTransformer;
template <typename T> template <typename T>
@@ -106,13 +116,13 @@ class CWindowOverridableVar {
}; };
struct SWindowSpecialRenderData { struct SWindowSpecialRenderData {
CWindowOverridableVar<bool> alphaOverride = false; CWindowOverridableVar<bool> alphaOverride = false;
CWindowOverridableVar<float> alpha = 1.f; CWindowOverridableVar<float> alpha = 1.f;
CWindowOverridableVar<bool> alphaInactiveOverride = false; CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<int64_t> activeBorderColor = -1; // -1 means unset CWindowOverridableVar<CGradientValueData> activeBorderColor = CGradientValueData(); // empty color vector means unset
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset CWindowOverridableVar<CGradientValueData> inactiveBorderColor = CGradientValueData(); // empty color vector means unset
// set by the layout // set by the layout
CWindowOverridableVar<int> borderSize = -1; // -1 means unset CWindowOverridableVar<int> borderSize = -1; // -1 means unset
@@ -342,7 +352,7 @@ class CWindow {
// methods // methods
CBox getFullWindowBoundingBox(); CBox getFullWindowBoundingBox();
SWindowDecorationExtents getFullWindowExtents(); SWindowDecorationExtents getFullWindowExtents();
CBox getWindowInputBox(); CBox getWindowBoxUnified(uint64_t props);
CBox getWindowMainSurfaceBox(); CBox getWindowMainSurfaceBox();
CBox getWindowIdealBoundingBoxIgnoreReserved(); CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco); void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
@@ -356,7 +366,7 @@ class CWindow {
void createToplevelHandle(); void createToplevelHandle();
void destroyToplevelHandle(); void destroyToplevelHandle();
void updateToplevel(); void updateToplevel();
void updateSurfaceOutputs(); void updateSurfaceScaleTransformDetails();
void moveToWorkspace(int); void moveToWorkspace(int);
CWindow* X11TransientFor(); CWindow* X11TransientFor();
void onUnmap(); void onUnmap();

View File

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

View File

@@ -119,7 +119,7 @@ gestures {
misc { misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more # 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 # Example per-device config

View File

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

View File

@@ -7,6 +7,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h>
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@@ -28,7 +29,7 @@ static std::string getWorkspaceNameFromSpecialID(const int workspaceID) {
return workspace->m_szName; 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, ' '); CVarList vars(request, 0, ' ');
auto allMonitors = false; auto allMonitors = false;
@@ -39,7 +40,7 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f
allMonitors = true; allMonitors = true;
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { 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; return result;
} }
static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) { static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) {
const bool isJson = format == HyprCtl::FORMAT_JSON; const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
if (!w->m_sGroupData.pNextWindow) if (!w->m_sGroupData.pNextWindow)
return isJson ? "" : "0"; return isJson ? "" : "0";
@@ -133,7 +134,7 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
return result.str(); 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 { auto getFocusHistoryID = [](CWindow* wnd) -> int {
for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) { for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) {
if (g_pCompositor->m_vWindowFocusHistory[i] == wnd) if (g_pCompositor->m_vWindowFocusHistory[i] == wnd)
@@ -142,7 +143,7 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
return -1; return -1;
}; };
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format( return std::format(
R"#({{ R"#({{
"address": "0x{:x}", "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 = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {
@@ -218,10 +219,10 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; 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 PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#({{ return std::format(R"#({{
"id": {}, "id": {},
"name": "{}", "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"; }; 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 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 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)) : ""; 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; return result;
} }
} }
std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
if (!g_pCompositor->m_pLastMonitor) if (!g_pCompositor->m_pLastMonitor)
return "unsafe state"; return "unsafe state";
@@ -293,10 +294,10 @@ std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
return getWorkspaceData(w, format); return getWorkspaceData(w, format);
} }
std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format); result += getWorkspaceData(w.get(), format);
@@ -314,9 +315,9 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) { for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format); result += getWorkspaceRuleData(r, format);
@@ -334,24 +335,24 @@ std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_pLastWindow; const auto PWINDOW = g_pCompositor->m_pLastWindow;
if (!g_pCompositor->windowValidMapped(PWINDOW)) if (!g_pCompositor->windowValidMapped(PWINDOW))
return format == HyprCtl::FORMAT_JSON ? "{}" : "Invalid"; return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
auto result = getWindowData(PWINDOW, format); auto result = getWindowData(PWINDOW, format);
if (format == HyprCtl::FORMAT_JSON) if (format == eHyprCtlOutputFormat::FORMAT_JSON)
result.pop_back(); result.pop_back();
return result; return result;
} }
std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n"; result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) { for (auto& mon : g_pCompositor->m_vMonitors) {
@@ -422,9 +423,9 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) { for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
@@ -444,10 +445,10 @@ std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n"; result += "{\n";
result += "\"mice\": [\n"; result += "\"mice\": [\n";
@@ -603,9 +604,9 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = ""; std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
ret += "animations:\n"; ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) { for (auto& ac : g_pConfigManager->getAnimationConfig()) {
@@ -656,10 +657,10 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret; return ret;
} }
std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[\n\"log\":\""; result += "[\n\"log\":\"";
result += escapeJSONStrings(Debug::rollingLog); result += escapeJSONStrings(Debug::rollingLog);
result += "\"]"; result += "\"]";
@@ -670,10 +671,10 @@ std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = ""; std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts(); const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS) for (auto& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description); ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else { } else {
@@ -693,9 +694,9 @@ std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret; return ret;
} }
std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = ""; std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& kb : g_pKeybindManager->m_lKeybinds) { for (auto& kb : g_pKeybindManager->m_lKeybinds) {
ret += "bind"; ret += "bind";
if (kb.locked) if (kb.locked)
@@ -741,12 +742,12 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret; return ret;
} }
std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE); auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' '); 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 + std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n"; ").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
@@ -793,7 +794,40 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ""; // make the compiler happy 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 // get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1); in = in.substr(in.find_first_of(' ') + 1);
@@ -814,7 +848,7 @@ std::string dispatchRequest(std::string in) {
return "ok"; return "ok";
} }
std::string dispatchKeyword(std::string in) { std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// get rid of the keyword keyword // get rid of the keyword keyword
in = in.substr(in.find_first_of(' ') + 1); in = in.substr(in.find_first_of(' ') + 1);
@@ -862,7 +896,7 @@ std::string dispatchKeyword(std::string in) {
return retval; 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); const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
@@ -877,20 +911,20 @@ std::string reloadRequest(const std::string& request) {
return "ok"; return "ok";
} }
std::string killRequest() { std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
g_pInputManager->setClickMode(CLICKMODE_KILL); g_pInputManager->setClickMode(CLICKMODE_KILL);
return "ok"; return "ok";
} }
std::string splashRequest() { std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
return g_pCompositor->m_szCurrentSplash; 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(); 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); return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y);
} else { } else {
return std::format(R"#( return std::format(R"#(
@@ -905,9 +939,7 @@ std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) {
return "error"; return "error";
} }
std::string getReply(std::string); std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
std::string dispatchBatch(std::string request) {
// split by ; // split by ;
request = request.substr(9); request = request.substr(9);
@@ -931,7 +963,7 @@ std::string dispatchBatch(std::string request) {
nextItem(); nextItem();
while (curitem != "") { while (curitem != "") {
reply += getReply(curitem); reply += g_pHyprCtl->getReply(curitem);
nextItem(); nextItem();
} }
@@ -939,7 +971,7 @@ std::string dispatchBatch(std::string request) {
return reply; return reply;
} }
std::string dispatchSetCursor(std::string request) { std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
const auto SIZESTR = vars[vars.size() - 1]; const auto SIZESTR = vars[vars.size() - 1];
@@ -971,7 +1003,7 @@ std::string dispatchSetCursor(std::string request) {
return "ok"; return "ok";
} }
std::string switchXKBLayoutRequest(const std::string& request) { std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
const auto KB = vars[1]; const auto KB = vars[1];
@@ -1017,7 +1049,7 @@ std::string switchXKBLayoutRequest(const std::string& request) {
return "ok"; return "ok";
} }
std::string dispatchSeterror(std::string request) { std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
std::string errorMessage = ""; std::string errorMessage = "";
@@ -1046,7 +1078,7 @@ std::string dispatchSeterror(std::string request) {
return "ok"; return "ok";
} }
std::string dispatchSetProp(std::string request) { std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
if (vars.size() < 4) if (vars.size() < 4)
@@ -1104,9 +1136,9 @@ std::string dispatchSetProp(std::string request) {
} else if (PROP == "alphainactive") { } else if (PROP == "alphainactive") {
PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock); PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor") { } 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") { } 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") { } else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") { } else if (PROP == "bordersize") {
@@ -1130,7 +1162,7 @@ std::string dispatchSetProp(std::string request) {
return "ok"; return "ok";
} }
std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat format) { std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = ""; std::string curitem = "";
auto nextItem = [&]() { auto nextItem = [&]() {
@@ -1155,7 +1187,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
if (!PCFGOPT) if (!PCFGOPT)
return "no such option"; 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, 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); (uintptr_t)PCFGOPT->data.get(), PCFGOPT->set);
else { 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, ' '); CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
@@ -1182,7 +1214,7 @@ std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat
return "none"; return "none";
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& wd : PWINDOW->m_dWindowDecorations) { for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},"; 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 = ""; std::string curitem = "";
auto nextItem = [&]() { auto nextItem = [&]() {
@@ -1280,7 +1312,7 @@ std::string dispatchOutput(std::string request) {
return "ok"; return "ok";
} }
std::string dispatchPlugin(std::string request) { std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
if (vars.size() < 2) if (vars.size() < 2)
@@ -1323,7 +1355,7 @@ std::string dispatchPlugin(std::string request) {
return "ok"; return "ok";
} }
std::string dispatchNotify(std::string request) { std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
if (vars.size() < 5) if (vars.size() < 5)
@@ -1364,92 +1396,100 @@ std::string dispatchNotify(std::string request) {
return "ok"; return "ok";
} }
std::string getReply(std::string request) { CHyprCtl::CHyprCtl() {
auto format = HyprCtl::FORMAT_NORMAL; 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 // process flags for non-batch requests
if (!request.contains("[[BATCH]]") && request.contains("/")) { if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
long unsigned int sepIndex = 0; long unsigned int sepIndex = 0;
for (const auto& c : request) { for (const auto& c : request) {
if (c == '/') { // stop at separator if (c == '/') { // stop at separator
break; 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++; sepIndex++;
if (c == 'j') if (c == 'j')
format = HyprCtl::FORMAT_JSON; format = eHyprCtlOutputFormat::FORMAT_JSON;
} }
if (sepIndex < request.size()) if (sepIndex < request.size())
request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
} }
if (request.starts_with("monitors")) // parse exact cmds first, then non-exact.
return monitorsRequest(request, format); for (auto& cmd : m_vCommands) {
else if (request == "workspaces") if (!cmd->exact)
return workspacesRequest(format); continue;
else if (request == "workspacerules")
return workspaceRulesRequest(format); if (cmd->name == request)
else if (request == "activeworkspace") return cmd->fn(format, request);
return activeWorkspaceRequest(format); }
else if (request == "clients")
return clientsRequest(format); for (auto& cmd : m_vCommands) {
else if (request == "kill") if (cmd->exact)
return killRequest(); continue;
else if (request == "activewindow")
return activeWindowRequest(format); if (request.starts_with(cmd->name))
else if (request == "layers") return cmd->fn(format, request);
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);
return "unknown request"; return "unknown request";
} }
std::string HyprCtl::makeDynamicCall(const std::string& input) { std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input); return getReply(input);
} }
@@ -1460,7 +1500,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
sockaddr_in clientAddress; sockaddr_in clientAddress;
socklen_t clientSize = sizeof(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; std::array<char, 1024> readBuffer;
@@ -1490,7 +1530,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
std::string reply = ""; std::string reply = "";
try { try {
reply = getReply(request); reply = g_pHyprCtl->getReply(request);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Error in request: {}", e.what()); Debug::log(ERR, "Error in request: {}", e.what());
reply = "Err: " + std::string(e.what()); reply = "Err: " + std::string(e.what());
@@ -1500,18 +1540,17 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
close(ACCEPTEDCONNECTION); close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_bWantsMonitorReload) { if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->ensureMonitorStatus(); g_pConfigManager->ensureMonitorStatus();
}
return 0; 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."); Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return; return;
} }
@@ -1522,15 +1561,15 @@ void HyprCtl::startHyprCtlSocket() {
strcpy(SERVERADDRESS.sun_path, socketPath.c_str()); 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."); Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
return; return;
} }
// 10 max queued. // 10 max queued.
listen(iSocketFD, 10); listen(m_iSocketFD, 10);
Debug::log(LOG, "Hypr socket started at {}", socketPath); 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 "../Compositor.hpp"
#include <fstream> #include <fstream>
#include "../helpers/MiscFunctions.hpp" #include "../helpers/MiscFunctions.hpp"
#include <functional>
namespace HyprCtl { class CHyprCtl {
void startHyprCtlSocket(); public:
std::string makeDynamicCall(const std::string& input); CHyprCtl();
// very simple thread-safe request method std::string makeDynamicCall(const std::string& input);
inline bool requestMade = false; std::shared_ptr<SHyprCtlCommand> registerCommand(SHyprCtlCommand cmd);
inline bool requestReady = false; void unregisterCommand(const std::shared_ptr<SHyprCtlCommand>& cmd);
inline std::string request = ""; std::string getReply(std::string);
inline std::ifstream requestStream; int m_iSocketFD = -1;
inline wl_event_source* hyprCtlTickSource = nullptr; private:
void startHyprCtlSocket();
inline int iSocketFD = -1; std::vector<std::shared_ptr<SHyprCtlCommand>> m_vCommands;
};
enum eHyprCtlOutputFormat { inline std::unique_ptr<CHyprCtl> g_pHyprCtl;
FORMAT_NORMAL = 0,
FORMAT_JSON
};
};

View File

@@ -142,6 +142,9 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output); 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 && const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained // don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint); (!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 : "")}); g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", layersurface); 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) { if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");

View File

@@ -175,11 +175,17 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) { void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) {
Debug::log(LOG, "PowerMgr set mode!"); Debug::log(LOG, "PowerMgr set mode!");
const auto EVENT = (wlr_output_power_v1_set_mode_event*)data; 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"); 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) { void Events::listener_newTearingHint(wl_listener* listener, void* data) {
const auto TCTL = (wlr_tearing_control_v1*)data; const auto TCTL = (wlr_tearing_control_v1*)data;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TCTL->surface); Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data);
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);
const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get(); const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get();
NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data; 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->hyprListener_destroy.initCallback(
&NEWCTRL->pWlrHint->events.destroy, &NEWCTRL->pWlrHint->events.destroy,
[&](void* owner, void* data) { [&](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; }); 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); const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface);
if (PWINDOW) { 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"); NEWCTRL, "TearingController");

View File

@@ -109,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) { if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1; 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) { void Events::listener_monitorStateRequest(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner; //const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_request_state*)data; //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) { void Events::listener_monitorDamage(void* owner, void* data) {

View File

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

View File

@@ -1,4 +1,8 @@
#include "Box.hpp" #include "Box.hpp"
#include <limits>
#include <algorithm>
wlr_box CBox::wlr() { wlr_box CBox::wlr() {
CBox rounded = roundInternal(); CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h}; 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; 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() { CBox CBox::roundInternal() {
float newW = x + w - std::floor(x); float newW = x + w - std::floor(x);
float newH = y + h - std::floor(y); 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& transform(const wl_output_transform t, double w, double h);
CBox& addExtents(const SWindowDecorationExtents& e); CBox& addExtents(const SWindowDecorationExtents& e);
CBox& expand(const double& value); CBox& expand(const double& value);
CBox& noNegativeSize();
CBox copy() const; CBox copy() const;
@@ -73,6 +74,8 @@ class CBox {
double height; double height;
}; };
double rot = 0; /* rad, ccw */
// //
bool operator==(const CBox& rhs) const { bool operator==(const CBox& rhs) const {
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; 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); 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) { void handleNoop(struct wl_listener* listener, void* data) {
// Do nothing // Do nothing
} }

View File

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

View File

@@ -8,7 +8,7 @@ int ratHandler(void* data) {
return 1; return 1;
} }
CMonitor::CMonitor() { CMonitor::CMonitor() : state(this) {
wlr_damage_ring_init(&damage); 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 tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
if (m_bEnabled) { if (m_bEnabled) {
wlr_output_enable(output, 1); wlr_output_state_set_enabled(state.wlr(), true);
wlr_output_commit(output); state.commit();
return; return;
} }
@@ -63,8 +63,8 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore // if it's disabled, disable and ignore
if (monitorRule.disabled) { if (monitorRule.disabled) {
wlr_output_set_scale(output, 1); wlr_output_state_set_scale(state.wlr(), 1);
wlr_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL); wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL);
auto PREFSTATE = wlr_output_preferred_mode(output); auto PREFSTATE = wlr_output_preferred_mode(output);
@@ -72,9 +72,9 @@ void CMonitor::onConnect(bool noRule) {
wlr_output_mode* mode; wlr_output_mode* mode;
wl_list_for_each(mode, &output->modes, link) { 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; continue;
PREFSTATE = mode; PREFSTATE = mode;
@@ -83,13 +83,13 @@ void CMonitor::onConnect(bool noRule) {
} }
if (PREFSTATE) if (PREFSTATE)
wlr_output_set_mode(output, PREFSTATE); wlr_output_state_set_mode(state.wlr(), PREFSTATE);
else else
Debug::log(WARN, "No mode found for disabled output {}", output->name); 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); Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false; m_bEnabled = false;
@@ -130,13 +130,14 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true; m_bEnabled = true;
wlr_output_enable(output, 1); wlr_output_state_set_enabled(state.wlr(), 1);
// set mode, also applies // set mode, also applies
if (!noRule) if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); 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); wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
@@ -283,9 +284,10 @@ void CMonitor::onDisconnect(bool destroy) {
if (!destroy) if (!destroy)
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output); 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) if (g_pCompositor->m_pLastMonitor == this)
g_pCompositor->setActiveMonitor(BACKUPMON); g_pCompositor->setActiveMonitor(BACKUPMON);
@@ -502,7 +504,7 @@ float CMonitor::getDefaultScale() {
return 1; 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) if (!pWorkspace)
return; 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; static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
CWindow* pWindow = pWorkspace->getLastFocusedWindow(); CWindow* pWindow = pWorkspace->getLastFocusedWindow();
if (!pWindow) { if (!pWindow) {
if (*PFOLLOWMOUSE == 1) 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) if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID); pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
@@ -621,7 +623,7 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) { if (w->m_iWorkspaceID == pWorkspace->m_iID) {
w->m_iMonitorID = ID; w->m_iMonitorID = ID;
w->updateSurfaceOutputs(); w->updateSurfaceScaleTransformDetails();
const auto MIDDLE = w->middle(); 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) { 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); 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; 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 { class CMonitor {
public: public:
CMonitor(); CMonitor();
@@ -51,6 +70,8 @@ class CMonitor {
drmModeModeInfo customDrmMode = {}; drmModeModeInfo customDrmMode = {};
CMonitorState state;
// WLR stuff // WLR stuff
wlr_damage_ring damage; wlr_damage_ring damage;
wlr_output* output = nullptr; wlr_output* output = nullptr;
@@ -120,7 +141,7 @@ class CMonitor {
void setMirror(const std::string&); void setMirror(const std::string&);
bool isMirror(); bool isMirror();
float getDefaultScale(); 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 changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace); void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id); 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.", "Compile, wait for 20 minutes, notice a new commit, compile again.",
"To rice, or not to rice, that is the question.", "To rice, or not to rice, that is the question.",
"Now available on Fedora!", "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 // music reference / quote section
"J'remue le ciel, le jour, la nuit.", "J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!", "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); Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface);
subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner); 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) { 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 // tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(pNode->pWindowOwner->m_iMonitorID); 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) { pNode->pSurface->wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
CRegion damageBox; CRegion damageBox{&pNode->pSurface->wlr()->buffer_damage};
wlr_surface_get_effective_damage(pNode->pSurface->wlr(), damageBox.pixman());
if (!damageBox.empty()) { if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) { if (PMONITOR->tearingState.busy) {
PMONITOR->tearingState.frameScheduledWhileBusy = true; PMONITOR->tearingState.frameScheduledWhileBusy = true;
} else { } else {

View File

@@ -42,14 +42,10 @@ double Vector2D::distance(const Vector2D& other) const {
return std::sqrt(dx * dx + dy * dy); 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 { double Vector2D::size() const {
return std::sqrt(x * x + y * y); 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 floor() const;
Vector2D round() 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,13 +7,15 @@
void handleWrapped(wl_listener* listener, void* data) { void handleWrapped(wl_listener* listener, void* data) {
CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener);
g_pWatchdog->startWatching(); if (g_pWatchdog)
g_pWatchdog->startWatching();
try { try {
pWrap->m_pSelf->emit(data); pWrap->m_pSelf->emit(data);
} catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); } } catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); }
g_pWatchdog->endWatching(); if (g_pWatchdog)
g_pWatchdog->endWatching();
} }
CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function<void(void*, void*)> callback, void* pOwner) { CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function<void(void*, void*)> callback, void* pOwner) {

View File

@@ -28,8 +28,14 @@ class CWLSurface {
bool m_bFillIgnoreSmall = false; bool m_bFillIgnoreSmall = false;
// if present, means this is a base surface of a window. Cleaned on unassign() // if present, means this is a base surface of a window. Cleaned on unassign()
CWindow* m_pOwner = nullptr; 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) { CWLSurface& operator=(wlr_surface* pSurface) {
destroy(); destroy();
m_pWLRSurface = pSurface; m_pWLRSurface = pSurface;

View File

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

View File

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

View File

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

View File

@@ -85,7 +85,7 @@ int main(int argc, char** argv) {
} }
if (!ignoreSudo && Init::isSudo()) { 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"; std::cerr << " Hint: Use the --i-am-really-stupid flag to omit that check.\n";
return 1; return 1;

View File

@@ -7,6 +7,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include <vector>
#if defined(__linux__) #if defined(__linux__)
#include <linux/vt.h> #include <linux/vt.h>
#elif defined(__NetBSD__) || defined(__OpenBSD__) #elif defined(__NetBSD__) || defined(__OpenBSD__)
@@ -18,64 +19,65 @@
CKeybindManager::CKeybindManager() { CKeybindManager::CKeybindManager() {
// initialize all dispatchers // initialize all dispatchers
m_mDispatchers["exec"] = spawn; m_mDispatchers["exec"] = spawn;
m_mDispatchers["execr"] = spawnRaw; m_mDispatchers["execr"] = spawnRaw;
m_mDispatchers["killactive"] = killActive; m_mDispatchers["killactive"] = killActive;
m_mDispatchers["closewindow"] = kill; m_mDispatchers["closewindow"] = kill;
m_mDispatchers["togglefloating"] = toggleActiveFloating; m_mDispatchers["togglefloating"] = toggleActiveFloating;
m_mDispatchers["workspace"] = changeworkspace; m_mDispatchers["workspace"] = changeworkspace;
m_mDispatchers["renameworkspace"] = renameWorkspace; m_mDispatchers["renameworkspace"] = renameWorkspace;
m_mDispatchers["fullscreen"] = fullscreenActive; m_mDispatchers["fullscreen"] = fullscreenActive;
m_mDispatchers["fakefullscreen"] = fakeFullscreenActive; m_mDispatchers["fakefullscreen"] = fakeFullscreenActive;
m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace;
m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent;
m_mDispatchers["pseudo"] = toggleActivePseudo; m_mDispatchers["pseudo"] = toggleActivePseudo;
m_mDispatchers["movefocus"] = moveFocusTo; m_mDispatchers["movefocus"] = moveFocusTo;
m_mDispatchers["movewindow"] = moveActiveTo; m_mDispatchers["movewindow"] = moveActiveTo;
m_mDispatchers["swapwindow"] = swapActive; m_mDispatchers["swapwindow"] = swapActive;
m_mDispatchers["centerwindow"] = centerWindow; m_mDispatchers["centerwindow"] = centerWindow;
m_mDispatchers["togglegroup"] = toggleGroup; m_mDispatchers["togglegroup"] = toggleGroup;
m_mDispatchers["changegroupactive"] = changeGroupActive; m_mDispatchers["changegroupactive"] = changeGroupActive;
m_mDispatchers["movegroupwindow"] = moveGroupWindow; m_mDispatchers["movegroupwindow"] = moveGroupWindow;
m_mDispatchers["togglesplit"] = toggleSplit; m_mDispatchers["togglesplit"] = toggleSplit;
m_mDispatchers["splitratio"] = alterSplitRatio; m_mDispatchers["splitratio"] = alterSplitRatio;
m_mDispatchers["focusmonitor"] = focusMonitor; m_mDispatchers["focusmonitor"] = focusMonitor;
m_mDispatchers["movecursortocorner"] = moveCursorToCorner; m_mDispatchers["movecursortocorner"] = moveCursorToCorner;
m_mDispatchers["movecursor"] = moveCursor; m_mDispatchers["movecursor"] = moveCursor;
m_mDispatchers["workspaceopt"] = workspaceOpt; m_mDispatchers["workspaceopt"] = workspaceOpt;
m_mDispatchers["exit"] = exitHyprland; m_mDispatchers["exit"] = exitHyprland;
m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor;
m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; m_mDispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor;
m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor;
m_mDispatchers["forcerendererreload"] = forceRendererReload; m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace;
m_mDispatchers["resizeactive"] = resizeActive; m_mDispatchers["forcerendererreload"] = forceRendererReload;
m_mDispatchers["moveactive"] = moveActive; m_mDispatchers["resizeactive"] = resizeActive;
m_mDispatchers["cyclenext"] = circleNext; m_mDispatchers["moveactive"] = moveActive;
m_mDispatchers["focuswindowbyclass"] = focusWindow; m_mDispatchers["cyclenext"] = circleNext;
m_mDispatchers["focuswindow"] = focusWindow; m_mDispatchers["focuswindowbyclass"] = focusWindow;
m_mDispatchers["submap"] = setSubmap; m_mDispatchers["focuswindow"] = focusWindow;
m_mDispatchers["pass"] = pass; m_mDispatchers["submap"] = setSubmap;
m_mDispatchers["layoutmsg"] = layoutmsg; m_mDispatchers["pass"] = pass;
m_mDispatchers["toggleopaque"] = toggleOpaque; m_mDispatchers["layoutmsg"] = layoutmsg;
m_mDispatchers["dpms"] = dpms; m_mDispatchers["toggleopaque"] = toggleOpaque;
m_mDispatchers["movewindowpixel"] = moveWindow; m_mDispatchers["dpms"] = dpms;
m_mDispatchers["resizewindowpixel"] = resizeWindow; m_mDispatchers["movewindowpixel"] = moveWindow;
m_mDispatchers["swapnext"] = swapnext; m_mDispatchers["resizewindowpixel"] = resizeWindow;
m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; m_mDispatchers["swapnext"] = swapnext;
m_mDispatchers["pin"] = pinActive; m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces;
m_mDispatchers["mouse"] = mouse; m_mDispatchers["pin"] = pinActive;
m_mDispatchers["bringactivetotop"] = bringActiveToTop; m_mDispatchers["mouse"] = mouse;
m_mDispatchers["alterzorder"] = alterZOrder; m_mDispatchers["bringactivetotop"] = bringActiveToTop;
m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; m_mDispatchers["alterzorder"] = alterZOrder;
m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast;
m_mDispatchers["lockgroups"] = lockGroups; m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast;
m_mDispatchers["lockactivegroup"] = lockActiveGroup; m_mDispatchers["lockgroups"] = lockGroups;
m_mDispatchers["moveintogroup"] = moveIntoGroup; m_mDispatchers["lockactivegroup"] = lockActiveGroup;
m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; m_mDispatchers["moveintogroup"] = moveIntoGroup;
m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; m_mDispatchers["moveoutofgroup"] = moveOutOfGroup;
m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup;
m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock;
m_mDispatchers["global"] = global; m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup;
m_mDispatchers["global"] = global;
m_tScrollTimer.reset(); m_tScrollTimer.reset();
@@ -95,7 +97,7 @@ void CKeybindManager::addKeybind(SKeybind kb) {
void CKeybindManager::removeKeybind(uint32_t mod, const std::string& key) { void CKeybindManager::removeKeybind(uint32_t mod, const std::string& key) {
for (auto it = m_lKeybinds.begin(); it != m_lKeybinds.end(); ++it) { for (auto it = m_lKeybinds.begin(); it != m_lKeybinds.end(); ++it) {
if (isNumber(key) && std::stoi(key) > 9) { 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) { if (it->modmask == mod && it->keycode == KEYNUM) {
it = m_lKeybinds.erase(it); it = m_lKeybinds.erase(it);
@@ -137,6 +139,22 @@ uint32_t CKeybindManager::stringToModMask(std::string mods) {
return modMask; 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() { void CKeybindManager::updateXKBTranslationState() {
if (m_pXKBTranslationState) { if (m_pXKBTranslationState) {
xkb_keymap_unref(xkb_state_get_keymap(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) { bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard) {
if (!g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { if (!g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
m_dPressedKeycodes.clear(); m_dPressedKeys.clear();
m_dPressedKeysyms.clear();
return true; return true;
} }
@@ -291,9 +308,16 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
m_uLastCode = KEYCODE; m_uLastCode = KEYCODE;
m_uLastMouseCode = 0; m_uLastMouseCode = 0;
bool mouseBindWasActive = ensureMouseBindState(); 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) { if (e->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
// clean repeat // clean repeat
if (m_pActiveKeybindEventSource) { if (m_pActiveKeybindEventSource) {
@@ -302,16 +326,15 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
m_pActiveKeybind = nullptr; m_pActiveKeybind = nullptr;
} }
m_dPressedKeycodes.push_back(KEYCODE); m_dPressedKeys.push_back(KEY);
m_dPressedKeysyms.push_back(keysym);
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 (suppressEvent)
if (found)
shadowKeybinds(keysym, KEYCODE); shadowKeybinds(keysym, KEYCODE);
} else if (e->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
m_dPressedKeys.back().sent = !suppressEvent;
} else { // key release
// clean repeat // clean repeat
if (m_pActiveKeybindEventSource) { if (m_pActiveKeybindEventSource) {
wl_event_source_remove(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_pActiveKeybind = nullptr;
} }
m_dPressedKeycodes.erase(std::remove(m_dPressedKeycodes.begin(), m_dPressedKeycodes.end(), KEYCODE), m_dPressedKeycodes.end()); bool foundInPressedKeys = false;
m_dPressedKeysyms.erase(std::remove(m_dPressedKeysyms.begin(), m_dPressedKeysyms.end(), keysym), m_dPressedKeysyms.end()); for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) {
if (it->keycode == KEYCODE) {
found = handleKeybinds(MODS, "", keysym, 0, false, e->time_msec) || found; suppressEvent = handleKeybinds(MODS, *it, false);
foundInPressedKeys = true;
found = handleKeybinds(MODS, "", 0, KEYCODE, false, e->time_msec) || found; 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(); shadowKeybinds();
} }
return !found && !mouseBindWasActive; return !suppressEvent && !mouseBindWasActive;
} }
bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
@@ -347,14 +380,14 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
bool found = false; bool found = false;
if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_VERTICAL) { if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_VERTICAL) {
if (e->delta < 0) if (e->delta < 0)
found = handleKeybinds(MODS, "mouse_down", 0, 0, true, 0); found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_down"}, true);
else 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) { } else if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) {
if (e->delta < 0) if (e->delta < 0)
found = handleKeybinds(MODS, "mouse_left", 0, 0, true, 0); found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_left"}, true);
else else
found = handleKeybinds(MODS, "mouse_right", 0, 0, true, 0); found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_right"}, true);
} }
if (found) if (found)
@@ -366,26 +399,52 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) {
bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) { bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) {
const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); const auto MODS = g_pInputManager->accumulateModsFromAllKBs();
bool found = false; bool suppressEvent = false;
m_uLastMouseCode = e->button; m_uLastMouseCode = e->button;
m_uLastCode = 0; m_uLastCode = 0;
m_uTimeLastMs = e->time_msec; m_uTimeLastMs = e->time_msec;
bool mouseBindWasActive = ensureMouseBindState(); bool mouseBindWasActive = ensureMouseBindState();
const auto KEY_NAME = "mouse:" + std::to_string(e->button);
const auto KEY = SPressedKeyWithMods{
.keyName = KEY_NAME,
.modmaskAtPressTime = MODS,
};
if (e->state == WLR_BUTTON_PRESSED) { if (e->state == WLR_BUTTON_PRESSED) {
found = handleKeybinds(MODS, "mouse:" + std::to_string(e->button), 0, 0, true, 0); m_dPressedKeys.push_back(KEY);
if (found) suppressEvent = handleKeybinds(MODS, KEY, true);
if (suppressEvent)
shadowKeybinds(); shadowKeybinds();
m_dPressedKeys.back().sent = !suppressEvent;
} else { } 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(); shadowKeybinds();
} }
return !found && !mouseBindWasActive; return !suppressEvent && !mouseBindWasActive;
} }
void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) { 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) { 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) { 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) { 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) { int repeatKeyHandler(void* data) {
@@ -424,7 +483,7 @@ int repeatKeyHandler(void* data) {
return 0; 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; bool found = false;
if (g_pCompositor->m_sSeat.exclusiveClient) 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)) ((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
continue; continue;
if (!key.empty()) { if (!key.keyName.empty()) {
if (key != k.key) if (key.keyName != k.key)
continue; continue;
} else if (k.keycode != -1) { } else if (k.keycode != 0) {
if (keycode != k.keycode) if (key.keycode != k.keycode)
continue; continue;
} else { } else {
if (keysym == 0)
continue; // this is a keycode check run
// oMg such performance hit!!11! // oMg such performance hit!!11!
// this little maneouver is gonna cost us 4µs // 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 KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
if (keysym != KBKEY && keysym != KBKEYUPPER) if (key.keysym != KBKEY && key.keysym != KBKEYUPPER)
continue; continue;
} }
@@ -468,12 +524,24 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
continue; continue;
} }
if (!pressed && !k.release && !SPECIALDISPATCHER) { if (!pressed) {
if (k.nonConsuming) // Require mods to be matching when the key was first pressed.
continue; 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;
found = true; // suppress the event } else if (!k.release && !SPECIALDISPATCHER) {
continue; if (k.nonConsuming)
continue;
found = true; // suppress the event
continue;
}
} }
const auto DISPATCHER = m_mDispatchers.find(k.mouse ? "mouse" : k.handler); 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); Debug::log(ERR, "Invalid handler in a keybind! (handler {} does not exist)", k.handler);
} else { } else {
// call the dispatcher // 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; m_iPassPressed = (int)pressed;
@@ -521,7 +589,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
return found; 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 // shadow disables keybinds after one has been triggered
for (auto& k : m_lKeybinds) { 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 KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
for (auto& pk : m_dPressedKeysyms) { for (auto& pk : m_dPressedKeys) {
if ((pk == KBKEY || pk == KBKEYUPPER)) { if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
shadow = true; shadow = true;
if (pk == doesntHave && doesntHave != 0) { if (pk.keysym == doesntHave && doesntHave != 0) {
shadow = false; shadow = false;
break; break;
} }
} }
}
for (auto& pk : m_dPressedKeycodes) { if (pk.keycode != 0 && pk.keycode == k.keycode) {
if (pk == k.keycode) {
shadow = true; shadow = true;
if (pk == doesntHaveCode && doesntHaveCode != 0 && doesntHaveCode != -1) { if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
shadow = false; shadow = false;
break; break;
} }
@@ -1000,10 +1066,12 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
} }
if (const auto PATCOORDS = g_pCompositor->vectorToWindowIdeal(OLDMIDDLE); PATCOORDS && PATCOORDS != PWINDOW) if (PWINDOW == g_pCompositor->m_pLastWindow) {
g_pCompositor->focusWindow(PATCOORDS); if (const auto PATCOORDS = g_pCompositor->vectorToWindowUnified(OLDMIDDLE, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING, PWINDOW); PATCOORDS)
else g_pCompositor->focusWindow(PATCOORDS);
g_pInputManager->refocus(); else
g_pInputManager->refocus();
}
} }
void CKeybindManager::moveFocusTo(std::string args) { void CKeybindManager::moveFocusTo(std::string args) {
@@ -1385,7 +1453,7 @@ void CKeybindManager::renameWorkspace(std::string args) {
} }
void CKeybindManager::exitHyprland(std::string argz) { void CKeybindManager::exitHyprland(std::string argz) {
g_pCompositor->cleanup(); g_pInputManager->m_bExitTriggered = true;
} }
void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) { void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) {
@@ -1435,6 +1503,48 @@ void CKeybindManager::moveWorkspaceToMonitor(std::string args) {
g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PMONITOR); 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) { void CKeybindManager::toggleSpecialWorkspace(std::string args) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; 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) if (!port.empty() && m->szName != port)
continue; continue;
wlr_output_enable(m->output, enable); wlr_output_state_set_enabled(m->state.wlr(), enable);
m->dpmsStatus = enable; m->dpmsStatus = enable;
if (!wlr_output_commit(m->output)) { if (!m->state.commit()) {
Debug::log(ERR, "Couldn't commit output {}", m->szName); 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); 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) { void CKeybindManager::mouse(std::string args) {
@@ -1841,7 +1951,7 @@ void CKeybindManager::mouse(std::string args) {
g_pKeybindManager->m_bIsMouseBindActive = true; g_pKeybindManager->m_bIsMouseBindActive = true;
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal(); 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) if (pWindow && !pWindow->m_bIsFullscreen)
pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_START, mouseCoords); pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_START, mouseCoords);
@@ -1864,7 +1974,8 @@ void CKeybindManager::mouse(std::string args) {
if (PRESSED) { if (PRESSED) {
g_pKeybindManager->m_bIsMouseBindActive = true; 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 { try {
switch (std::stoi(ARGS[1])) { switch (std::stoi(ARGS[1])) {

View File

@@ -12,7 +12,7 @@ class CPluginSystem;
struct SKeybind { struct SKeybind {
std::string key = ""; std::string key = "";
int keycode = -1; uint32_t keycode = 0;
uint32_t modmask = 0; uint32_t modmask = 0;
std::string handler = ""; std::string handler = "";
std::string arg = ""; std::string arg = "";
@@ -36,6 +36,14 @@ enum eFocusWindowMode {
MODE_PID 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 { class CKeybindManager {
public: public:
CKeybindManager(); CKeybindManager();
@@ -51,8 +59,9 @@ class CKeybindManager {
void addKeybind(SKeybind); void addKeybind(SKeybind);
void removeKeybind(uint32_t, const std::string&); void removeKeybind(uint32_t, const std::string&);
uint32_t stringToModMask(std::string); uint32_t stringToModMask(std::string);
uint32_t keycodeToModifier(xkb_keycode_t);
void clearKeybinds(); 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; std::unordered_map<std::string, std::function<void(std::string)>> m_mDispatchers;
@@ -63,38 +72,37 @@ class CKeybindManager {
std::list<SKeybind> m_lKeybinds; std::list<SKeybind> m_lKeybinds;
private: private:
std::deque<xkb_keysym_t> m_dPressedKeysyms; std::deque<SPressedKeyWithMods> m_dPressedKeys;
std::deque<int> m_dPressedKeycodes;
inline static std::string m_szCurrentSelectedSubmap = ""; inline static std::string m_szCurrentSelectedSubmap = "";
SKeybind* m_pActiveKeybind = nullptr; SKeybind* m_pActiveKeybind = nullptr;
uint32_t m_uTimeLastMs = 0; uint32_t m_uTimeLastMs = 0;
uint32_t m_uLastCode = 0; uint32_t m_uLastCode = 0;
uint32_t m_uLastMouseCode = 0; uint32_t m_uLastMouseCode = 0;
bool m_bIsMouseBindActive = false; bool m_bIsMouseBindActive = false;
std::vector<SKeybind*> m_vPressedSpecialBinds; std::vector<SKeybind*> m_vPressedSpecialBinds;
int m_iPassPressed = -1; // used for pass int m_iPassPressed = -1; // used for pass
CTimer m_tScrollTimer; 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 handleInternalKeybinds(xkb_keysym_t);
bool handleVT(xkb_keysym_t); bool handleVT(xkb_keysym_t);
xkb_state* m_pXKBTranslationState = nullptr; xkb_state* m_pXKBTranslationState = nullptr;
void updateXKBTranslationState(); void updateXKBTranslationState();
bool ensureMouseBindState(); bool ensureMouseBindState();
static bool tryMoveFocusToMonitor(CMonitor* monitor); static bool tryMoveFocusToMonitor(CMonitor* monitor);
static void moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir = ""); static void moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir = "");
static void moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDirection); static void moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDirection);
static void switchToWindow(CWindow* PWINDOWTOCHANGETO); static void switchToWindow(CWindow* PWINDOWTOCHANGETO);
// -------------- Dispatchers -------------- // // -------------- Dispatchers -------------- //
static void killActive(std::string); static void killActive(std::string);
@@ -126,6 +134,7 @@ class CKeybindManager {
static void exitHyprland(std::string); static void exitHyprland(std::string);
static void moveCurrentWorkspaceToMonitor(std::string); static void moveCurrentWorkspaceToMonitor(std::string);
static void moveWorkspaceToMonitor(std::string); static void moveWorkspaceToMonitor(std::string);
static void focusWorkspaceOnCurrentMonitor(std::string);
static void toggleSpecialWorkspace(std::string); static void toggleSpecialWorkspace(std::string);
static void forceRendererReload(std::string); static void forceRendererReload(std::string);
static void resizeActive(std::string); static void resizeActive(std::string);

View File

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

View File

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

View File

@@ -142,25 +142,28 @@ class CInputManager {
// Switches // Switches
std::list<SSwitchDevice> m_lSwitches; std::list<SSwitchDevice> m_lSwitches;
void newTabletTool(wlr_input_device*); // Exclusive layer surfaces
void newTabletPad(wlr_input_device*); std::deque<SLayerSurface*> m_dExclusiveLSes;
void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(wlr_idle_inhibitor_v1*);
void recheckIdleInhibitorStatus();
void onSwipeBegin(wlr_pointer_swipe_begin_event*); void newTabletTool(wlr_input_device*);
void onSwipeEnd(wlr_pointer_swipe_end_event*); void newTabletPad(wlr_input_device*);
void onSwipeUpdate(wlr_pointer_swipe_update_event*); void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(wlr_idle_inhibitor_v1*);
void recheckIdleInhibitorStatus();
SSwipeGesture m_sActiveSwipe; void onSwipeBegin(wlr_pointer_swipe_begin_event*);
void onSwipeEnd(wlr_pointer_swipe_end_event*);
void onSwipeUpdate(wlr_pointer_swipe_update_event*);
SKeyboard* m_pActiveKeyboard = nullptr; SSwipeGesture m_sActiveSwipe;
CTimer m_tmrLastCursorMovement; SKeyboard* m_pActiveKeyboard = nullptr;
CInputMethodRelay m_sIMERelay; CTimer m_tmrLastCursorMovement;
void updateKeyboardsLeds(wlr_input_device* pKeyboard); CInputMethodRelay m_sIMERelay;
void updateKeyboardsLeds(wlr_input_device* pKeyboard);
// for shared mods // for shared mods
uint32_t accumulateModsFromAllKBs(); uint32_t accumulateModsFromAllKBs();
@@ -243,6 +246,8 @@ class CInputManager {
void restoreCursorIconToApp(); // no-op if restored void restoreCursorIconToApp(); // no-op if restored
bool m_bExitTriggered = false; // for exit dispatcher
friend class CKeybindManager; friend class CKeybindManager;
friend class CWLSurface; friend class CWLSurface;
}; };

View File

@@ -265,7 +265,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) {
PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_bForceRendering = true;
PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); 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); const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (PWORKSPACER) { if (PWORKSPACER) {
@@ -305,7 +305,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) {
PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_bForceRendering = true;
PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); 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); const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (PWORKSPACEL) { if (PWORKSPACEL) {

View File

@@ -74,9 +74,10 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr
std::string code = probe.assembly.substr(lastAsmNewline, probe.assembly.find("\n", lastAsmNewline) - lastAsmNewline); std::string code = probe.assembly.substr(lastAsmNewline, probe.assembly.find("\n", lastAsmNewline) - lastAsmNewline);
if (code.contains("%rip")) { if (code.contains("%rip")) {
CVarList tokens{code, 0, 's'}; CVarList tokens{code, 0, 's'};
size_t plusPresent = tokens[1][0] == '+' ? 1 : 0; size_t plusPresent = tokens[1][0] == '+' ? 1 : 0;
std::string addr = tokens[1].substr(plusPresent, tokens[1].find("(%rip)") - plusPresent); size_t minusPresent = tokens[1][0] == '-' ? 1 : 0;
const uint64_t OFFSET = configStringToInt(addr); 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) if (OFFSET == 0)
return {}; return {};
const uint64_t DESTINATION = currentAddress + OFFSET + len; const uint64_t DESTINATION = currentAddress + OFFSET + len;
@@ -190,9 +191,9 @@ bool CFunctionHook::hook() {
(uint64_t)((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS)); (uint64_t)((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS));
// make jump to hk // make jump to hk
const auto PAGESIZE = sysconf(_SC_PAGE_SIZE); const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE);
const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE); 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) * PAGESIZE; 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); mprotect((uint8_t*)PROTSTART, PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC);
memcpy((uint8_t*)m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); 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) { APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format) {
if (args.empty()) if (args.empty())
return HyprCtl::makeDynamicCall(format + "/" + call); return g_pHyprCtl->makeDynamicCall(format + "/" + call);
else 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) { APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) {
@@ -362,4 +362,28 @@ APICALL SVersionInfo HyprlandAPI::getHyprlandVersion(HANDLE handle) {
return {}; return {};
return {GIT_COMMIT_HASH, GIT_TAG, GIT_DIRTY != std::string(""), GIT_BRANCH, GIT_COMMIT_MESSAGE}; 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. for a different hash.
*/ */
APICALL SVersionInfo getHyprlandVersion(HANDLE handle); 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) for (auto& d : rdi)
HyprlandAPI::removeDispatcher(plugin->m_pHandle, d); 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); g_pConfigManager->removePluginConfig(plugin->m_pHandle);
dlclose(plugin->m_pHandle); dlclose(plugin->m_pHandle);

View File

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

View File

@@ -189,16 +189,39 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) {
void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* fb) { void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* fb) {
m_RenderData.pMonitor = pMonitor; 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"); TRACY_GPU_ZONE("RenderBegin");
glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
matrixProjection(m_RenderData.projection, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, WL_OUTPUT_TRANSFORM_NORMAL); 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]; m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor];
if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
initShaders();
// ensure a framebuffer for the monitor exists // 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->stencilTex.allocate();
m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; 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->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->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->offMainFB.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()) if (m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_RenderData.pMonitor->mirrors.empty())
m_RenderData.pCurrentMonData->monitorMirrorFB.release(); m_RenderData.pCurrentMonData->monitorMirrorFB.release();
if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
initShaders();
m_RenderData.damage.set(*pDamage); m_RenderData.damage.set(*pDamage);
m_bFakeFrame = fb; m_bFakeFrame = fb;
@@ -232,7 +250,8 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer*
const auto PRBO = g_pHyprRenderer->getCurrentRBO(); const auto PRBO = g_pHyprRenderer->getCurrentRBO();
const bool FBPROPERSIZE = fb && fb->m_vSize == pMonitor->vecPixelSize; 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 // we have to offload
// bind the offload Hypr Framebuffer // bind the offload Hypr Framebuffer
m_RenderData.pCurrentMonData->offloadFB.bind(); m_RenderData.pCurrentMonData->offloadFB.bind();
@@ -307,9 +326,19 @@ void CHyprOpenGLImpl::end() {
} }
// reset our data // reset our data
m_RenderData.pMonitor = nullptr; m_RenderData.pMonitor = nullptr;
m_RenderData.mouseZoomFactor = 1.f; m_RenderData.mouseZoomFactor = 1.f;
m_RenderData.mouseZoomUseMouse = true; 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() { void CHyprOpenGLImpl::initShaders() {
@@ -497,10 +526,10 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) {
return; return;
} }
m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj"); m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj");
m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex"); m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex");
m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time"); 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) { if (m_sFinalScreenShader.time != -1 && g_pConfigManager->getInt("debug:damage_tracking") != 0 && !g_pHyprRenderer->m_bCrashingInProgress) {
// The screen shader uses the "time" uniform // The screen shader uses the "time" uniform
// Since the screen shader could change every frame, damage tracking *needs* to be disabled // 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"); TRACY_GPU_ZONE("RenderRectWithDamage");
CBox newBox = *box; CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); m_RenderData.renderModif.applyToBox(newBox);
box = &newBox; box = &newBox;
float matrix[9]; 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 m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9]; float glMatrix[9];
@@ -719,18 +748,18 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
alpha = std::clamp(alpha, 0.f, 1.f); alpha = std::clamp(alpha, 0.f, 1.f);
if (m_RenderData.damage.empty()) if (damage->empty())
return; return;
CBox newBox = *pBox; 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; static auto* const PDIMINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue;
// get transform // get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9]; 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]; float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -792,8 +821,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
glUniform1f(shader->time, 0.f); glUniform1f(shader->time, 0.f);
} }
if (usingFinalShader && shader->output != -1) if (usingFinalShader && shader->wl_output != -1)
glUniform1i(shader->output, m_RenderData.pMonitor->ID); glUniform1i(shader->wl_output, m_RenderData.pMonitor->ID);
if (CRASHING) { if (CRASHING) {
glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort); glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort);
@@ -886,12 +915,12 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) {
return; return;
CBox newBox = *pBox; CBox newBox = *pBox;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); m_RenderData.renderModif.applyToBox(newBox);
// get transform // get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9]; 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]; float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -940,12 +969,12 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame
return; return;
CBox newBox = *pBox; CBox newBox = *pBox;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); m_RenderData.renderModif.applyToBox(newBox);
// get transform // get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9]; 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]; float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -1475,14 +1504,14 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
return; return;
CBox newBox = *box; CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); m_RenderData.renderModif.applyToBox(newBox);
box = &newBox; box = &newBox;
if (borderSize < 1) if (borderSize < 1)
return; 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 // adjust box
box->x -= scaledBorderSize; box->x -= scaledBorderSize;
@@ -1493,7 +1522,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
round += round == 0 ? 0 : scaledBorderSize; round += round == 0 ? 0 : scaledBorderSize;
float matrix[9]; 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 m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9]; float glMatrix[9];
@@ -1779,7 +1808,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
TRACY_GPU_ZONE("RenderShadow"); TRACY_GPU_ZONE("RenderShadow");
CBox newBox = *box; CBox newBox = *box;
newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); m_RenderData.renderModif.applyToBox(newBox);
box = &newBox; box = &newBox;
@@ -1790,7 +1819,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
const auto col = color; const auto col = color;
float matrix[9]; 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 m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9]; float glMatrix[9];
@@ -1876,10 +1905,10 @@ void CHyprOpenGLImpl::renderMirrored() {
renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false); 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); 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_font_size(CAIRO, FONTSIZE);
cairo_set_source_rgba(CAIRO, 1.0, 1.0, 1.0, 0.32); 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_t textExtents;
cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &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()); cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str());
@@ -1899,109 +1928,123 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) {
static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; 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 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; 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)); const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast<int64_t>(-1L), static_cast<int64_t>(2L));
static std::string texPath = "";
if (*PRENDERTEX) if (*PRENDERTEX)
return; return;
// release the last tex if exists // release the last tex if exists
const auto PTEX = &m_mMonitorBGTextures[pMonitor]; const auto PFB = &m_mMonitorBGFBs[pMonitor];
PTEX->destroyTexture(); PFB->release();
PTEX->allocate(); PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
Debug::log(LOG, "Allocated texture for BGTex"); Debug::log(LOG, "Allocated texture for BGTex");
// TODO: use relative paths to the installation // TODO: use relative paths to the installation
// or configure the paths at build time // or configure the paths at build time
std::string texPath = "/usr/share/hyprland/wall_"; if (texPath.empty()) {
std::string prefixes[] = {"", "anime_", "anime2_"}; texPath = "/usr/share/hyprland/wall";
// get the adequate tex // get the adequate tex
if (FORCEWALLPAPER == -1) { if (FORCEWALLPAPER == -1) {
std::random_device dev; std::mt19937_64 engine(time(nullptr));
std::mt19937 engine(dev()); std::uniform_int_distribution<> distribution(0, 2);
std::uniform_int_distribution<> distribution(0, 2);
std::uniform_int_distribution<> distribution_anime(1, 2);
if (PFORCEHYPRCHAN) texPath += std::to_string(distribution(engine));
texPath += prefixes[distribution_anime(engine)]; } else
else texPath += std::to_string(std::clamp(*PFORCEWALLPAPER, (int64_t)0, (int64_t)2));
texPath += prefixes[distribution(engine)];
} else
texPath += prefixes[FORCEWALLPAPER];
Vector2D textureSize; texPath += ".png";
if (pMonitor->vecTransformedSize.x > 3850) {
textureSize = Vector2D(7680, 4320); // check if wallpapers exist
texPath += "8K.png"; if (!std::filesystem::exists(texPath)) {
} else if (pMonitor->vecTransformedSize.x > 1930) { // try local
textureSize = Vector2D(3840, 2160); texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5);
texPath += "4K.png";
} else { if (!std::filesystem::exists(texPath))
textureSize = Vector2D(1920, 1080); return; // the texture will be empty, oh well. We'll clear with a solid color anyways.
texPath += "2K.png"; }
} }
// check if wallpapers exist // create a new one with cairo
if (!std::filesystem::exists(texPath)) { CTexture tex;
// try local
texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5);
if (!std::filesystem::exists(texPath)) const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str());
return; // the texture will be empty, oh well. We'll clear with a solid color anyways. const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE);
}
PTEX->m_vSize = textureSize; tex.allocate();
const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
// calc the target box // calc the target box
const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; 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; Vector2D origin;
double scale; double scale;
if (MONRATIO > WPRATIO) { 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 { } 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; const auto CAIROSURFACE = cairo_image_surface_create(CAIROFORMAT, scaledSize.x, scaledSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
// create a new one with cairo cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD);
const auto CAIROSURFACE = cairo_image_surface_create_from_png(texPath.c_str()); cairo_scale(CAIRO, scale, scale);
const auto CAIRO = cairo_create(CAIROSURFACE); cairo_rectangle(CAIRO, 0, 0, 100, 100);
cairo_set_source_surface(CAIRO, CAIROISURFACE, 0, 0);
cairo_paint(CAIRO);
// scale it to fit the current monitor
cairo_scale(CAIRO, textureSize.x / pMonitor->vecTransformedSize.x, textureSize.y / pMonitor->vecTransformedSize.y);
// render splash on wallpaper
if (!*PNOSPLASH) 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 // copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
glBindTexture(GL_TEXTURE_2D, PTEX->m_iTexID); 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, tex.m_iTexID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#ifndef GLES2 #ifndef GLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
}
#endif #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(CAIROSURFACE);
cairo_surface_destroy(CAIROISURFACE);
cairo_destroy(CAIRO); 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); Debug::log(LOG, "Background created for monitor {}", pMonitor->szName);
} }
@@ -2010,15 +2053,19 @@ void CHyprOpenGLImpl::clearWithTex() {
TRACY_GPU_ZONE("RenderClearWithTex"); 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); createBGTextureForMonitor(m_RenderData.pMonitor);
TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor); TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor);
} }
if (TEXIT != m_mMonitorBGTextures.end()) if (TEXIT != m_mMonitorBGFBs.end()) {
renderTexturePrimitive(TEXIT->second, &m_mMonitorRenderResources[m_RenderData.pMonitor].backgroundTexBox); 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) { void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
@@ -2036,10 +2083,10 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT); g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT);
} }
auto TEXIT = g_pHyprOpenGL->m_mMonitorBGTextures.find(pMonitor); auto TEXIT = g_pHyprOpenGL->m_mMonitorBGFBs.find(pMonitor);
if (TEXIT != g_pHyprOpenGL->m_mMonitorBGTextures.end()) { if (TEXIT != g_pHyprOpenGL->m_mMonitorBGFBs.end()) {
TEXIT->second.destroyTexture(); TEXIT->second.release();
g_pHyprOpenGL->m_mMonitorBGTextures.erase(TEXIT); g_pHyprOpenGL->m_mMonitorBGFBs.erase(TEXIT);
} }
Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName); Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName);
@@ -2210,4 +2257,26 @@ const SGLPixelFormat* CHyprOpenGLImpl::getPixelFormatFromDRM(uint32_t drmFormat)
} }
return nullptr; 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 { struct SRenderModifData {
Vector2D translate = {}; enum eRenderModifType {
float scale = 1.f; 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 { struct SGLPixelFormat {
@@ -62,8 +71,6 @@ struct SMonitorRenderData {
bool blurFBDirty = true; bool blurFBDirty = true;
bool blurFBShouldRender = false; bool blurFBShouldRender = false;
CBox backgroundTexBox;
// Shaders // Shaders
bool m_bShadersInitialized = false; bool m_bShadersInitialized = false;
CShader m_shQUAD; CShader m_shQUAD;
@@ -99,6 +106,7 @@ struct SCurrentRenderData {
float mouseZoomFactor = 1.f; float mouseZoomFactor = 1.f;
bool mouseZoomUseMouse = true; // true by default bool mouseZoomUseMouse = true; // true by default
bool useNearestNeighbor = false; bool useNearestNeighbor = false;
bool forceIntrospection = false; // cleaned in ::end()
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
@@ -181,7 +189,7 @@ class CHyprOpenGLImpl {
std::unordered_map<CWindow*, CFramebuffer> m_mWindowFramebuffers; std::unordered_map<CWindow*, CFramebuffer> m_mWindowFramebuffers;
std::unordered_map<SLayerSurface*, CFramebuffer> m_mLayerFramebuffers; std::unordered_map<SLayerSurface*, CFramebuffer> m_mLayerFramebuffers;
std::unordered_map<CMonitor*, SMonitorRenderData> m_mMonitorRenderResources; std::unordered_map<CMonitor*, SMonitorRenderData> m_mMonitorRenderResources;
std::unordered_map<CMonitor*, CTexture> m_mMonitorBGTextures; std::unordered_map<CMonitor*, CFramebuffer> m_mMonitorBGFBs;
struct { struct {
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; 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, 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); bool allowCustomUV = false, bool allowDim = false);
void renderTexturePrimitive(const CTexture& tex, CBox* pBox); 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(); void preBlurForCurrentMonitor();

View File

@@ -9,8 +9,6 @@ extern "C" {
} }
CHyprRenderer::CHyprRenderer() { CHyprRenderer::CHyprRenderer() {
if (envEnabled("WLR_DRM_NO_ATOMIC"))
m_bTearingEnvSatisfied = true;
if (g_pCompositor->m_sWLRSession) { if (g_pCompositor->m_sWLRSession) {
wlr_device* dev; 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) if (windowBox.width <= 1 || windowBox.height <= 1)
return; // invisible return; // invisible
g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface);
windowBox.scale(RDATA->pMonitor->scale); windowBox.scale(RDATA->pMonitor->scale);
windowBox.round(); 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 // check for fractional scale surfaces misaligning the buffer size
// in those cases it's better to just force nearest neighbor // 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; const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor;
if (std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && if (MISALIGNEDFSV1)
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 */)
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
float rounding = RDATA->rounding; 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) { if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
wlr_surface_send_frame_done(surface, RDATA->when); 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); g_pHyprOpenGL->blend(true);
@@ -289,11 +291,14 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
continue; continue;
} }
if (w->m_iWorkspaceID != pMonitor->activeWorkspace || !w->m_bIsFullscreen) if (!w->m_bIsFullscreen)
continue; continue;
renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL); renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL);
if (w->m_iWorkspaceID != pWorkspace->m_iID)
continue;
pWorkspaceWindow = w.get(); pWorkspaceWindow = w.get();
} }
@@ -452,7 +457,7 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec*
// clip box for animated offsets // clip box for animated offsets
const Vector2D PREOFFSETPOS = {renderdata.x, renderdata.y}; 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; Vector2D offset;
if (PWORKSPACE->m_vRenderOffset.vec().x != 0) { if (PWORKSPACE->m_vRenderOffset.vec().x != 0) {
@@ -652,13 +657,17 @@ void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMon
} }
void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* time, const Vector2D& translate, const float& scale) { void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* time, const Vector2D& translate, const float& scale) {
static auto* const PDIMSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:dim_special")->floatValue; static auto* const PDIMSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:dim_special")->floatValue;
static auto* const PBLURSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:blur:special")->intValue; static auto* const PBLURSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:blur:special")->intValue;
static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue;
static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue;
static auto* const PBACKGROUNDCOLOR = &g_pConfigManager->getConfigValuePtr("misc:background_color")->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) if (!pMonitor)
return; 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) { if (!pWindow || !pWindow->m_bIsX11) {
Vector2D uvTL; Vector2D uvTL;
Vector2D uvBR = Vector2D(1, 1); 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.primarySurfaceUVTopLeft = uvTL;
g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR; g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR;
@@ -897,17 +915,17 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
return false; return false;
// finally, we should be GTG. // 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; return false;
timespec now; timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
wlr_surface_send_frame_done(PSURFACE, &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) { if (!m_pLastScanout) {
m_pLastScanout = PCANDIDATE; m_pLastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE, PCANDIDATE->m_szTitle); 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); 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); Debug::log(ERR, "Could not apply gamma control to {}", pMonitor->szName);
return; 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); 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); wlr_gamma_control_v1_send_failed_and_destroy(PGAMMACTRL);
} }
} }
@@ -1144,6 +1163,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
if (UNLOCK_SC) if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false); wlr_output_lock_software_cursors(pMonitor->output, false);
pMonitor->state.clear();
return; return;
} }
@@ -1230,9 +1251,9 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
EMIT_HOOK_EVENT("render", RENDER_POST); 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) if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false); wlr_output_lock_software_cursors(pMonitor->output, false);
@@ -1319,6 +1340,8 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
std::string commandForCfg = ""; std::string commandForCfg = "";
const auto OUTPUT = head->state.output; const auto OUTPUT = head->state.output;
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT);
RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest");
commandForCfg += std::string(OUTPUT->name) + ","; commandForCfg += std::string(OUTPUT->name) + ",";
@@ -1329,7 +1352,7 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
continue; continue;
} }
wlr_output_enable(OUTPUT, head->state.enabled); wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled);
if (head->state.mode) if (head->state.mode)
commandForCfg += commandForCfg +=
@@ -1343,10 +1366,10 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool
if (!test) { if (!test) {
g_pConfigManager->parseKeyword("monitor", commandForCfg, true); 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) if (!ok)
break; break;
@@ -1567,8 +1590,7 @@ void CHyprRenderer::damageSurface(wlr_surface* pSurface, double x, double y, dou
y += CORRECTION.y; y += CORRECTION.y;
} }
CRegion damageBox; CRegion damageBox{&pSurface->buffer_damage};
wlr_surface_get_effective_damage(pSurface, damageBox.pixman());
if (scale != 1.0) if (scale != 1.0)
wlr_region_scale(damageBox.pixman(), damageBox.pixman(), scale); wlr_region_scale(damageBox.pixman(), damageBox.pixman(), scale);
@@ -1745,6 +1767,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
return true; 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 // Needed in case we are switching from a custom modeline to a standard mode
pMonitor->customDrmMode = {}; pMonitor->customDrmMode = {};
bool autoScale = false; bool autoScale = false;
@@ -1757,10 +1782,10 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->scale = DEFAULTSCALE; 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; 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; 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; 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 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) && if (DELTALESSTHAN(mode->width, pMonitorRule->resolution.x, 1) && DELTALESSTHAN(mode->height, pMonitorRule->resolution.y, 1) &&
DELTALESSTHAN(mode->refresh / 1000.f, pMonitorRule->refreshRate, 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); Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f);
continue; continue;
} }
@@ -1795,11 +1820,11 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
if (!found) { 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->vecSize = pMonitorRule->resolution;
pMonitor->refreshRate = pMonitorRule->refreshRate; 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"); Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output);
@@ -1811,7 +1836,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
// Preferred is valid // 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, 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); 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 { } else {
auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &pMonitorRule->drmMode); auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &pMonitorRule->drmMode);
if (mode) { if (mode) {
wlr_output_set_mode(pMonitor->output, mode); wlr_output_state_set_mode(pMonitor->state.wlr(), mode);
pMonitor->customDrmMode = pMonitorRule->drmMode; pMonitor->customDrmMode = pMonitorRule->drmMode;
} else { } else {
Debug::log(ERR, "wlr_drm_connector_add_mode failed"); Debug::log(ERR, "wlr_drm_connector_add_mode failed");
@@ -1841,13 +1866,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
} }
} else { } 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->vecSize = pMonitorRule->resolution;
pMonitor->refreshRate = pMonitorRule->refreshRate; 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"); Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output);
@@ -1859,7 +1884,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
// Preferred is valid // 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, 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); 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)) { if (pMonitorRule->resolution == Vector2D(-1, -1)) {
wl_list_for_each(mode, &pMonitor->output->modes, link) { 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)) { if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || mode->refresh > (currentRefresh + 3000.f)) {
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())) {
currentWidth = mode->width; currentWidth = mode->width;
currentHeight = mode->height; currentHeight = mode->height;
currentRefresh = mode->refresh; currentRefresh = mode->refresh;
@@ -1896,8 +1921,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
wl_list_for_each(mode, &pMonitor->output->modes, link) { wl_list_for_each(mode, &pMonitor->output->modes, link) {
if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) ||
(mode->width > currentWidth && mode->height > currentHeight)) { (mode->width > currentWidth && mode->height > currentHeight)) {
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())) {
currentWidth = mode->width; currentWidth = mode->width;
currentHeight = mode->height; currentHeight = mode->height;
currentRefresh = mode->refresh; currentRefresh = mode->refresh;
@@ -1920,7 +1945,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
// Preferred is valid // 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, 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); 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; wlr_output_mode* mode;
wl_list_for_each(mode, &pMonitor->output->modes, link) { 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); Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f);
continue; continue;
} }
@@ -1963,7 +1988,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} }
} else { } else {
// Preferred is valid // 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->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; 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; pMonitor->vecPixelSize = pMonitor->vecSize;
@@ -1989,6 +2015,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero; Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero;
if (logicalZero == logicalZero.round()) { if (logicalZero == logicalZero.round()) {
pMonitor->scale = scaleZero; pMonitor->scale = scaleZero;
wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale);
} else { } else {
for (size_t i = 1; i < 90; ++i) { for (size_t i = 1; i < 90; ++i) {
double scaleUp = (searchScale + i) / 120.0; double scaleUp = (searchScale + i) / 120.0;
@@ -2026,11 +2053,15 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
} else } else
pMonitor->scale = searchScale; pMonitor->scale = searchScale;
} }
// 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);
} }
} }
wlr_output_set_scale(pMonitor->output, pMonitor->scale);
// clang-format off // clang-format off
static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{ static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{
std::vector<std::pair<std::string, uint32_t>>{ /* 10-bit */ std::vector<std::pair<std::string, uint32_t>>{ /* 10-bit */
@@ -2046,9 +2077,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->drmFormat = DRM_FORMAT_INVALID; pMonitor->drmFormat = DRM_FORMAT_INVALID;
for (auto& fmt : formats[(int)!pMonitorRule->enable10bit]) { 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); Debug::log(ERR, "output {} failed basic test on format {}", pMonitor->szName, fmt.first);
} else { } else {
Debug::log(LOG, "output {} succeeded basic test on format {}", pMonitor->szName, fmt.first); 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; pMonitor->enabled10bit = set10bit;
if (!wlr_output_commit(pMonitor->output)) { if (!pMonitor->state.commit())
Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name); Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name);
}
int x, y; int x, y;
wlr_output_transformed_resolution(pMonitor->output, &x, &y); wlr_output_transformed_resolution(pMonitor->output, &x, &y);
@@ -2080,14 +2110,21 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->updateMatrix(); 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); g_pHyprOpenGL->destroyMonitorResources(pMonitor);
// updato wlroots // updato wlroots
g_pCompositor->arrangeMonitors(); g_pCompositor->arrangeMonitors();
wlr_damage_ring_set_bounds(&pMonitor->damage, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y); 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 // updato us
arrangeLayersForMonitor(pMonitor->ID); arrangeLayersForMonitor(pMonitor->ID);
@@ -2422,7 +2459,7 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode
} }
if (!buffer) { 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; return false;
m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge); m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge);
@@ -2466,7 +2503,7 @@ void CHyprRenderer::endRender() {
glFlush(); glFlush();
if (m_eRenderMode == RENDER_MODE_NORMAL) { 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 unsetEGL(); // flush the context
} }

View File

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

View File

@@ -42,9 +42,9 @@ class CShader {
GLint gradientLength = -1; GLint gradientLength = -1;
GLint angle = -1; GLint angle = -1;
GLint time = -1; GLint time = -1;
GLint distort = -1; GLint distort = -1;
GLint output = -1; GLint wl_output = -1;
// Blur prepare // Blur prepare
GLint contrast = -1; GLint contrast = -1;

View File

@@ -104,5 +104,5 @@ std::string CHyprBorderDecoration::getDisplayName() {
} }
bool CHyprBorderDecoration::doesntWantBorders() { 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() {} CHyprGroupBarDecoration::~CHyprGroupBarDecoration() {}
SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; static auto* const PHEIGHT = &g_pConfigManager->getConfigValuePtr("group:groupbar:height")->intValue;
static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->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; SDecorationPositioningInfo info;
info.policy = DECORATION_POSITION_STICKY; info.policy = DECORATION_POSITION_STICKY;
@@ -37,7 +38,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
info.reserved = true; info.reserved = true;
if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) 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 else
info.desiredExtents = {{0, 0}, {0, 0}}; 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 PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue;
static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->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 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; static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue;
if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate) 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; 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) if (DESIREDHEIGHT != ASSIGNEDBOX.h)
g_pDecorationPositioner->repositionDeco(this); g_pDecorationPositioner->repositionDeco(this);
@@ -128,12 +130,18 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
color.a *= a; color.a *= a;
g_pHyprOpenGL->renderRect(&rect, color); g_pHyprOpenGL->renderRect(&rect, color);
// render title if necessary rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth,
if (*PRENDERTITLES) { ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2};
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, rect.scale(pMonitor->scale);
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); CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle);
if (!pTitleTex) 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})) Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}))
.get(); .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.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale;
rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale;
@@ -243,7 +245,7 @@ CTitleTex::~CTitleTex() {
tex.destroyTexture(); tex.destroyTexture();
} }
void renderGradientTo(CTexture& tex, const CColor& grad) { void renderGradientTo(CTexture& tex, CGradientValueData* grad) {
if (!g_pCompositor->m_pLastMonitor) if (!g_pCompositor->m_pLastMonitor)
return; return;
@@ -261,8 +263,12 @@ void renderGradientTo(CTexture& tex, const CColor& grad) {
cairo_pattern_t* pattern; cairo_pattern_t* pattern;
pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y); 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_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y);
cairo_set_source(CAIRO, pattern); cairo_set_source(CAIRO, pattern);
cairo_fill(CAIRO); cairo_fill(CAIRO);
@@ -290,13 +296,13 @@ void renderGradientTo(CTexture& tex, const CColor& grad) {
} }
void refreshGroupBarGradients() { void refreshGroupBarGradients() {
static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; 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 PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue;
static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data; CGradientValueData* PGROUPCOLACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data.get();
static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data; CGradientValueData* PGROUPCOLINACTIVE = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data.get();
static auto* const PGROUPCOLACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data; CGradientValueData* PGROUPCOLACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data.get();
static auto* const PGROUPCOLINACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data; CGradientValueData* PGROUPCOLINACTIVELOCKED = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data.get();
g_pHyprRenderer->makeEGLCurrent(); g_pHyprRenderer->makeEGLCurrent();
@@ -310,13 +316,16 @@ void refreshGroupBarGradients() {
if (!*PENABLED || !*PGRADIENTS) if (!*PENABLED || !*PGRADIENTS)
return; return;
renderGradientTo(m_tGradientActive, ((CGradientValueData*)PGROUPCOLACTIVE->get())->m_vColors[0]); renderGradientTo(m_tGradientActive, PGROUPCOLACTIVE);
renderGradientTo(m_tGradientInactive, ((CGradientValueData*)PGROUPCOLINACTIVE->get())->m_vColors[0]); renderGradientTo(m_tGradientInactive, PGROUPCOLINACTIVE);
renderGradientTo(m_tGradientLockedActive, ((CGradientValueData*)PGROUPCOLACTIVELOCKED->get())->m_vColors[0]); renderGradientTo(m_tGradientLockedActive, PGROUPCOLACTIVELOCKED);
renderGradientTo(m_tGradientLockedInactive, ((CGradientValueData*)PGROUPCOLINACTIVELOCKED->get())->m_vColors[0]); renderGradientTo(m_tGradientLockedInactive, PGROUPCOLINACTIVELOCKED);
} }
bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
if (m_pWindow == m_pWindow->m_sGroupData.pNextWindow)
return false;
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); 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