Compare commits

...

64 Commits

Author SHA1 Message Date
Vaxry
6310b8a1fe Revert "pass: remove renderer finalDamage since it's unused (#9996)"
This reverts commit 0a7e2cb152.
2025-04-11 00:34:00 +02:00
Aaron Blasko
b83c9f5c6f cmake: do not install version.h.in (#10035) 2025-04-11 00:31:07 +02:00
Virt
d775686380 input: add warp_on_toggle_special (#9945) 2025-04-10 14:54:24 +02:00
Ikalco
0dc531c4a7 core: fix crash in data device on shutdown (#9997) 2025-04-09 18:08:42 +02:00
Ikalco
0a7e2cb152 pass: remove renderer finalDamage since it's unused (#9996) 2025-04-09 17:50:06 +02:00
Vaxry
4f991610d0 watchdog: remove watchdog
it has been unused for a while now
2025-04-09 01:48:21 +01:00
nyx
ea852965ff xdg-shell: fix some null refs (#9992) 2025-04-08 19:43:15 +02:00
Vaxry
260d8e1f71 Permission Manager: add permission management for screencopy (#9930) 2025-04-08 19:39:53 +02:00
nyx
642f394eb3 xwayland: sync primary selection with wayland (#9952) 2025-04-08 17:36:29 +02:00
Vaxry
b15c2bfff6 CursorManager: Store cursor pixel data retrieved from X/HC as a copy (#9986)
Instead of storing pointers as refs (which could randomly get invalid very easily) copy the data.
2025-04-07 21:08:16 +02:00
Ikalco
da86db43d4 core: refactor and improve surface commit (#9805)
* make CHLBufferReference not a SP anymore

* copy over release and acquire points in CHLBufferReference

* use CHLBufferReference in screencopy and toplevel export

TODO: use CHLBufferReference in direct scanout properly
      the only problem is the scanout buffer release timing,
      specifically the onBackendRelease mechanism

* cleanup SSurfaceState and surface pending commit tracking

* move surface code from DRMSyncobj, and move acquire to SSurfaceState

* use queue for comitted pending surface states like proto says

"The content update is placed in a queue until it becomes active." - wl_surface::commit

* drop, not release, prev buffer if 2nd buffer wl_surface.attach is sent

"A wl_buffer that has been attached and then replaced by another attach instead of committed will not receive a release event, and is not used by the compositor." - wl_surface::attach
2025-04-07 21:03:27 +02:00
Vaxry
70ae99f521 input/layers: Fix exclusive LS focus / refocus after unmap (#9984) 2025-04-07 20:52:11 +02:00
Jan Beich
a8eda7f978 helpers: add missing include for BSDs after 3c128679ee (#9982)
src/helpers/AsyncDialogBox.cpp:47:23: error: use of undeclared identifier 'read'
   47 |         while ((ret = read(m_pipeReadFd.get(), buf.data(), 1023)) > 0) {
      |                       ^
src/helpers/AsyncDialogBox.cpp:83:9: error: use of undeclared identifier 'pipe'
   83 |     if (pipe(outPipe)) {
      |         ^
src/helpers/AsyncDialogBox.cpp:110:5: error: use of undeclared identifier 'close'
  110 |     close(outPipe[1]);
      |     ^
2025-04-07 20:36:01 +02:00
kerty0
8a8f394da7 swipe: fix swiping onto a new workspace bound to another monitor (#8176) (#9927)
The previous code didn't check if the chosen new workspace was bound to another monitor, causing buggy behavior where workspace was simultaneously open and not.

The fix simply uses `r+1` for new workspace selection.

Also, the previous code would select rightmost workspace + 1, creating large gaps in workspace IDs in some scenarios. Example (`()` and `[]` indicate workspaces on different monitors):

`(1), 2, 3, 4, 5, 6, 7, 8, 9, [10]`

Swipe right on `()` monitor would create:

`(1), 2, 3, 4, 5, 6, 7, 8, 9, [10], (11)`

But with this commit:

`(1), (2), 3, 4, 5, 6, 7, 8, 9, [10]`
2025-04-07 14:46:31 +02:00
Virt
51838fb5f5 layout: properly track floating window position (#9937) 2025-04-06 23:41:27 +02:00
Vaxry
85f874d10f swipe: fix prev workspace remembering
fixes #9904
2025-04-06 22:35:26 +01:00
nyx
9b3925009a DataDevice: position icon at cursor hotspot (#9895)
* DataDevice: position icon at cursor hotspot

ref: https://wayland.app/protocols/wayland#wl_data_device:request:start_drag
2025-04-06 23:24:14 +02:00
Vaxry
3c128679ee helpers: Add an async dialog box impl (#9919)
Adds an async dialog box, way safer than our previous local solution for ANR
2025-04-06 17:31:58 +02:00
Vaxry
e96b8ce4cc window: send fractional scale on updateScaleTransform
fixes #9889
2025-04-06 00:30:13 +01:00
Maximilian Seidler
433b7881a3 compositor: fix crash when moving a workspace to a monitor with size 0x0 (#9848) 2025-04-06 00:54:29 +02:00
Vaxry
ed05f14300 ci: nuke stalebot 2025-04-05 19:28:42 +01:00
Vaxry
c62fb08da6 github: remove issue templates 2025-04-05 18:45:52 +01:00
Vaxry
8ba20fcae1 compositor: avoid crash on null window monitor move
ref #9809
2025-04-05 00:30:33 +01:00
Mihai Fufezan
ff97d18c4c flake.lock: update 2025-04-03 18:08:55 +00:00
Maximilian Seidler
5e8bb71785 ctm: fix crash when finishing ctm progress with a destroyed monitor (#9835) 2025-04-03 16:40:59 +02:00
Amadej Kastelic
b496e2c718 nix/module: load plugins using exec-once (#9836) 2025-04-03 10:43:06 +03:00
Arkady Buryakov
a41b8d5e97 groupbar: add text offset and upper gap settings (#9733)
* Groupbar: add keep_upper_gap setting to apply/remove outer gap offset to the upper side of groupbar

* Groupbar: add text_offset setting to adjust text vertical position in a group header
2025-04-02 22:26:46 +02:00
Armin
8654029f86 versionkeeper: create version file if not present (#9829) 2025-04-02 22:21:05 +02:00
nyx
a4e6c5d678 window: don't deactivate unfocused xwayland windows in groups (#9781)
* window: don't deactivate unfocused xwayland windows in groups

we dont want to deactivate unfocused xwayland windows because X is weird, keep the behavior for wayland windows
2025-04-02 00:51:37 +02:00
nyx
3a47c73f34 layout: center floating window at cursor when picked up from fullscreen (#9780)
* layout: center floating window at cursor when picked up from fullscreen

when picking up a floating window after it had been fullscreened before it would return to its previous position which looked ugly because the cursor could be no where near the windows original position, this patch makes it so that the window is returned to the users current cursor position

* E
2025-04-02 00:45:51 +02:00
X2E4VXpZKv
1f0fd79b91 internal: Don't force default cursor on config reload/monitor reconfigure (#9815) 2025-04-01 16:20:38 +02:00
Vaxry
d1a59ec39e renderer: render tiled fading out above other tiled windows
fixes #9717

closes #9796
2025-04-01 00:25:09 +01:00
Vaxry
4c987b20e2 makefile: fix find command in installheaders
fixes #9812
2025-03-31 17:13:27 +01:00
nyx
2309270752 anr: add config for ping number before popup shows up (#9782)
* anr: make pings configurable

makes the pings of the dialog popup configurable
2025-03-31 18:06:17 +02:00
Vaxry
79b526a041 socket2: add minimized event for foreign-wlr
ref #995
2025-03-30 22:38:30 +01:00
nyx
075bbecabd core: fix artifacts when fullscreening (#9778)
* core: fix artifacts when fullscreening

fixes an issue where fullscreening a floating window that is between two monitors causes artifacts to appear on the monitor where it did not become fullscreened on

* e
2025-03-30 23:28:12 +02:00
nyx
8aaffda969 core: fix null ref when resuming system (#9794)
* core: fix null ref when resuming system

* e
2025-03-30 23:18:04 +02:00
Shockingly Good
10a335631e solitary: Fix the non-working tearing #9429 (#9772)
Fixes the non-working tearing by removing the incorrect
opaqueness check for the windows.

Fixes #9429
2025-03-30 20:29:39 +02:00
Emad Elsaid
da2d7c3971 config: Fix matching monitor by description to allow space prefix (#9788) 2025-03-30 03:12:15 +02:00
LeviVanDerMaas
05eb0aa43d workspaces: Add binds:hide_special_on_workspace_change (#9728) 2025-03-30 03:11:39 +02:00
Tom Englund
fc7223edc0 synctimeline: check if fd is readable before wait (#9789)
a lot of the time the fd is already readable, and done. so just call the
waiter directly instead of making a waiter and adding it to the
eventloop.
2025-03-30 01:53:23 +01:00
Lee Bousfield
86c279d7d0 protocols: Don't update hdr metadata if image description is unchanged (#9776) 2025-03-30 01:25:27 +01:00
micha4w
46b00a4a86 makefile: add new shaders to make installheaders (#9783) 2025-03-30 01:25:02 +01:00
Tom Englund
4a79eea6dc opengl: check for g_pHyprOpengl pointer (#9791)
restore the pointer check to avoid null ptr dereference on compositor
destruction.
2025-03-29 21:52:27 +01:00
UjinT34
7374a023ef renderer/opengl: Extract shaders from source (#9600)
---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2025-03-29 01:19:35 +01:00
Lee Bousfield
a46576afc3 xwayland: Cleanup server startup and FDs (#9769) 2025-03-28 17:12:25 +01:00
Lee Bousfield
10035a85cc core: Don't damage the entire surface every frame (#9763)
* core: Don't damage the entire surface every frame

* core: Damage buffer on dims or transform change

* core: Use guards for scale and tr equality checks
2025-03-28 17:00:39 +01:00
Vaxry
c93140a5f1 surfacestate: reset buffer bit before applying to current
fixes #9759
2025-03-28 12:32:07 +00:00
Vaxry
5380cbcdda workspaces: minor fixes to persistence
fixes #9741
2025-03-27 14:00:29 +00:00
Tom Englund
9ea76428b6 internal: fix minor ubsan errors (#9743)
* opengl: check if g_pHyprOpengl exist

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

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

* xdgshell: dont apply state on empty states

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

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

fixes #9738
2025-03-26 14:28:19 +00:00
vaxerski
cec084c178 pass/rect: include clipBox in opaque calculations
ref #9730 ref #9709
2025-03-26 11:47:04 +00:00
vaxerski
c2ef8fcc00 groupbar: round boxes 2025-03-26 11:44:38 +00:00
Tom Englund
3fc3521a97 pass: remove unusued timeline in texpass (#9734)
remove unused timeline and waitpoint in texpass and especially remove
the passing it to renderTextureInternalWithDamage that implicitly
converted it to bool. setting discardActive and allowCustomUV
2025-03-26 02:22:09 +01:00
Arkady Buryakov
9a67354fa2 Groupbar: apply scaling factor to text (#9731) 2025-03-26 02:07:56 +01:00
nyx
f7ba86d1f3 keybinds: add sendkeystate dispatcher (#9599) 2025-03-25 00:59:13 +01:00
Vaxry
f3db1b172c decoration: bring back border_part_of_window
fixes #9683, now under decoration: though
2025-03-24 23:57:50 +00:00
Tom Englund
2a6d070774 xwl: dont close the fd to early (#9715)
dont close the fd until the wl_event_source is removed, so we dont get
another event triggered with an already closed fd.
2025-03-24 19:33:07 +01:00
Vaxry
aec69131cd seat: avoid sending null surfaces in leave/enter events
ref #9699
2025-03-24 14:10:47 +00:00
vaxerski
4b968e5bc1 [gha] Nix: update inputs 2025-03-24 12:57:34 +00:00
UjinT34
a852461c7d renderer: Simplify and fix hdr metadata setting (#9706)
* simplify and fix hdr metadata setting

* keep incorrect(?) cm skip till #9600
2025-03-24 13:56:07 +01:00
Mihai Fufezan
e4abf26069 Nix: add changes from Nixpkgs derivation 2025-03-23 18:19:34 +02:00
Mihai Fufezan
006bd9eef5 protocols/meson.build: use native wayland-scanner 2025-03-23 18:15:17 +02:00
112 changed files with 3037 additions and 1812 deletions

View File

@@ -1,117 +1,15 @@
name: Bug Report
description: Something is not working right
labels: ["bug"]
name: Do not open issues, go to discussions please!
description: Do not open an issue
body:
- type: checkboxes
attributes:
label: Already reported ? *
description: Before opening a new bug report, please take a moment to search through the current open issues. If the same bug is already reported, don't open new issue - instead go upvote/comment on an existing one.
label: Please close this issue.
description: Users cannot open issues. I want my issue to be closed.
options:
- label: I have searched the existing open and closed issues.
- label: Yes, I want this issue to be closed.
required: true
- type: dropdown
id: type
attributes:
label: Regression?
description: |
Regression means that something used to work but no longer does.
**BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue
multiple: true
options:
- "Definitely a regression - something broke after update (requires bisect)"
- "Probably not a regression / I don't remember it happening before"
- "Not a regression - it's bug regarding new feature"
- "Not a regression - it's an old bug"
- "I don't know, I started using Hyprland only recently"
validations:
required: true
- type: textarea
id: ver
id: body
attributes:
label: System Info and Hyprland Version
description: |
Paste the output of `hyprctl systeminfo` here. If you can't
launch Hyprland, paste the output of `Hyprland --systeminfo`.
value: "<details>
<summary>System/Version info</summary>
```
<Paste the output of the command here, without removing any formatting around this>
```
</details>"
validations:
required: true
- type: textarea
id: desc
attributes:
label: Description
description: "What went wrong?"
validations:
required: true
- type: textarea
id: repro
attributes:
label: How to reproduce
description: "How can someone else reproduce the issue?"
placeholder: |
1. ...
2. ...
3. ...
validations:
required: true
- type: markdown
attributes:
value: |
## Additional info section
In the section below you will be asked to upload some files.
When including text files (such as logs or config), please **always ATTACH** them, and not paste them directly.
This is important to avoid clutter, spam, and make the issues more readable.
Thanks for your understanding.
# The main reason to disallow pasting directly or in a dropdown, is to not clutter
# the issue with unnecessary keywords, making the github issue search useless.
- type: checkboxes
attributes:
label: Attach not paste
options:
- label: I understand that all text files must be *attached*, and not pasted directly. If not respected, this issue will likely get closed as spam
required: true
- type: markdown
attributes:
value: >-
Please be sure to upload the following files below if they are relevant to the issue:
- Logs can be found in $XDG_RUNTIME_DIR/hypr (sort by date to grab the latest)
- Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
- Hyprland config files - `hyprctl systeminfo -c > /tmp/hyprland_config_dump.txt` use this command to dump full configuration to a single file.
- type: checkboxes
attributes:
label: Checklist of files to include below
options:
- label: Hyprland config - `hyprctl systeminfo -c` (always include)
- label: Crash report (always include in case of crash)
- label: Video (always include in case of a visual bug)
- label: Logs (might contain useful info such as errors)
- type: textarea
id: logs
attributes:
label: Additional info & File uploads
description: |
Tip: You can attach files by clicking this area to highlight it and then dragging files in.
label: Issue body

View File

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

View File

@@ -1,28 +0,0 @@
# 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:
if: github.repository == 'hyprwm/Hyprland'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.STALEBOT_PAT }}
stale-issue-label: "stale"
stale-pr-label: "stale"
operations-per-run: 40
days-before-close: -1

2
.gitignore vendored
View File

@@ -28,6 +28,8 @@ protocols/*.c*
protocols/*.h*
.ccls-cache
*.so
src/render/shaders/*.inc
src/render/shaders/Shaders.hpp
hyprctl/hyprctl

View File

@@ -25,6 +25,9 @@ message(STATUS "Gathering git info")
# Get git info hash and branch
execute_process(COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# Make shader files includable
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
find_package(PkgConfig REQUIRED)
@@ -105,7 +108,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.6.0)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
@@ -444,5 +447,7 @@ install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
PATTERN "*.h*"
PATTERN "*.frag")
PATTERN "*.h"
PATTERN "*.hpp"
PATTERN "*.inc"
)

View File

@@ -52,7 +52,7 @@ installheaders:
cmake --build ./build --config Release --target generate-protocol-headers
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
find src -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.inc' \) -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi

View File

@@ -52,6 +52,20 @@ env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24
###################
### PERMISSIONS ###
###################
# See https://wiki.hyprland.org/Configuring/Permissions/
# ecosystem {
# enforce_permissions = 1
# }
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
#####################
### LOOK AND FEEL ###
#####################

30
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1742213273,
"narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=",
"lastModified": 1743265529,
"narHash": "sha256-QbjP15/2N+VJl0b5jxrrTc+VOt39aU4XrDvtP0Lz5ik=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf",
"rev": "1d2dbd72c2bbaceab031c592d4810f744741d203",
"type": "github"
},
"original": {
@@ -128,11 +128,11 @@
]
},
"locked": {
"lastModified": 1738422629,
"narHash": "sha256-5v+bv75wJWvahyM2xcMTSNNxmV8a7hb01Eey5zYnBJw=",
"lastModified": 1743714874,
"narHash": "sha256-yt8F7NhMFCFHUHy/lNjH/pjZyIDFNk52Q4tivQ31WFo=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "755aef8dab49d0fc4663c715fa4ad221b2aedaed",
"rev": "3a5c2bda1c1a4e55cc1330c782547695a93f05b2",
"type": "github"
},
"original": {
@@ -238,11 +238,11 @@
]
},
"locked": {
"lastModified": 1741534688,
"narHash": "sha256-EV3945SnjOCuRVbGRghsWx/9D89FyshnSO1Q6/TuQ14=",
"lastModified": 1743950287,
"narHash": "sha256-/6IAEWyb8gC/NKZElxiHChkouiUOrVYNq9YqG0Pzm4Y=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "dd1f720cbc2dbb3c71167c9598045dd3261d27b3",
"rev": "f2dc70e448b994cef627a157ee340135bd68fbc6",
"type": "github"
},
"original": {
@@ -276,11 +276,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1742069588,
"narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"lastModified": 1743827369,
"narHash": "sha256-rpqepOZ8Eo1zg+KJeWoq1HAOgoMCDloqv5r2EAa9TSA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"rev": "42a1c966be226125b48c384171c44c651c236c22",
"type": "github"
},
"original": {
@@ -299,11 +299,11 @@
]
},
"locked": {
"lastModified": 1742058297,
"narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=",
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {

View File

@@ -23,7 +23,7 @@ _hyprctl () {
local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize)
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize sendkeystate)
declare -A literal_transitions
literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)"
literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)"

View File

@@ -29,7 +29,7 @@ function _hyprctl
set COMP_CWORD (count $COMP_WORDS)
end
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize"
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate"
set descriptions
set descriptions[1] "Resize the active window"

View File

@@ -106,6 +106,7 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (execr) "Execute a raw shell command"
| (pass) "Pass the key to a specified window"
| (sendshortcut) "On shortcut X sends shortcut Y to a specified window"
| (sendkeystate) "Send a key with specific state (down/repeat/up) to a specified window (window must keep focus for events to continue)"
| (killactive) "Close the active window"
| (closewindow) "Close a specified window"
| (workspace) "Change the workspace"

View File

@@ -17,7 +17,7 @@ _hyprctl_cmd_0 () {
}
_hyprctl () {
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize")
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate")
local -A descriptions
descriptions[1]="Resize the active window"

View File

@@ -35,7 +35,7 @@ aquamarine = dependency('aquamarine', version: '>=0.8.0')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
hyprutils = dependency('hyprutils', version: '>= 0.6.0')
aquamarine_version_list = aquamarine.version().split('.')
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
@@ -87,9 +87,11 @@ endif
# Generate hyprland version and populate version.h
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
# Make shader files includable
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
# Install headers
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.frag', check: true)
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.inc', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)

View File

@@ -11,6 +11,7 @@
aquamarine,
binutils,
cairo,
epoll-shim,
git,
glaze,
hyprcursor,
@@ -141,6 +142,7 @@ in
wayland-scanner
xorg.libXcursor
]
(optionals customStdenv.hostPlatform.isBSD [ epoll-shim ])
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
(optionals enableXWayland [
xorg.libxcb
@@ -153,6 +155,8 @@ in
(optional withSystemd systemd)
];
strictDeps = true;
mesonBuildType =
if debug
then "debug"
@@ -162,6 +166,7 @@ in
(mapAttrsToList mesonEnable {
"xwayland" = enableXWayland;
"legacy_renderer" = legacyRenderer;
"systemd" = withSystemd;
"uwsm" = false;
"hyprpm" = false;
})

View File

@@ -126,13 +126,14 @@ in {
bottomCommandsPrefixes = cfg.bottomPrefixes;
}
{
plugin = let
"exec-once" = let
mkEntry = entry:
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
in
map mkEntry cfg.plugins;
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
};
in
lib.mkIf shouldGenerate {

View File

@@ -87,7 +87,7 @@ foreach protocol : protocols
endforeach
# wayland.xml generation
wayland_scanner = dependency('wayland-scanner')
wayland_scanner = dependency('wayland-scanner', native: true)
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
wayland_xml = wayland_scanner_datadir / 'wayland.xml'

View File

@@ -0,0 +1,24 @@
#!/bin/sh
SHADERS_SRC="./src/render/shaders/glsl"
echo "-- Generating shader includes"
if [ ! -d ./src/render/shaders ]; then
mkdir ./src/render/shaders
fi
echo '#pragma once' > ./src/render/shaders/Shaders.hpp
echo '#include <map>' >> ./src/render/shaders/Shaders.hpp
echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/render/shaders/Shaders.hpp
for filename in `ls ${SHADERS_SRC}`; do
echo "-- ${filename}"
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
echo "}," >> ./src/render/shaders/Shaders.hpp
done
echo '};' >> ./src/render/shaders/Shaders.hpp

View File

@@ -15,6 +15,7 @@
#include "managers/DonationNagManager.hpp"
#include "managers/ANRManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include "managers/permissions/DynamicPermissionManager.hpp"
#include <algorithm>
#include <aquamarine/output/Output.hpp>
#include <bit>
@@ -58,7 +59,6 @@
#include "managers/ProtocolManager.hpp"
#include "managers/LayoutManager.hpp"
#include "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp"
#include "hyprerror/HyprError.hpp"
#include "debug/HyprNotificationOverlay.hpp"
#include "debug/HyprDebugOverlay.hpp"
@@ -570,6 +570,7 @@ void CCompositor::cleanup() {
removeAllSignals();
g_pInputManager.reset();
g_pDynamicPermissionManager.reset();
g_pDecorationPositioner.reset();
g_pCursorManager.reset();
g_pPluginSystem.reset();
@@ -586,7 +587,6 @@ void CCompositor::cleanup() {
g_pConfigManager.reset();
g_pKeybindManager.reset();
g_pHookSystem.reset();
g_pWatchdog.reset();
g_pXWaylandManager.reset();
g_pPointerManager.reset();
g_pSeatManager.reset();
@@ -624,6 +624,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the AnimationManager!");
g_pAnimationManager = makeUnique<CHyprAnimationManager>();
Debug::log(LOG, "Creating the DynamicPermissionManager!");
g_pDynamicPermissionManager = makeUnique<CDynamicPermissionManager>();
Debug::log(LOG, "Creating the ConfigManager!");
g_pConfigManager = makeUnique<CConfigManager>();
@@ -637,11 +640,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
g_pTokenManager = makeUnique<CTokenManager>();
g_pConfigManager->init();
g_pWatchdog = makeUnique<CWatchdog>(); // requires config
// wait for watchdog to initialize to not hit data races in reading config values.
while (!g_pWatchdog->m_bWatchdogInitialized) {
std::this_thread::yield();
}
Debug::log(LOG, "Creating the PointerManager!");
g_pPointerManager = makeUnique<CPointerManager>();
@@ -2117,6 +2115,8 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) {
}
void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMonitor, bool noWarpCursor) {
static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue<Hyprlang::INT>("binds:hide_special_on_workspace_change");
if (!pWorkspace || !pMonitor)
return;
@@ -2152,11 +2152,13 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new {}", nextWorkspaceOnMonitorID);
g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->ID);
if (POLDMON)
g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->ID);
}
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID);
POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true);
if (POLDMON)
POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true);
}
// move the workspace
@@ -2182,9 +2184,11 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
*w->m_vRealPosition = pMonitor->vecPosition;
*w->m_vRealSize = pMonitor->vecSize;
}
} else {
*w->m_vRealPosition = Vector2D{(int)w->m_vRealPosition->goal().x % (int)pMonitor->vecSize.x, (int)w->m_vRealPosition->goal().y % (int)pMonitor->vecSize.y};
}
} else
*w->m_vRealPosition = Vector2D{
(pMonitor->vecSize.x != 0) ? (int)w->m_vRealPosition->goal().x % (int)pMonitor->vecSize.x : 0,
(pMonitor->vecSize.y != 0) ? (int)w->m_vRealPosition->goal().y % (int)pMonitor->vecSize.y : 0,
};
}
w->updateToplevel();
@@ -2199,6 +2203,9 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
pMonitor->activeWorkspace->startAnim(false, false);
}
if (*PHIDESPECIALONWORKSPACECHANGE)
pMonitor->setSpecialWorkspace(nullptr);
setActiveMonitor(pMonitor);
pMonitor->activeWorkspace = pWorkspace;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID);
@@ -2308,6 +2315,9 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
const eFullscreenMode CURRENT_EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)PWINDOW->m_sFullscreenState.internal);
const eFullscreenMode EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)state.internal);
if (PWINDOW->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE && EFFECTIVE_MODE != FSMODE_NONE)
g_pHyprRenderer->damageWindow(PWINDOW);
if (*PALLOWPINFULLSCREEN && !PWINDOW->m_bPinFullscreened && !PWINDOW->isFullscreen() && PWINDOW->m_bPinned) {
PWINDOW->m_bPinned = false;
PWINDOW->m_bPinFullscreened = true;
@@ -2627,7 +2637,13 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITO
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, getMonitorFromID(monID), NAME, SPECIAL, isEmpty));
const auto PMONITOR = getMonitorFromID(monID);
if (!PMONITOR) {
Debug::log(ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace");
return nullptr;
}
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty));
PWORKSPACE->m_fAlpha->setValueAndWarp(0);
@@ -2717,8 +2733,7 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
const PHLWINDOW pFirstWindowOnWorkspace = pWorkspace->getFirstWindow();
const int visibleWindowsOnWorkspace = pWorkspace->getWindows(std::nullopt, true);
const auto PWINDOWMONITOR = pWindow->m_pMonitor.lock();
const auto POSTOMON = pWindow->m_vRealPosition->goal() - PWINDOWMONITOR->vecPosition;
const auto POSTOMON = pWindow->m_vRealPosition->goal() - (pWindow->m_pMonitor ? pWindow->m_pMonitor->vecPosition : Vector2D{});
const auto PWORKSPACEMONITOR = pWorkspace->m_pMonitor.lock();
if (!pWindow->m_bIsFloating)
@@ -3061,6 +3076,8 @@ bool CCompositor::shouldChangePreferredImageDescription() {
}
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
if (!m_pLastMonitor)
return;
for (const auto& rule : rules) {
if (!rule.isPersistent)
@@ -3076,6 +3093,9 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
const auto PMONITOR = getMonitorFromString(rule.monitor);
if (!rule.monitor.empty() && !PMONITOR)
continue; // don't do anything yet, as the monitor is not yet present.
if (!PWORKSPACE) {
WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName;
@@ -3092,7 +3112,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
}
PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false);
createNewWorkspace(id, PMONITOR ? PMONITOR->ID : m_pLastMonitor->ID, wsname, false);
}
if (PWORKSPACE)

View File

@@ -247,6 +247,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_STRING_LONG,
.data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET?
},
SConfigOptionDescription{
.value = "decoration:border_part_of_window",
.description = "whether the border should be treated as a part of the window.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
/*
* blur:
@@ -1005,6 +1011,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
},
SConfigOptionDescription{
.value = "group:groupbar:keep_upper_gap",
.description = "keep an upper gap above gradient",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "group:groupbar:text_offset",
.description = "set an offset for a text",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SRangeData{0, -20, 20},
},
/*
* misc:
@@ -1217,6 +1235,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "misc:anr_missed_pings",
.description = "number of missed pings before showing the ANR dialog",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 1, 10},
},
/*
* binds:
@@ -1240,6 +1264,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "binds:hide_special_on_workspace_change",
.description = "If enabled, changing the active workspace (including to itself) will hide the special workspace on the monitor where the newly active workspace resides.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "binds:allow_workspace_cycles",
.description = "If enabled, workspaces dont forget their previous workspace, so cycles can be created by switching to the first workspace in a sequence, then endlessly "
@@ -1444,6 +1474,13 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
},
SConfigOptionDescription{
.value = "cursor:warp_on_toggle_special",
.description = "Move the cursor to the last focused window when toggling a special workspace. Options: 0 (Disabled), 1 (Enabled), "
"2 (Force - ignores cursor:no_warps option)",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
},
SConfigOptionDescription{
.value = "cursor:default_monitor",
.description = "the name of a default monitor for the cursor to be set to on startup (see hyprctl monitors for names)",
@@ -1568,12 +1605,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "debug:watchdog_timeout",
.description = "sets the timeout in seconds for watchdog to abort processing of a signal of the main thread. Set to 0 to disable.",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{5, 0, 20},
},
SConfigOptionDescription{
.value = "debug:disable_scale_checks",
.description = "disables verification of the scale factors. Will result in pixel alignment and rounding errors.",

View File

@@ -22,6 +22,7 @@
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/permissions/DynamicPermissionManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
#include "../plugins/PluginSystem.hpp"
@@ -374,6 +375,18 @@ static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) {
return result;
}
static Hyprlang::CParseResult handlePermission(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handlePermission(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
void CConfigManager::registerConfigVar(const char* name, const Hyprlang::INT& val) {
m_configValueNumber++;
m_pConfig->addConfigValue(name, val);
@@ -460,6 +473,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("misc:disable_hyprland_qtutils_check", Hyprlang::INT{0});
registerConfigVar("misc:lockdead_screen_delay", Hyprlang::INT{1000});
registerConfigVar("misc:enable_anr_dialog", Hyprlang::INT{1});
registerConfigVar("misc:anr_missed_pings", Hyprlang::INT{1});
registerConfigVar("group:insert_after_current", Hyprlang::INT{1});
registerConfigVar("group:focus_removed_window", Hyprlang::INT{1});
@@ -486,6 +500,8 @@ CConfigManager::CConfigManager() {
registerConfigVar("group:groupbar:gradient_round_only_edges", Hyprlang::INT{1});
registerConfigVar("group:groupbar:gaps_out", Hyprlang::INT{2});
registerConfigVar("group:groupbar:gaps_in", Hyprlang::INT{2});
registerConfigVar("group:groupbar:keep_upper_gap", Hyprlang::INT{1});
registerConfigVar("group:groupbar:text_offset", Hyprlang::INT{0});
registerConfigVar("debug:log_damage", Hyprlang::INT{0});
registerConfigVar("debug:overlay", Hyprlang::INT{0});
@@ -499,7 +515,6 @@ CConfigManager::CConfigManager() {
registerConfigVar("debug:suppress_errors", Hyprlang::INT{0});
registerConfigVar("debug:error_limit", Hyprlang::INT{5});
registerConfigVar("debug:error_position", Hyprlang::INT{0});
registerConfigVar("debug:watchdog_timeout", Hyprlang::INT{5});
registerConfigVar("debug:disable_scale_checks", Hyprlang::INT{0});
registerConfigVar("debug:colored_stdout_logs", Hyprlang::INT{1});
registerConfigVar("debug:full_cm_proto", Hyprlang::INT{0});
@@ -539,6 +554,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("decoration:dim_special", {0.2f});
registerConfigVar("decoration:dim_around", {0.4f});
registerConfigVar("decoration:screen_shader", {STRVAL_EMPTY});
registerConfigVar("decoration:border_part_of_window", Hyprlang::INT{1});
registerConfigVar("dwindle:pseudotile", Hyprlang::INT{0});
registerConfigVar("dwindle:force_split", Hyprlang::INT{0});
@@ -625,6 +641,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("binds:pass_mouse_when_bound", Hyprlang::INT{0});
registerConfigVar("binds:scroll_event_delay", Hyprlang::INT{300});
registerConfigVar("binds:workspace_back_and_forth", Hyprlang::INT{0});
registerConfigVar("binds:hide_special_on_workspace_change", Hyprlang::INT{0});
registerConfigVar("binds:allow_workspace_cycles", Hyprlang::INT{0});
registerConfigVar("binds:workspace_center_on", Hyprlang::INT{1});
registerConfigVar("binds:focus_preferred_method", Hyprlang::INT{0});
@@ -665,6 +682,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("cursor:no_warps", Hyprlang::INT{0});
registerConfigVar("cursor:persistent_warps", Hyprlang::INT{0});
registerConfigVar("cursor:warp_on_change_workspace", Hyprlang::INT{0});
registerConfigVar("cursor:warp_on_toggle_special", Hyprlang::INT{0});
registerConfigVar("cursor:default_monitor", {STRVAL_EMPTY});
registerConfigVar("cursor:zoom_factor", {1.f});
registerConfigVar("cursor:zoom_rigid", Hyprlang::INT{0});
@@ -698,6 +716,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
registerConfigVar("ecosystem:enforce_permissions", Hyprlang::INT{0});
registerConfigVar("experimental:xx_color_management_v4", Hyprlang::INT{0});
@@ -759,6 +778,7 @@ CConfigManager::CConfigManager() {
m_pConfig->registerHandler(&::handleSubmap, "submap", {false});
m_pConfig->registerHandler(&::handleBlurLS, "blurls", {false});
m_pConfig->registerHandler(&::handlePlugin, "plugin", {false});
m_pConfig->registerHandler(&::handlePermission, "permission", {false});
m_pConfig->registerHandler(&::handleEnv, "env", {true});
// pluginza
@@ -941,6 +961,8 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
m_vFailedPluginConfigValues.clear();
finalExecRequests.clear();
g_pDynamicPermissionManager->clearConfigPermissions();
// paths
m_configPaths.clear();
std::string mainConfigPath = getMainConfigPath();
@@ -1705,8 +1727,8 @@ void CConfigManager::addParseError(const std::string& err) {
PHLMONITOR CConfigManager::getBoundMonitorForWS(const std::string& wsname) {
auto monitor = getBoundMonitorStringForWS(wsname);
if (monitor.substr(0, 5) == "desc:")
return g_pCompositor->getMonitorFromDesc(monitor.substr(5));
if (monitor.starts_with("desc:"))
return g_pCompositor->getMonitorFromDesc(trim(monitor.substr(5)));
else
return g_pCompositor->getMonitorFromName(monitor);
}
@@ -1796,8 +1818,8 @@ std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) {
if (other->isDefault) {
if (other->monitor == name)
return other->workspaceString;
if (other->monitor.substr(0, 5) == "desc:") {
auto const monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5));
if (other->monitor.starts_with("desc:")) {
auto const monitor = g_pCompositor->getMonitorFromDesc(trim(other->monitor.substr(5)));
if (monitor && monitor->szName == name)
return other->workspaceString;
}
@@ -2826,6 +2848,32 @@ std::optional<std::string> CConfigManager::handlePlugin(const std::string& comma
return {};
}
std::optional<std::string> CConfigManager::handlePermission(const std::string& command, const std::string& value) {
CVarList data(value);
eDynamicPermissionType type = PERMISSION_TYPE_UNKNOWN;
eDynamicPermissionAllowMode mode = PERMISSION_RULE_ALLOW_MODE_UNKNOWN;
if (data[1] == "screencopy")
type = PERMISSION_TYPE_SCREENCOPY;
if (data[2] == "ask")
mode = PERMISSION_RULE_ALLOW_MODE_ASK;
else if (data[2] == "allow")
mode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
else if (data[2] == "deny")
mode = PERMISSION_RULE_ALLOW_MODE_DENY;
if (type == PERMISSION_TYPE_UNKNOWN)
return "unknown permission type";
if (mode == PERMISSION_RULE_ALLOW_MODE_UNKNOWN)
return "unknown permission allow mode";
g_pDynamicPermissionManager->addConfigPermissionRule(data[0], type, mode);
return {};
}
const std::vector<SConfigOptionDescription>& CConfigManager::getAllDescriptions() {
return CONFIG_OPTIONS;
}

View File

@@ -244,6 +244,7 @@ class CConfigManager {
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handlePermission(const std::string&, const std::string&);
std::string configCurrentPath;

View File

@@ -65,6 +65,20 @@ env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24
###################
### PERMISSIONS ###
###################
# See https://wiki.hyprland.org/Configuring/Permissions/
# ecosystem {
# enforce_permissions = 1
# }
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
#####################
### LOOK AND FEEL ###
#####################

View File

@@ -192,7 +192,7 @@ void NCrashReporter::createAndSaveCrash(int sig) {
#endif
};
u_int miblen = sizeof(mib) / sizeof(mib[0]);
char exe[PATH_MAX] = "";
char exe[PATH_MAX] = "/nonexistent";
size_t sz = sizeof(exe);
sysctl(mib, miblen, &exe, &sz, NULL, 0);
const auto FPATH = std::filesystem::canonical(exe);

View File

@@ -51,6 +51,7 @@ using namespace Hyprutils::OS;
#include "../managers/AnimationManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
#include "../render/Renderer.hpp"
#include "../render/OpenGL.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@@ -1643,6 +1644,13 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
return format == FORMAT_JSON ? std::format("{{\"{}\"}}\n", escapeJSONStrings(submap)) : (submap + "\n");
}
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
if (g_pHyprOpenGL->initShaders())
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
else
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
}
CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
@@ -1665,6 +1673,7 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});

View File

@@ -234,9 +234,10 @@ void CLayerSurface::onUnmap() {
// refocus if needed
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable()))
g_pInputManager->refocusLastWindow(PMONITOR);
else if (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus != surface->resource())
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable())) {
if (!g_pInputManager->refocusLastWindow(PMONITOR))
g_pInputManager->refocus();
} else if (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus != surface->resource())
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};

View File

@@ -17,6 +17,7 @@
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ContentType.hpp"
#include "../protocols/FractionalScale.hpp"
#include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp"
#include "../events/Events.hpp"
@@ -397,6 +398,7 @@ void CWindow::updateSurfaceScaleTransformDetails(bool force) {
if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale)
return;
PROTO::fractional->sendScale(s, PMONITOR->scale);
g_pCompositor->setPreferredScaleForSurface(s, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(s, PMONITOR->transform);
},
@@ -1148,9 +1150,6 @@ bool CWindow::opaque() {
if (m_fAlpha->value() != 1.f || m_fActiveInactiveAlpha->value() != 1.f)
return false;
if (m_vRealSize->goal().floor() != m_vReportedSize)
return false;
const auto PWORKSPACE = m_pWorkspace;
if (m_pWLSurface->small() && !m_pWLSurface->m_bFillIgnoreSmall)
@@ -1238,7 +1237,7 @@ void CWindow::setSuspended(bool suspend) {
if (suspend == m_bSuspended)
return;
if (m_bIsX11 || !m_pXDGSurface->toplevel)
if (m_bIsX11 || !m_pXDGSurface || !m_pXDGSurface->toplevel)
return;
m_pXDGSurface->toplevel->setSuspeneded(suspend);
@@ -1688,7 +1687,7 @@ Vector2D CWindow::requestedMinSize() {
Vector2D CWindow::requestedMaxSize() {
constexpr int NO_MAX_SIZE_LIMIT = 99999;
if (((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && !m_pXDGSurface->toplevel) || m_sWindowData.noMaxSize.valueOrDefault()))
if (((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && (!m_pXDGSurface || !m_pXDGSurface->toplevel)) || m_sWindowData.noMaxSize.valueOrDefault()))
return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT);
Vector2D maxSize = m_bIsX11 ? Vector2D(m_pXWaylandSurface->sizeHints->max_width, m_pXWaylandSurface->sizeHints->max_height) : m_pXDGSurface->toplevel->layoutMaxSize();
@@ -1791,9 +1790,11 @@ void CWindow::deactivateGroupMembers() {
auto curr = getGroupHead();
while (curr) {
if (curr != m_pSelf.lock()) {
if (curr->m_bIsX11)
curr->m_pXWaylandSurface->activate(false);
else if (curr->m_pXDGSurface && curr->m_pXDGSurface->toplevel)
// we dont want to deactivate unfocused xwayland windows
// because X is weird, keep the behavior for wayland windows
// also its not really needed for xwayland windows
// ref: #9760 #9294
if (!curr->m_bIsX11 && curr->m_pXDGSurface && curr->m_pXDGSurface->toplevel)
curr->m_pXDGSurface->toplevel->setActive(false);
}

View File

@@ -0,0 +1,123 @@
#include "AsyncDialogBox.hpp"
#include "./fs/FsUtils.hpp"
#include <csignal>
#include <unistd.h>
#include "../managers/eventLoop/EventLoopManager.hpp"
using namespace Hyprutils::OS;
SP<CAsyncDialogBox> CAsyncDialogBox::create(const std::string& title, const std::string& description, std::vector<std::string> buttons) {
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
Debug::log(ERR, "CAsyncDialogBox: cannot create, no hyprland-dialog");
return nullptr;
}
auto dialog = SP<CAsyncDialogBox>(new CAsyncDialogBox(title, description, buttons));
dialog->m_selfWeakReference = dialog;
return dialog;
}
CAsyncDialogBox::CAsyncDialogBox(const std::string& title, const std::string& description, std::vector<std::string> buttons) :
m_title(title), m_description(description), m_buttons(buttons) {
;
}
static int onFdWrite(int fd, uint32_t mask, void* data) {
auto box = (CAsyncDialogBox*)data;
box->onWrite(fd, mask);
return 0;
}
void CAsyncDialogBox::onWrite(int fd, uint32_t mask) {
if (mask & WL_EVENT_READABLE) {
std::array<char, 1024> buf;
int ret = 0;
// make the FD nonblock for a moment
// TODO: can we avoid this without risking a blocking read()?
int fdFlags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, fdFlags | O_NONBLOCK) < 0) {
Debug::log(ERR, "CAsyncDialogBox::onWrite: fcntl 1 failed!");
return;
}
while ((ret = read(m_pipeReadFd.get(), buf.data(), 1023)) > 0) {
m_stdout += std::string_view{(char*)buf.data(), (size_t)ret};
}
// restore the flags (otherwise libwayland wont give us a hangup)
if (fcntl(fd, F_SETFL, fdFlags) < 0) {
Debug::log(ERR, "CAsyncDialogBox::onWrite: fcntl 2 failed!");
return;
}
}
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
Debug::log(LOG, "CAsyncDialogBox: dialog {:x} hung up, closed.");
if (m_onResolution)
m_onResolution(m_stdout);
wl_event_source_remove(m_readEventSource);
m_selfReference.reset();
return;
}
}
void CAsyncDialogBox::open(std::function<void(std::string)> onResolution) {
m_onResolution = onResolution;
std::string buttonsString = "";
for (auto& b : m_buttons) {
buttonsString += b + ";";
}
if (!buttonsString.empty())
buttonsString.pop_back();
CProcess proc("hyprland-dialog", std::vector<std::string>{"--title", m_title, "--text", m_description, "--buttons", buttonsString});
int outPipe[2];
if (pipe(outPipe)) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to pipe()");
return;
}
m_pipeReadFd = CFileDescriptor(outPipe[0]);
proc.setStdoutFD(outPipe[1]);
m_readEventSource = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, m_pipeReadFd.get(), WL_EVENT_READABLE, ::onFdWrite, this);
if (!m_readEventSource) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to add read fd to loop");
return;
}
m_selfReference = m_selfWeakReference.lock();
m_dialogPid = proc.pid();
if (!proc.runAsync()) {
Debug::log(ERR, "CAsyncDialogBox::open: failed to run async");
wl_event_source_remove(m_readEventSource);
return;
}
// close the write fd, only the dialog owns it now
close(outPipe[1]);
}
void CAsyncDialogBox::kill() {
if (m_dialogPid <= 0)
return;
::kill(m_dialogPid, SIGKILL);
}
bool CAsyncDialogBox::isRunning() const {
return m_readEventSource;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include "../macros.hpp"
#include "./memory/Memory.hpp"
#include <vector>
#include <functional>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
struct wl_event_source;
class CAsyncDialogBox {
public:
static SP<CAsyncDialogBox> create(const std::string& title, const std::string& description, std::vector<std::string> buttons);
CAsyncDialogBox(const CAsyncDialogBox&) = delete;
CAsyncDialogBox(CAsyncDialogBox&&) = delete;
CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete;
CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete;
void open(std::function<void(std::string)> onResolution);
void kill();
bool isRunning() const;
void onWrite(int fd, uint32_t mask);
private:
CAsyncDialogBox(const std::string& title, const std::string& description, std::vector<std::string> buttons);
pid_t m_dialogPid = 0;
wl_event_source* m_readEventSource = nullptr;
std::function<void(std::string)> m_onResolution;
Hyprutils::OS::CFileDescriptor m_pipeReadFd;
std::string m_stdout = "";
const std::string m_title;
const std::string m_description;
const std::vector<std::string> m_buttons;
// WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers
SP<CAsyncDialogBox> m_selfReference;
WP<CAsyncDialogBox> m_selfWeakReference;
};

View File

@@ -881,7 +881,7 @@ bool CMonitor::isMirror() {
bool CMonitor::matchesStaticSelector(const std::string& selector) const {
if (selector.starts_with("desc:")) {
// match by description
const auto DESCRIPTIONSELECTOR = selector.substr(5);
const auto DESCRIPTIONSELECTOR = trim(selector.substr(5));
return szDescription.starts_with(DESCRIPTIONSELECTOR) || szShortDescription.starts_with(DESCRIPTIONSELECTOR);
} else {
@@ -1348,13 +1348,13 @@ bool CMonitor::attemptDirectScanout() {
return false;
// we can't scanout shm buffers.
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
const auto params = PSURFACE->current.buffer->dmabuf();
if (!params.success || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
return false;
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get());
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer.buffer.get());
auto PBUFFER = PSURFACE->current.buffer->buffer;
auto PBUFFER = PSURFACE->current.buffer.buffer;
if (PBUFFER == output->state->state().buffer) {
if (scanoutNeedsCursorUpdate) {
@@ -1407,10 +1407,10 @@ bool CMonitor::attemptDirectScanout() {
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.buffer->acquire && explicitOptions.explicitKMSEnabled;
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.acquire && explicitOptions.explicitKMSEnabled;
if (DOEXPLICIT) {
// wait for surface's explicit fence if present
inFence = PSURFACE->current.buffer->acquire->exportAsFD();
inFence = PSURFACE->current.acquire.exportAsFD();
if (inFence.isValid()) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get());
output->state->setExplicitInFence(inFence.get());

View File

@@ -1,58 +0,0 @@
#include "Watchdog.hpp"
#include <csignal>
#include "config/ConfigManager.hpp"
#include "../config/ConfigValue.hpp"
CWatchdog::~CWatchdog() {
m_bExitThread = true;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
if (m_pWatchdog && m_pWatchdog->joinable())
m_pWatchdog->join();
}
CWatchdog::CWatchdog() : m_iMainThreadPID(pthread_self()) {
m_pWatchdog = makeUnique<std::thread>([this] {
static auto PTIMEOUT = CConfigValue<Hyprlang::INT>("debug:watchdog_timeout");
m_bWatchdogInitialized = true;
while (!m_bExitThread) {
std::unique_lock<std::mutex> lk(m_mWatchdogMutex);
if (!m_bWillWatch)
m_cvWatchdogCondition.wait(lk, [this] { return m_bNotified || m_bExitThread; });
else if (!m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(*PTIMEOUT * 1000.0)), [this] { return m_bNotified || m_bExitThread; }))
pthread_kill(m_iMainThreadPID, SIGUSR1);
if (m_bExitThread)
break;
m_bWatching = false;
m_bNotified = false;
}
});
}
void CWatchdog::startWatching() {
static auto PTIMEOUT = CConfigValue<Hyprlang::INT>("debug:watchdog_timeout");
if (*PTIMEOUT == 0)
return;
m_tTriggered = std::chrono::high_resolution_clock::now();
m_bWillWatch = true;
m_bWatching = true;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
}
void CWatchdog::endWatching() {
m_bWatching = false;
m_bWillWatch = false;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include "memory/Memory.hpp"
#include <chrono>
#include <thread>
#include <condition_variable>
class CWatchdog {
public:
// must be called from the main thread
CWatchdog();
~CWatchdog();
void startWatching();
void endWatching();
std::atomic<bool> m_bWatchdogInitialized{false};
private:
std::chrono::high_resolution_clock::time_point m_tTriggered;
pthread_t m_iMainThreadPID = 0;
std::atomic<bool> m_bWatching = false;
std::atomic<bool> m_bWillWatch = false;
UP<std::thread> m_pWatchdog;
std::mutex m_mWatchdogMutex;
std::atomic<bool> m_bNotified = false;
std::atomic<bool> m_bExitThread = false;
std::condition_variable m_cvWatchdogCondition;
};
inline UP<CWatchdog> g_pWatchdog;

View File

@@ -91,28 +91,28 @@ static int handleWaiterFD(int fd, uint32_t mask, void* data) {
}
bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags) {
auto w = makeShared<SWaiter>();
w->fn = waiter;
w->timeline = self;
w->eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
CFileDescriptor eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
if (!w->eventFd.isValid()) {
if (!eventFd.isValid()) {
Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
return false;
}
drm_syncobj_eventfd syncobjEventFD = {
.handle = handle,
.flags = flags,
.point = point,
.fd = w->eventFd.get(),
};
if (drmIoctl(drmFD, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobjEventFD) != 0) {
Debug::log(ERR, "CSyncTimeline::addWaiter: drmIoctl failed");
if (drmSyncobjEventfd(drmFD, handle, point, eventFd.get(), flags)) {
Debug::log(ERR, "CSyncTimeline::addWaiter: drmSyncobjEventfd failed");
return false;
}
if (eventFd.isReadable()) {
waiter();
return true;
}
auto w = makeShared<SWaiter>();
w->fn = waiter;
w->timeline = self;
w->eventFd = std::move(eventFd);
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, w->eventFd.get(), WL_EVENT_READABLE, ::handleWaiterFD, w.get());
if (!w->source) {
Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed");

View File

@@ -240,7 +240,8 @@ void IHyprLayout::onBeginDragWindow() {
return;
}
if (DRAGGINGWINDOW->isFullscreen()) {
const bool WAS_FULLSCREEN = DRAGGINGWINDOW->isFullscreen();
if (WAS_FULLSCREEN) {
Debug::log(LOG, "Dragging a fullscreen window");
g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
}
@@ -257,7 +258,10 @@ void IHyprLayout::onBeginDragWindow() {
m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize;
if (!DRAGGINGWINDOW->m_bIsFloating) {
if (WAS_FULLSCREEN && DRAGGINGWINDOW->m_bIsFloating) {
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
*DRAGGINGWINDOW->m_vRealPosition = MOUSECOORDS - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
} else if (!DRAGGINGWINDOW->m_bIsFloating) {
if (g_pInputManager->dragMode == MBIND_MOVE) {
DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor();
changeWindowFloatingMode(DRAGGINGWINDOW);
@@ -614,6 +618,8 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
DRAGGINGWINDOW->sendWindowSize();
}
DRAGGINGWINDOW->m_vPosition = wb.pos();
} else if (g_pInputManager->dragMode == MBIND_RESIZE || g_pInputManager->dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->dragMode == MBIND_RESIZE_BLOCK_RATIO) {
if (DRAGGINGWINDOW->m_bIsFloating) {
@@ -685,6 +691,9 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
DRAGGINGWINDOW->m_vRealPosition->setValueAndWarp(wb.pos());
DRAGGINGWINDOW->sendWindowSize();
}
DRAGGINGWINDOW->m_vPosition = wb.pos();
DRAGGINGWINDOW->m_vSize = wb.size();
} else {
resizeActiveWindow(TICKDELTA, m_eGrabbedCorner, DRAGGINGWINDOW);
}
@@ -776,8 +785,8 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
*pWindow->m_vRealPosition = wb.pos();
*pWindow->m_vRealSize = wb.size();
pWindow->m_vSize = wb.pos();
pWindow->m_vPosition = wb.size();
pWindow->m_vSize = wb.size();
pWindow->m_vPosition = wb.pos();
g_pHyprRenderer->damageMonitor(pWindow->m_pMonitor.lock());
@@ -806,6 +815,7 @@ void IHyprLayout::moveActiveWindow(const Vector2D& delta, PHLWINDOW pWindow) {
PWINDOW->setAnimationsToMove();
PWINDOW->m_vPosition += delta;
*PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition->goal() + delta;
g_pHyprRenderer->damageWindow(PWINDOW);

View File

@@ -39,9 +39,8 @@ CANRManager::CANRManager() {
}
void CANRManager::onTick() {
std::erase_if(m_data, [](const auto& e) { return e->isDefunct(); });
static auto PENABLEANR = CConfigValue<Hyprlang::INT>("misc:enable_anr_dialog");
static auto PENABLEANR = CConfigValue<Hyprlang::INT>("misc:enable_anr_dialog");
static auto PANRTHRESHOLD = CConfigValue<Hyprlang::INT>("misc:anr_missed_pings");
if (!*PENABLEANR) {
m_timer->updateTimeout(TIMER_TIMEOUT * 10);
@@ -66,8 +65,8 @@ void CANRManager::onTick() {
if (count == 0)
continue;
if (data->missedResponses > 0) {
if (!data->isThreadRunning() && !data->dialogThreadSaidWait) {
if (data->missedResponses >= *PANRTHRESHOLD) {
if (!data->isRunning() && !data->dialogSaidWait) {
data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, data->getPid());
for (const auto& w : g_pCompositor->m_vWindows) {
@@ -80,11 +79,11 @@ void CANRManager::onTick() {
*w->m_notRespondingTint = 0.2F;
}
}
} else if (data->isThreadRunning())
} else if (data->isRunning())
data->killDialog();
if (data->missedResponses == 0)
data->dialogThreadSaidWait = false;
data->dialogSaidWait = false;
data->missedResponses++;
@@ -114,7 +113,7 @@ void CANRManager::onResponse(SP<CXWaylandSurface> pXwaylandSurface) {
void CANRManager::onResponse(SP<CANRManager::SANRData> data) {
data->missedResponses = 0;
if (data->isThreadRunning())
if (data->isRunning())
data->killDialog();
}
@@ -128,7 +127,8 @@ bool CANRManager::isNotResponding(PHLWINDOW pWindow) {
}
bool CANRManager::isNotResponding(SP<CANRManager::SANRData> data) {
return data->missedResponses > 1;
static auto PANRTHRESHOLD = CConfigValue<Hyprlang::INT>("misc:anr_missed_pings");
return data->missedResponses > *PANRTHRESHOLD;
}
SP<CANRManager::SANRData> CANRManager::dataFor(PHLWINDOW pWindow) {
@@ -156,63 +156,39 @@ CANRManager::SANRData::SANRData(PHLWINDOW pWindow) :
}
CANRManager::SANRData::~SANRData() {
if (dialogThread.joinable()) {
if (dialogBox && dialogBox->isRunning())
killDialog();
// dangerous: might lock if the above failed!!
dialogThread.join();
}
}
void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) {
if (!dialogThreadExited)
if (dialogBox && dialogBox->isRunning())
killDialog();
// dangerous: might lock if the above failed!!
if (dialogThread.joinable())
dialogThread.join();
dialogBox = CAsyncDialogBox::create(title,
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?", appName.empty() ? "unknown" : appName,
appClass.empty() ? "unknown" : appClass),
std::vector<std::string>{"Terminate", "Wait"});
dialogThreadExited = false;
dialogThreadSaidWait = false;
dialogThread = std::thread([title, appName, appClass, dialogWmPID, this]() {
SP<CProcess> proc = makeShared<CProcess>("hyprland-dialog",
std::vector<std::string>{"--title", title, "--text",
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?",
appName.empty() ? "unknown" : appName, appClass.empty() ? "unknown" : appClass),
"--buttons", "Terminate;Wait"});
dialogProc = proc;
proc->runSync();
dialogThreadExited = true;
if (proc->stdOut().empty())
return;
if (proc->stdOut().starts_with("Terminate"))
kill(dialogWmPID, SIGKILL);
if (proc->stdOut().starts_with("Wait"))
dialogThreadSaidWait = true;
dialogBox->open([dialogWmPID, this](std::string result) {
if (result.starts_with("Terminate"))
::kill(dialogWmPID, SIGKILL);
else if (result.starts_with("Wait"))
dialogSaidWait = true;
else
Debug::log(ERR, "CANRManager::SANRData::runDialog: lambda: unrecognized result: {}", result);
});
}
bool CANRManager::SANRData::isThreadRunning() {
if (dialogThread.native_handle() == 0)
return false;
if (dialogThreadExited)
return false;
return pthread_kill(dialogThread.native_handle(), 0) != ESRCH;
bool CANRManager::SANRData::isRunning() {
return dialogBox && dialogBox->isRunning();
}
void CANRManager::SANRData::killDialog() const {
if (!dialogProc)
void CANRManager::SANRData::killDialog() {
if (!dialogBox)
return;
if (!dialogProc->pid()) {
Debug::log(ERR, "ANR: cannot kill dialogProc, as it doesn't have a pid. If you have hyprutils <= 0.6.0, you will crash soon. Otherwise, dialog failed to spawn??");
return;
}
kill(dialogProc->pid(), SIGKILL);
dialogBox->kill();
dialogBox = nullptr;
}
bool CANRManager::SANRData::fitsWindow(PHLWINDOW pWindow) const {

View File

@@ -7,8 +7,7 @@
#include <hyprutils/os/FileDescriptor.hpp>
#include "./eventLoop/EventLoopTimer.hpp"
#include "../helpers/signal/Signal.hpp"
#include <atomic>
#include <thread>
#include "../helpers/AsyncDialogBox.hpp"
#include <vector>
class CXDGWMBase;
@@ -32,22 +31,21 @@ class CANRManager {
SANRData(PHLWINDOW pWindow);
~SANRData();
WP<CXWaylandSurface> xwaylandSurface;
WP<CXDGWMBase> xdgBase;
WP<CXWaylandSurface> xwaylandSurface;
WP<CXDGWMBase> xdgBase;
int missedResponses = 0;
std::thread dialogThread;
SP<Hyprutils::OS::CProcess> dialogProc;
std::atomic<bool> dialogThreadExited = false;
std::atomic<bool> dialogThreadSaidWait = false;
int missedResponses = 0;
void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID);
bool isThreadRunning();
void killDialog() const;
bool isDefunct() const;
bool fitsWindow(PHLWINDOW pWindow) const;
pid_t getPid() const;
void ping();
bool dialogSaidWait = false;
SP<CAsyncDialogBox> dialogBox;
void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID);
bool isRunning();
void killDialog();
bool isDefunct() const;
bool fitsWindow(PHLWINDOW pWindow) const;
pid_t getPid() const;
void ping();
};
void onResponse(SP<SANRData> data);

View File

@@ -18,12 +18,16 @@ static void hcLogger(enum eHyprcursorLogLevel level, char* message) {
Debug::log(NONE, "[hc] {}", message);
}
CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_), surface(surf), stride(cairo_image_surface_get_stride(surf)) {
CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : m_hotspot(hot_), m_stride(cairo_image_surface_get_stride(surf)) {
size = size_;
m_data = std::vector<uint8_t>((uint8_t*)cairo_image_surface_get_data(surf), ((uint8_t*)cairo_image_surface_get_data(surf)) + (cairo_image_surface_get_height(surf) * m_stride));
}
CCursorBuffer::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_), pixelData(pixelData_), stride(4 * size_.x) {
CCursorBuffer::CCursorBuffer(const uint8_t* pixelData, const Vector2D& size_, const Vector2D& hot_) : m_hotspot(hot_), m_stride(4 * size_.x) {
size = size_;
m_data = std::vector<uint8_t>(pixelData, pixelData + ((int)size_.y * m_stride));
}
Aquamarine::eBufferCapability CCursorBuffer::caps() {
@@ -51,12 +55,12 @@ Aquamarine::SSHMAttrs CCursorBuffer::shm() {
attrs.success = true;
attrs.format = DRM_FORMAT_ARGB8888;
attrs.size = size;
attrs.stride = stride;
attrs.stride = m_stride;
return attrs;
}
std::tuple<uint8_t*, uint32_t, size_t> CCursorBuffer::beginDataPtr(uint32_t flags) {
return {pixelData ? pixelData : cairo_image_surface_get_data(surface), DRM_FORMAT_ARGB8888, stride};
return {m_data.data(), DRM_FORMAT_ARGB8888, m_stride};
}
void CCursorBuffer::endDataPtr() {
@@ -302,8 +306,6 @@ void CCursorManager::updateTheme() {
m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo);
}
setCursorFromName("left_ptr");
for (auto const& m : g_pCompositor->m_vMonitors) {
m->forceFullFrames = 5;
g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);

View File

@@ -17,7 +17,7 @@ AQUAMARINE_FORWARD(IBuffer);
class CCursorBuffer : public Aquamarine::IBuffer {
public:
CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
CCursorBuffer(const uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
~CCursorBuffer() = default;
virtual Aquamarine::eBufferCapability caps();
@@ -30,10 +30,9 @@ class CCursorBuffer : public Aquamarine::IBuffer {
virtual void endDataPtr();
private:
Vector2D hotspot;
cairo_surface_t* surface = nullptr;
uint8_t* pixelData = nullptr;
size_t stride = 0;
Vector2D m_hotspot;
std::vector<uint8_t> m_data;
size_t m_stride = 0;
};
class CCursorManager {

View File

@@ -117,6 +117,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["submap"] = setSubmap;
m_mDispatchers["pass"] = pass;
m_mDispatchers["sendshortcut"] = sendshortcut;
m_mDispatchers["sendkeystate"] = sendkeystate;
m_mDispatchers["layoutmsg"] = layoutmsg;
m_mDispatchers["dpms"] = dpms;
m_mDispatchers["movewindowpixel"] = moveWindow;
@@ -1214,9 +1215,10 @@ static SWorkspaceIDName getWorkspaceToChangeFromArgs(std::string args, PHLWORKSP
SDispatchResult CKeybindManager::changeworkspace(std::string args) {
// Workspace_back_and_forth being enabled means that an attempt to switch to
// the current workspace will instead switch to the previous.
static auto PBACKANDFORTH = CConfigValue<Hyprlang::INT>("binds:workspace_back_and_forth");
static auto PALLOWWORKSPACECYCLES = CConfigValue<Hyprlang::INT>("binds:allow_workspace_cycles");
static auto PWORKSPACECENTERON = CConfigValue<Hyprlang::INT>("binds:workspace_center_on");
static auto PBACKANDFORTH = CConfigValue<Hyprlang::INT>("binds:workspace_back_and_forth");
static auto PALLOWWORKSPACECYCLES = CConfigValue<Hyprlang::INT>("binds:allow_workspace_cycles");
static auto PWORKSPACECENTERON = CConfigValue<Hyprlang::INT>("binds:workspace_center_on");
static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue<Hyprlang::INT>("binds:hide_special_on_workspace_change");
const auto PMONITOR = g_pCompositor->m_pLastMonitor.lock();
@@ -1238,8 +1240,12 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) {
const SWorkspaceIDName PPREVWS = args.contains("_per_monitor") ? PMONITOR->getPrevWorkspaceIDName(PCURRENTWORKSPACE->m_iID) : PCURRENTWORKSPACE->getPrevWorkspaceIDName();
const bool BISWORKSPACECURRENT = workspaceToChangeTo == PCURRENTWORKSPACE->m_iID;
if (BISWORKSPACECURRENT && (!(*PBACKANDFORTH || EXPLICITPREVIOUS) || PPREVWS.id == -1))
if (BISWORKSPACECURRENT && (!(*PBACKANDFORTH || EXPLICITPREVIOUS) || PPREVWS.id == -1)) {
if (*PHIDESPECIALONWORKSPACECHANGE)
PMONITOR->setSpecialWorkspace(nullptr);
return {.success = false, .error = "Previous workspace doesn't exist"};
}
g_pInputManager->unconstrainMouse();
g_pInputManager->m_bEmptyFocusCursorSet = false;
@@ -1274,6 +1280,8 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) {
} else
pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE);
if (*PHIDESPECIALONWORKSPACECHANGE)
PMONITORWORKSPACEOWNER->setSpecialWorkspace(nullptr);
PMONITORWORKSPACEOWNER->changeWorkspace(pWorkspaceToChangeTo, false, true);
if (PMONITOR != PMONITORWORKSPACEOWNER) {
@@ -2089,10 +2097,16 @@ SDispatchResult CKeybindManager::toggleSpecialWorkspace(std::string args) {
}
}
updateRelativeCursorCoords();
PHLWORKSPACEREF focusedWorkspace;
if (requestedWorkspaceIsAlreadyOpen && specialOpenOnMonitor == workspaceID) {
// already open on this monitor
Debug::log(LOG, "Toggling special workspace {} to closed", workspaceID);
PMONITOR->setSpecialWorkspace(nullptr);
focusedWorkspace = PMONITOR->activeWorkspace;
} else {
Debug::log(LOG, "Toggling special workspace {} to open", workspaceID);
auto PSPECIALWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID);
@@ -2101,6 +2115,18 @@ SDispatchResult CKeybindManager::toggleSpecialWorkspace(std::string args) {
PSPECIALWORKSPACE = g_pCompositor->createNewWorkspace(workspaceID, PMONITOR->ID, workspaceName);
PMONITOR->setSpecialWorkspace(PSPECIALWORKSPACE);
focusedWorkspace = PSPECIALWORKSPACE;
}
const static auto PWARPONTOGGLESPECIAL = CConfigValue<Hyprlang::INT>("cursor:warp_on_toggle_special");
if (*PWARPONTOGGLESPECIAL > 0) {
auto PLAST = focusedWorkspace->getLastFocusedWindow();
auto HLSurface = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock());
if (PLAST && (!HLSurface || HLSurface->getWindow()))
PLAST->warpCursor(*PWARPONTOGGLESPECIAL == 2);
}
return {};
@@ -3215,3 +3241,45 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
return {};
}
SDispatchResult CKeybindManager::sendkeystate(std::string args) {
// args=<NEW_MODKEYS><NEW_KEY><STATE>[,WINDOW_RULES]
const auto ARGS = CVarList(args, 4);
if (ARGS.size() != 4) {
Debug::log(ERR, "sendkeystate: invalid args");
return {.success = false, .error = "sendkeystate: invalid args"};
}
const auto STATE = ARGS[2];
if (STATE != "down" && STATE != "repeat" && STATE != "up") {
Debug::log(ERR, "sendkeystate: invalid state, must be 'down', 'repeat', or 'up'");
return {.success = false, .error = "sendkeystate: invalid state, must be 'down', 'repeat', or 'up'"};
}
std::string modifiedArgs = ARGS[0] + "," + ARGS[1] + "," + ARGS[3];
const int oldPassPressed = g_pKeybindManager->m_iPassPressed;
if (STATE == "down")
g_pKeybindManager->m_iPassPressed = 1;
else if (STATE == "up")
g_pKeybindManager->m_iPassPressed = 0;
else if (STATE == "repeat")
g_pKeybindManager->m_iPassPressed = 1;
auto result = sendshortcut(modifiedArgs);
if (STATE == "repeat" && result.success)
result = sendshortcut(modifiedArgs);
g_pKeybindManager->m_iPassPressed = oldPassPressed;
if (!result.success && !result.error.empty()) {
size_t pos = result.error.find("sendshortcut:");
if (pos != std::string::npos)
result.error = "sendkeystate:" + result.error.substr(pos + 13);
}
return result;
}

View File

@@ -203,6 +203,7 @@ class CKeybindManager {
static SDispatchResult setSubmap(std::string);
static SDispatchResult pass(std::string);
static SDispatchResult sendshortcut(std::string);
static SDispatchResult sendkeystate(std::string);
static SDispatchResult layoutmsg(std::string);
static SDispatchResult dpms(std::string);
static SDispatchResult swapnext(std::string);

View File

@@ -621,14 +621,10 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* no
box.y = std::round(box.y);
CTexPassElement::SRenderData data;
data.tex = texture;
data.box = box.round();
data.syncTimeline = currentCursorImage.waitTimeline;
data.syncPoint = currentCursorImage.waitPoint;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
data.tex = texture;
data.box = box.round();
currentCursorImage.waitTimeline.reset();
currentCursorImage.waitPoint = 0;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);

View File

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

View File

@@ -26,10 +26,12 @@ CVersionKeeperManager::CVersionKeeperManager() {
if (!DATAROOT)
return;
const auto LASTVER = NFsUtils::readFileAsString(*DATAROOT + "/" + VERSION_FILE_NAME);
auto LASTVER = NFsUtils::readFileAsString(*DATAROOT + "/" + VERSION_FILE_NAME);
if (!LASTVER)
return;
if (!LASTVER) {
NFsUtils::writeToFile(*DATAROOT + "/" + VERSION_FILE_NAME, "0.0.0");
LASTVER = "0.0.0";
}
if (!isVersionOlderThanRunning(*LASTVER)) {
Debug::log(LOG, "CVersionKeeperManager: Read version {} matches or is older than running.", *LASTVER);

View File

@@ -123,8 +123,10 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) {
(SIZEHINTS && (SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height)))
return true;
} else {
const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current;
if (!pWindow->m_pXDGSurface || !pWindow->m_pXDGSurface->toplevel)
return false;
const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current;
if (pWindow->m_pXDGSurface->toplevel->parent ||
(PSTATE->minSize.x != 0 && PSTATE->minSize.y != 0 && (PSTATE->minSize.x == PSTATE->maxSize.x || PSTATE->minSize.y == PSTATE->maxSize.y)))
return true;

View File

@@ -68,6 +68,7 @@ class CEventLoopManager {
wl_event_source* m_configWatcherInotifySource = nullptr;
friend class CSyncTimeline;
friend class CAsyncDialogBox;
};
inline UP<CEventLoopManager> g_pEventLoopManager;

View File

@@ -306,6 +306,17 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal());
// forced above all
if (!g_pInputManager->m_dExclusiveLSes.empty()) {
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &g_pInputManager->m_dExclusiveLSes, &surfaceCoords, &pFoundLayerSurface);
if (!foundSurface) {
foundSurface = (*g_pInputManager->m_dExclusiveLSes.begin())->surface->resource();
surfacePos = (*g_pInputManager->m_dExclusiveLSes.begin())->realPosition->goal();
}
}
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerPopupSurface(mouseCoords, PMONITOR, &surfaceCoords, &pFoundLayerSurface);
@@ -1420,10 +1431,15 @@ void CInputManager::refocus() {
mouseMoveUnified(0, true);
}
void CInputManager::refocusLastWindow(PHLMONITOR pMonitor) {
bool CInputManager::refocusLastWindow(PHLMONITOR pMonitor) {
if (!m_dExclusiveLSes.empty()) {
Debug::log(LOG, "CInputManager::refocusLastWindow: ignoring, exclusive LS present.");
return false;
}
if (!pMonitor) {
refocus();
return;
return true;
}
Vector2D surfaceCoords;
@@ -1432,10 +1448,6 @@ void CInputManager::refocusLastWindow(PHLMONITOR pMonitor) {
g_pInputManager->releaseAllMouseButtons();
// first try for an exclusive layer
if (!m_dExclusiveLSes.empty())
foundSurface = m_dExclusiveLSes[m_dExclusiveLSes.size() - 1]->surface->resource();
// then any surfaces above windows on the same monitor
if (!foundSurface) {
foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
@@ -1465,6 +1477,8 @@ void CInputManager::refocusLastWindow(PHLMONITOR pMonitor) {
refocus();
}
return true;
}
void CInputManager::unconstrainMouse() {

View File

@@ -114,7 +114,7 @@ class CInputManager {
Vector2D getMouseCoordsInternal();
void refocus();
void refocusLastWindow(PHLMONITOR pMonitor);
bool refocusLastWindow(PHLMONITOR pMonitor);
void simulateMouseMovement();
void sendMotionEventsToFocused();

View File

@@ -71,19 +71,8 @@ void CInputManager::endWorkspaceSwipe() {
// If we've been swiping off the right end with PSWIPENEW enabled, there is
// no workspace there yet, and we need to choose an ID for a new one now.
// With multiple monitors, it might not be appropriate to choose one more
// than the ID of the workspace we're swiping from, because that ID might
// just be on another monitor. It's also not just the smallest unused ID,
// because that could be a gap in the existing workspace numbers, and it'd
// be counterintuitive to swipe rightwards onto a new workspace and end up
// left of where we started. Instead, it's one more than the greatest
// workspace ID that currently exists.
if (workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID && *PSWIPENEW) {
WORKSPACEID maxWorkspace = 0;
for (const auto& ws : g_pCompositor->m_vWorkspaces) {
maxWorkspace = std::max(maxWorkspace, ws->m_iID);
}
workspaceIDRight = maxWorkspace + 1;
workspaceIDRight = getWorkspaceIDNameFromString("r+1").id;
}
auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER
@@ -135,6 +124,7 @@ void CInputManager::endWorkspaceSwipe() {
else {
m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_sActiveSwipe.pMonitor->ID));
PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
PWORKSPACEL->rememberPrevWorkspace(m_sActiveSwipe.pWorkspaceBegin);
}
PWORKSPACEL->m_vRenderOffset->setValue(RENDEROFFSET);
@@ -161,6 +151,7 @@ void CInputManager::endWorkspaceSwipe() {
else {
m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_sActiveSwipe.pMonitor->ID));
PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
PWORKSPACER->rememberPrevWorkspace(m_sActiveSwipe.pWorkspaceBegin);
}
PWORKSPACER->m_vRenderOffset->setValue(RENDEROFFSET);

View File

@@ -0,0 +1,234 @@
#include <re2/re2.h>
#include "DynamicPermissionManager.hpp"
#include <algorithm>
#include <wayland-server-core.h>
#include <expected>
#include <filesystem>
#include "../../Compositor.hpp"
#include "../../config/ConfigValue.hpp"
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/sysctl.h>
#endif
static void clientDestroyInternal(struct wl_listener* listener, void* data) {
SDynamicPermissionRuleDestroyWrapper* wrap = wl_container_of(listener, wrap, listener);
CDynamicPermissionRule* rule = wrap->parent;
g_pDynamicPermissionManager->removeRulesForClient(rule->client());
}
CDynamicPermissionRule::CDynamicPermissionRule(const std::string& binaryPathRegex, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode) :
m_type(type), m_source(PERMISSION_RULE_SOURCE_CONFIG), m_binaryRegex(makeUnique<re2::RE2>(binaryPathRegex)), m_allowMode(defaultAllowMode) {
;
}
CDynamicPermissionRule::CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode) :
m_type(type), m_source(PERMISSION_RULE_SOURCE_RUNTIME_USER), m_client(client), m_allowMode(defaultAllowMode) {
wl_list_init(&m_destroyWrapper.listener.link);
m_destroyWrapper.listener.notify = ::clientDestroyInternal;
m_destroyWrapper.parent = this;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_destroyWrapper.listener);
}
CDynamicPermissionRule::~CDynamicPermissionRule() {
if (m_client) {
wl_list_remove(&m_destroyWrapper.listener.link);
wl_list_init(&m_destroyWrapper.listener.link);
}
if (m_dialogBox && m_dialogBox->isRunning())
m_dialogBox->kill();
}
wl_client* CDynamicPermissionRule::client() const {
return m_client;
}
static const char* permissionToString(eDynamicPermissionType type) {
switch (type) {
case PERMISSION_TYPE_UNKNOWN: return "PERMISSION_TYPE_UNKNOWN";
case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY";
}
return "error";
}
static const char* permissionToHumanString(eDynamicPermissionType type) {
switch (type) {
case PERMISSION_TYPE_UNKNOWN: return "requesting an unknown permission";
case PERMISSION_TYPE_SCREENCOPY: return "trying to capture your screen";
}
return "error";
}
static std::expected<std::string, std::string> binaryNameForWlClient(wl_client* client) {
pid_t pid = 0;
wl_client_get_credentials(client, &pid, nullptr, nullptr);
if (pid <= 0)
return std::unexpected("No pid for client");
#if defined(KERN_PROC_PATHNAME)
int mib[] = {
CTL_KERN,
#if defined(__NetBSD__)
KERN_PROC_ARGS,
pid,
KERN_PROC_PATHNAME,
#else
KERN_PROC,
KERN_PROC_PATHNAME,
pid,
#endif
};
u_int miblen = sizeof(mib) / sizeof(mib[0]);
char exe[PATH_MAX] = "/nonexistent";
size_t sz = sizeof(exe);
sysctl(mib, miblen, &exe, &sz, NULL, 0);
std::string path = exe;
#else
std::string path = std::format("/proc/{}/exe", (uint64_t)pid);
#endif
std::error_code ec;
std::string fullPath = std::filesystem::canonical(path, ec);
if (ec)
return std::unexpected("canonical failed");
return fullPath;
}
void CDynamicPermissionManager::clearConfigPermissions() {
std::erase_if(m_rules, [](const auto& e) { return e->m_source == PERMISSION_RULE_SOURCE_CONFIG; });
}
void CDynamicPermissionManager::addConfigPermissionRule(const std::string& binaryName, eDynamicPermissionType type, eDynamicPermissionAllowMode mode) {
m_rules.emplace_back(SP<CDynamicPermissionRule>(new CDynamicPermissionRule(binaryName, type, mode)));
}
eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionMode(wl_client* client, eDynamicPermissionType permission) {
static auto PPERM = CConfigValue<Hyprlang::INT>("ecosystem:enforce_permissions");
if (*PPERM == 0)
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
const auto LOOKUP = binaryNameForWlClient(client);
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: checking permission {} for client {:x} (binary {})", permissionToString(permission), (uintptr_t)client,
LOOKUP.has_value() ? LOOKUP.value() : "lookup failed: " + LOOKUP.error());
// first, check if we have the client + perm combo in our cache.
auto it = std::ranges::find_if(m_rules, [client, permission](const auto& e) { return e->m_client == client && e->m_type == permission; });
if (it == m_rules.end()) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission not cached, checking binary name");
if (!LOOKUP.has_value())
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: binary name check failed");
else {
const auto BINNAME = LOOKUP.value().contains("/") ? LOOKUP.value().substr(LOOKUP.value().find_last_of('/') + 1) : LOOKUP.value();
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: binary path {}, name {}", LOOKUP.value(), BINNAME);
it = std::ranges::find_if(m_rules, [clientBinaryPath = LOOKUP.value(), permission](const auto& e) {
if (e->m_type != permission)
return false; // wrong perm
if (!e->m_binaryPath.empty() && e->m_binaryPath == clientBinaryPath)
return true; // matches binary path
if (!e->m_binaryRegex)
return false; // wl_client* rule
// regex match
if (RE2::FullMatch(clientBinaryPath, *e->m_binaryRegex))
return true;
return false;
});
if (it == m_rules.end())
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: no rule for binary");
else {
if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed by config rule");
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied by config rule");
return PERMISSION_RULE_ALLOW_MODE_DENY;
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending by config rule");
return PERMISSION_RULE_ALLOW_MODE_PENDING;
} else
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission ask by config rule");
}
}
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed before by user");
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied before by user");
return PERMISSION_RULE_ALLOW_MODE_DENY;
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) {
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending before by user");
return PERMISSION_RULE_ALLOW_MODE_PENDING;
}
// if we are here, we need to ask.
askForPermission(client, LOOKUP.value_or(""), permission);
return PERMISSION_RULE_ALLOW_MODE_PENDING;
}
void CDynamicPermissionManager::askForPermission(wl_client* client, const std::string& binaryPath, eDynamicPermissionType type) {
auto rule = m_rules.emplace_back(SP<CDynamicPermissionRule>(new CDynamicPermissionRule(client, type, PERMISSION_RULE_ALLOW_MODE_PENDING)));
std::string description = "";
if (binaryPath.empty())
description = std::format("An unknown application (wayland client ID 0x{:x}) is {}.", (uintptr_t)client, permissionToHumanString(type));
else {
std::string binaryName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath;
description = std::format("An application <b>{}</b> ({}) is {}.", binaryName, binaryPath, permissionToHumanString(type));
}
description += "<br/><br/>Do you want to allow this?";
std::vector<std::string> options;
if (!binaryPath.empty()) {
description += "<br/><br/><i>Hint: you can set persistent rules for these in the Hyprland config file.</i>";
options = {"Deny", "Allow and remember app", "Allow once"};
} else
options = {"Deny", "Allow"};
rule->m_dialogBox = CAsyncDialogBox::create("Permission request", description, options);
if (!rule->m_dialogBox) {
Debug::log(ERR, "CDynamicPermissionManager::askForPermission: hyprland-qtutils likely missing, cannot ask! Disabling permission control...");
rule->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
return;
}
rule->m_dialogBox->open([r = WP<CDynamicPermissionRule>(rule), binaryPath](std::string result) {
if (!r)
return;
Debug::log(TRACE, "CDynamicPermissionRule: user returned {}", result);
if (result.starts_with("Allow once"))
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
else if (result.starts_with("Deny")) {
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_DENY;
r->m_binaryPath = binaryPath;
} else if (result.starts_with("Allow and remember")) {
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
r->m_binaryPath = binaryPath;
} else if (result.starts_with("Allow"))
r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW;
});
}
void CDynamicPermissionManager::removeRulesForClient(wl_client* client) {
std::erase_if(m_rules, [client](const auto& e) { return e->m_client == client; });
}

View File

@@ -0,0 +1,85 @@
#pragma once
#include "../../macros.hpp"
#include "../../helpers/memory/Memory.hpp"
#include "../../helpers/AsyncDialogBox.hpp"
#include <vector>
#include <wayland-server-core.h>
#include <optional>
// NOLINTNEXTLINE
namespace re2 {
class RE2;
};
enum eDynamicPermissionType : uint8_t {
PERMISSION_TYPE_UNKNOWN = 0,
PERMISSION_TYPE_SCREENCOPY,
};
enum eDynamicPermissionRuleSource : uint8_t {
PERMISSION_RULE_SOURCE_UNKNOWN = 0,
PERMISSION_RULE_SOURCE_CONFIG,
PERMISSION_RULE_SOURCE_RUNTIME_USER,
};
enum eDynamicPermissionAllowMode : uint8_t {
PERMISSION_RULE_ALLOW_MODE_UNKNOWN = 0,
PERMISSION_RULE_ALLOW_MODE_DENY,
PERMISSION_RULE_ALLOW_MODE_ASK,
PERMISSION_RULE_ALLOW_MODE_ALLOW,
PERMISSION_RULE_ALLOW_MODE_PENDING, // popup is open
};
class CDynamicPermissionRule;
struct SDynamicPermissionRuleDestroyWrapper {
wl_listener listener;
CDynamicPermissionRule* parent = nullptr;
};
class CDynamicPermissionRule {
public:
~CDynamicPermissionRule();
wl_client* client() const;
private:
// config rule
CDynamicPermissionRule(const std::string& binaryPathRegex, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK);
// user rule
CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK);
const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN;
const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN;
wl_client* const m_client = nullptr;
std::string m_binaryPath = "";
UP<re2::RE2> m_binaryRegex;
eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
SP<CAsyncDialogBox> m_dialogBox; // for pending
SDynamicPermissionRuleDestroyWrapper m_destroyWrapper;
friend class CDynamicPermissionManager;
};
class CDynamicPermissionManager {
public:
void clearConfigPermissions();
void addConfigPermissionRule(const std::string& binaryPath, eDynamicPermissionType type, eDynamicPermissionAllowMode mode);
// if the rule is "ask", or missing, will pop up a dialog and return false until the user agrees.
// (will continue returning false if the user does not agree, of course.)
eDynamicPermissionAllowMode clientPermissionMode(wl_client* client, eDynamicPermissionType permission);
void removeRulesForClient(wl_client* client);
private:
void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type);
//
std::vector<SP<CDynamicPermissionRule>> m_rules;
};
inline UP<CDynamicPermissionManager> g_pDynamicPermissionManager;

View File

@@ -301,7 +301,7 @@ APICALL std::vector<SFunctionMatch> HyprlandAPI::findFunctionsByName(HANDLE hand
#endif
};
u_int miblen = sizeof(mib) / sizeof(mib[0]);
char exe[PATH_MAX] = "";
char exe[PATH_MAX] = "/nonexistent";
size_t sz = sizeof(exe);
sysctl(mib, miblen, &exe, &sz, NULL, 0);
const auto FPATH = std::filesystem::canonical(exe);

View File

@@ -158,7 +158,8 @@ void CHyprlandCTMControlProtocol::setCTM(PHLMONITOR monitor, const Mat3x3& ctm)
data->progress->setCallbackOnEnd([monitor = PHLMONITORREF{monitor}, this](auto) {
if (!monitor || !m_mCTMDatas.contains(monitor)) {
monitor->setCTM(Mat3x3::identity());
if (monitor)
monitor->setCTM(Mat3x3::identity());
return;
}
auto& data = m_mCTMDatas.at(monitor);

View File

@@ -331,11 +331,16 @@ const hdr_output_metadata& CColorManagementSurface::hdrMetadata() {
}
void CColorManagementSurface::setHDRMetadata(const hdr_output_metadata& metadata) {
m_hdrMetadata = metadata;
m_needsNewMetadata = false;
m_hdrMetadata = metadata;
m_lastImageDescription = m_imageDescription;
m_needsNewMetadata = false;
}
bool CColorManagementSurface::needsHdrMetadataUpdate() {
if (!m_needsNewMetadata)
return false;
if (m_imageDescription == m_lastImageDescription)
m_needsNewMetadata = false;
return m_needsNewMetadata;
}

View File

@@ -65,6 +65,7 @@ class CColorManagementSurface {
SP<CWpColorManagementSurfaceV1> m_resource;
wl_client* pClient = nullptr;
NColorManagement::SImageDescription m_imageDescription;
NColorManagement::SImageDescription m_lastImageDescription;
bool m_hasImageDescription = false;
bool m_needsNewMetadata = false;
hdr_output_metadata m_hdrMetadata;

View File

@@ -75,95 +75,41 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface->pending.buffer && surface->pending.newBuffer && !surface->pending.texture) {
removeAllWaiters();
surface->commitPendingState(surface->pending);
return; // null buffer attached.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && surface->current.buffer) {
surface->current.bufferDamage.clear();
surface->current.damage.clear();
surface->commitPendingState(surface->current);
return; // no new buffer, but we still have current around and a commit happend, commit current again.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && !surface->current.buffer) {
surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit
if (!surface->pending.updated.buffer || !surface->pending.buffer) {
if (pendingAcquire.timeline() || pendingRelease.timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true;
}
return;
}
if (pendingAcquire.timeline()) {
surface->pending.buffer->acquire = makeUnique<CDRMSyncPointState>(std::move(pendingAcquire));
pendingAcquire = {};
}
if (pendingRelease.timeline()) {
surface->pending.buffer->release = makeUnique<CDRMSyncPointState>(std::move(pendingRelease));
pendingRelease = {};
}
if (protocolError())
if (!pendingAcquire.timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline");
surface->pending.rejected = true;
return;
}
const auto& state = pendingStates.emplace_back(makeShared<SSurfaceState>(surface->pending));
surface->pending.damage.clear();
surface->pending.bufferDamage.clear();
surface->pending.newBuffer = false;
surface->pending.buffer.reset();
if (!pendingRelease.timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline");
surface->pending.rejected = true;
return;
}
state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease();
state->buffer->acquire->addWaiter([this, surf = surface, wp = CWeakPointer<SSurfaceState>(*std::prev(pendingStates.end()))] {
if (!surf)
return;
surf->commitPendingState(*wp.lock());
std::erase(pendingStates, wp);
});
});
}
void CDRMSyncobjSurfaceResource::removeAllWaiters() {
for (auto& s : pendingStates) {
if (s && s->buffer && s->buffer->acquire)
s->buffer->acquire->timeline()->removeAllWaiters();
}
pendingStates.clear();
}
CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() {
removeAllWaiters();
}
bool CDRMSyncobjSurfaceResource::protocolError() {
if (!surface->pending.buffer) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true;
return true;
}
if (!surface->pending.buffer->acquire || !surface->pending.buffer->acquire->timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline");
surface->pending.rejected = true;
return true;
}
if (!surface->pending.buffer->release || !surface->pending.buffer->release->timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline");
surface->pending.rejected = true;
return true;
}
if (surface->pending.buffer->acquire->timeline() == surface->pending.buffer->release->timeline()) {
if (surface->pending.buffer->acquire->point() >= surface->pending.buffer->release->point()) {
if (pendingAcquire.timeline() == pendingRelease.timeline() && pendingAcquire.point() >= pendingRelease.point()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, "Acquire and release points are on the same timeline, and acquire >= release");
surface->pending.rejected = true;
return true;
return;
}
}
return false;
surface->pending.updated.acquire = true;
surface->pending.acquire = pendingAcquire;
pendingAcquire = {};
surface->pending.buffer.release = pendingRelease;
pendingRelease = {};
surface->pending.buffer->syncReleaser = surface->pending.buffer.release.createSyncRelease();
});
}
bool CDRMSyncobjSurfaceResource::good() {

View File

@@ -5,14 +5,11 @@
#include "../helpers/sync/SyncReleaser.hpp"
#include "linux-drm-syncobj-v1.hpp"
#include "../helpers/signal/Signal.hpp"
#include "types/SurfaceState.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
#include <list>
class CWLSurfaceResource;
class CDRMSyncobjTimelineResource;
class CSyncTimeline;
struct SSurfaceState;
class CDRMSyncPointState {
public:
@@ -28,6 +25,10 @@ class CDRMSyncPointState {
Hyprutils::OS::CFileDescriptor exportAsFD();
void signal();
operator bool() const {
return m_timeline;
}
private:
SP<CSyncTimeline> m_timeline = {};
uint64_t m_point = 0;
@@ -38,19 +39,15 @@ class CDRMSyncPointState {
class CDRMSyncobjSurfaceResource {
public:
CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurfaceV1>&& resource_, SP<CWLSurfaceResource> surface_);
~CDRMSyncobjSurfaceResource();
bool protocolError();
bool good();
private:
void removeAllWaiters();
WP<CWLSurfaceResource> surface;
UP<CWpLinuxDrmSyncobjSurfaceV1> resource;
CDRMSyncPointState pendingAcquire;
CDRMSyncPointState pendingRelease;
std::vector<SP<SSurfaceState>> pendingStates;
struct {
CHyprSignalListener surfacePrecommit;

View File

@@ -4,6 +4,7 @@
#include "protocols/core/Output.hpp"
#include "render/Renderer.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/EventManager.hpp"
CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHandleV1> resource_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) {
if UNLIKELY (!resource_->resource())
@@ -95,6 +96,30 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, false);
});
resource->setSetMinimized([this](CZwlrForeignToplevelHandleV1* p) {
const auto PWINDOW = pWindow.lock();
if UNLIKELY (!PWINDOW)
return;
if UNLIKELY (!PWINDOW->m_bIsMapped)
return;
g_pEventManager->postEvent(SHyprIPCEvent{.event = "minimized", .data = std::format("{:x},1", (uintptr_t)PWINDOW.get())});
});
resource->setUnsetMinimized([this](CZwlrForeignToplevelHandleV1* p) {
const auto PWINDOW = pWindow.lock();
if UNLIKELY (!PWINDOW)
return;
if UNLIKELY (!PWINDOW->m_bIsMapped)
return;
g_pEventManager->postEvent(SHyprIPCEvent{.event = "minimized", .data = std::format("{:x},0", (uintptr_t)PWINDOW.get())});
});
resource->setClose([this](CZwlrForeignToplevelHandleV1* p) {
const auto PWINDOW = pWindow.lock();

View File

@@ -3,6 +3,7 @@
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../managers/PointerManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/permissions/DynamicPermissionManager.hpp"
#include "../render/Renderer.hpp"
#include "../render/OpenGL.hpp"
#include "../helpers/Monitor.hpp"
@@ -14,11 +15,6 @@
#include <algorithm>
#include <functional>
CScreencopyFrame::~CScreencopyFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource_, int32_t overlay_cursor, wl_resource* output, CBox box_) : resource(resource_) {
if UNLIKELY (!good())
return;
@@ -102,8 +98,6 @@ void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_
return;
}
PBUFFER->buffer->lock();
if UNLIKELY (PBUFFER->buffer->size != box.size()) {
LOGM(ERR, "Invalid dimensions in {:x}", (uintptr_t)this);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
@@ -146,7 +140,7 @@ void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_
return;
}
buffer = PBUFFER->buffer;
buffer = CHLBufferReference(PBUFFER->buffer.lock());
PROTO::screencopy->m_vFramesAwaitingWrite.emplace_back(self);
@@ -203,24 +197,34 @@ void CScreencopyFrame::share() {
}
void CScreencopyFrame::copyDmabuf(std::function<void(bool)> callback) {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(resource->client(), PERMISSION_TYPE_SCREENCOPY);
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) {
if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.buffer, nullptr, true)) {
LOGM(ERR, "Can't copy: failed to begin rendering to dma frame");
callback(false);
return;
}
CBox monbox = CBox{0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}
.translate({-box.x, -box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1);
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->setMonitorTransformEnabled(false);
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
CBox monbox = CBox{0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}
.translate({-box.x, -box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1);
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->setMonitorTransformEnabled(false);
} else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING)
g_pHyprOpenGL->clear(Colors::BLACK);
else {
g_pHyprOpenGL->clear(Colors::BLACK);
CBox texbox =
CBox{pMonitor->vecTransformedSize / 2.F, g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize}.translate(-g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize / 2.F);
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pScreencopyDeniedTexture, texbox, 1);
}
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender();
@@ -240,9 +244,10 @@ void CScreencopyFrame::copyDmabuf(std::function<void(bool)> callback) {
}
bool CScreencopyFrame::copyShm() {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(resource->client(), PERMISSION_TYPE_SCREENCOPY);
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
auto shm = buffer->shm();
auto shm = buffer->shm();
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
@@ -257,12 +262,21 @@ bool CScreencopyFrame::copyShm() {
return false;
}
CBox monbox = CBox{0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}.translate({-box.x, -box.y});
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1);
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->setMonitorTransformEnabled(false);
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
CBox monbox = CBox{0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}.translate({-box.x, -box.y});
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1);
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->setMonitorTransformEnabled(false);
} else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING)
g_pHyprOpenGL->clear(Colors::BLACK);
else {
g_pHyprOpenGL->clear(Colors::BLACK);
CBox texbox =
CBox{pMonitor->vecTransformedSize / 2.F, g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize}.translate(-g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize / 2.F);
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pScreencopyDeniedTexture, texbox, 1);
}
#ifndef GLES2
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.getFBID());
@@ -437,6 +451,14 @@ void CScreencopyProtocol::onOutputCommit(PHLMONITOR pMonitor) {
if (!f)
continue;
// check permissions
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(f->resource->client(), PERMISSION_TYPE_SCREENCOPY);
if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING)
continue; // pending an answer, don't do anything yet.
// otherwise share. If it's denied, it will be black.
if (!f->pMonitor || !f->buffer) {
framesToRemove.emplace_back(f);
continue;

View File

@@ -1,6 +1,7 @@
#pragma once
#include "../defines.hpp"
#include "./types/Buffer.hpp"
#include "wlr-screencopy-unstable-v1.hpp"
#include "WaylandProtocol.hpp"
@@ -50,7 +51,6 @@ class CScreencopyClient {
class CScreencopyFrame {
public:
CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource, int32_t overlay_cursor, wl_resource* output, CBox box);
~CScreencopyFrame();
bool good();
@@ -65,7 +65,7 @@ class CScreencopyFrame {
bool withDamage = false;
bool lockedSWCursors = false;
WP<IHLBuffer> buffer;
CHLBufferReference buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;

View File

@@ -8,6 +8,7 @@
#include "../helpers/Format.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/permissions/DynamicPermissionManager.hpp"
#include "../render/Renderer.hpp"
#include <algorithm>
@@ -73,11 +74,6 @@ bool CToplevelExportClient::good() {
return resource->resource();
}
CToplevelExportFrame::~CToplevelExportFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) {
if UNLIKELY (!good())
return;
@@ -159,8 +155,6 @@ void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resou
return;
}
PBUFFER->buffer->lock();
if UNLIKELY (PBUFFER->buffer->size != box.size()) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
PROTO::toplevelExport->destroyResource(this);
@@ -197,7 +191,7 @@ void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resou
return;
}
buffer = PBUFFER->buffer;
buffer = CHLBufferReference(PBUFFER->buffer.lock());
m_ignoreDamage = ignoreDamage;
@@ -238,7 +232,8 @@ void CToplevelExportFrame::share() {
}
bool CToplevelExportFrame::copyShm(timespec* now) {
auto shm = buffer->shm();
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(resource->client(), PERMISSION_TYPE_SCREENCOPY);
auto shm = buffer->shm();
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
// render the client
@@ -263,12 +258,18 @@ bool CToplevelExportFrame::copyShm(timespec* now) {
g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0));
// render client at 0,0
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value());
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value());
} else if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
CBox texbox =
CBox{PMONITOR->vecTransformedSize / 2.F, g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize}.translate(-g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize / 2.F);
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pScreencopyDeniedTexture, texbox, 1);
}
const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format);
if (!PFORMAT) {
@@ -329,6 +330,7 @@ bool CToplevelExportFrame::copyShm(timespec* now) {
}
bool CToplevelExportFrame::copyDmabuf(timespec* now) {
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(resource->client(), PERMISSION_TYPE_SCREENCOPY);
const auto PMONITOR = pWindow->m_pMonitor.lock();
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
@@ -340,17 +342,22 @@ bool CToplevelExportFrame::copyDmabuf(timespec* now) {
g_pPointerManager->damageCursor(PMONITOR->self.lock());
}
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock()))
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.buffer))
return false;
g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0));
if (PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value());
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value());
} else if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
CBox texbox =
CBox{PMONITOR->vecTransformedSize / 2.F, g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize}.translate(-g_pHyprOpenGL->m_pScreencopyDeniedTexture->m_vSize / 2.F);
g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pScreencopyDeniedTexture, texbox, 1);
}
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender();
@@ -424,6 +431,12 @@ void CToplevelExportProtocol::onOutputCommit(PHLMONITOR pMonitor) {
if (!f)
continue;
// check permissions
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(f->resource->client(), PERMISSION_TYPE_SCREENCOPY);
if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING)
continue; // pending an answer, don't do anything yet.
if (!validMapped(f->pWindow)) {
framesToRemove.emplace_back(f);
continue;

View File

@@ -40,7 +40,6 @@ class CToplevelExportClient {
class CToplevelExportFrame {
public:
CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor, PHLWINDOW pWindow);
~CToplevelExportFrame();
bool good();
@@ -55,7 +54,7 @@ class CToplevelExportFrame {
bool m_ignoreDamage = false;
bool lockedSWCursors = false;
WP<IHLBuffer> buffer;
CHLBufferReference buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;

View File

@@ -15,6 +15,8 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
return;
}
surface->pending.updated.viewport = true;
if (x == -1 && y == -1) {
surface->pending.viewport.hasDestination = false;
return;
@@ -35,6 +37,8 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
return;
}
surface->pending.updated.viewport = true;
double x = wl_fixed_to_double(fx), y = wl_fixed_to_double(fy), w = wl_fixed_to_double(fw), h = wl_fixed_to_double(fh);
if (x == -1 && y == -1 && w == -1 && h == -1) {

View File

@@ -319,8 +319,11 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
if (!pendingApply.states.empty()) {
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
}
resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);

View File

@@ -245,8 +245,8 @@ CXXColorManagementSurface::CXXColorManagementSurface(SP<CXxColorManagementSurfac
}
if (surface.valid()) {
surface->colorManagement->setHasImageDescription(true);
surface->colorManagement->m_imageDescription = imageDescription->get()->settings;
surface->colorManagement->setHasImageDescription(true);
} else
LOGM(ERR, "Set image description for invalid surface");
});

View File

@@ -71,25 +71,32 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
pending.offset = {x, y};
pending.newBuffer = true;
pending.updated.buffer = true;
pending.updated.offset = true;
if (!buffer) {
pending.buffer.reset();
pending.texture.reset();
pending.offset = {x, y};
if (pending.buffer)
pending.buffer.drop();
auto buf = buffer ? CWLBufferResource::fromResource(buffer) : nullptr;
if (buf && buf->buffer) {
pending.buffer = CHLBufferReference(buf->buffer.lock());
pending.texture = buf->buffer->texture;
pending.size = buf->buffer->size;
pending.bufferSize = buf->buffer->size;
} else {
auto res = CWLBufferResource::fromResource(buffer);
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr;
pending.size = res && res->buffer ? res->buffer->size : Vector2D{};
pending.texture = res && res->buffer ? res->buffer->texture : nullptr;
pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
pending.buffer = {};
pending.texture.reset();
pending.size = Vector2D{};
pending.bufferSize = Vector2D{};
}
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{};
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
if (pending.bufferSize != current.bufferSize) {
pending.updated.damage = true;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
}
});
resource->setCommit([this](CWlSurface* r) {
@@ -111,19 +118,81 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
events.precommit.emit();
if (pending.rejected) {
pending.rejected = false;
dropPendingBuffer();
return;
}
if (!syncobj)
commitPendingState(pending);
if ((!pending.updated.buffer) || // no new buffer attached
(!pending.buffer && !pending.texture) || // null buffer attached
(!pending.updated.acquire && pending.buffer->isSynchronous()) // synchronous buffers (ex. shm) can be read immediately
) {
commitState(pending);
pending.reset();
return;
}
// save state while we wait for buffer to become ready
const auto& state = pendingStates.emplace(makeUnique<SSurfaceState>(pending));
pending.reset();
auto whenReadable = [this, surf = self, state = WP<SSurfaceState>(pendingStates.back())] {
if (!surf || state.expired())
return;
while (!pendingStates.empty() && pendingStates.front() != state) {
commitState(*pendingStates.front());
pendingStates.pop();
}
commitState(*pendingStates.front());
pendingStates.pop();
};
if (state->updated.acquire) {
// wait on acquire point for this surface, from explicit sync protocol
state->acquire.addWaiter(whenReadable);
} else if (state->buffer->dmabuf().success) {
// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#implicit-fence-poll-support
// TODO: wait for the dma-buf fd's to become readable
whenReadable();
} else {
// huh??? only buffers with acquire or dmabuf should get through here...
Debug::log(ERR, "BUG THIS: wl_surface.commit: non-acquire non-dmabuf buffers needs wait...");
whenReadable();
}
});
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); });
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); });
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated.damage = true;
pending.damage.add(CBox{x, y, w, h});
});
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated.damage = true;
pending.bufferDamage.add(CBox{x, y, w, h});
});
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; });
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; });
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) {
if (scale == pending.scale)
return;
pending.updated.scale = true;
pending.updated.damage = true;
pending.scale = scale;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) {
if (tr == pending.transform)
return;
pending.updated.transform = true;
pending.updated.damage = true;
pending.transform = (wl_output_transform)tr;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) {
if (!region) {
@@ -131,6 +200,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated.input = true;
auto RG = CWLRegionResource::fromResource(region);
pending.input = RG->region;
});
@@ -141,13 +212,18 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated.opaque = true;
auto RG = CWLRegionResource::fromResource(region);
pending.opaque = RG->region;
});
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) {
pending.updated.offset = true;
pending.offset = {x, y};
});
}
CWLSurfaceResource::~CWLSurfaceResource() {
@@ -165,11 +241,11 @@ void CWLSurfaceResource::destroy() {
}
void CWLSurfaceResource::dropPendingBuffer() {
pending.buffer.reset();
pending.buffer = {};
}
void CWLSurfaceResource::dropCurrentBuffer() {
current.buffer.reset();
current.buffer = {};
}
SP<CWLSurfaceResource> CWLSurfaceResource::fromResource(wl_resource* res) {
@@ -256,7 +332,6 @@ void CWLSurfaceResource::resetRole() {
}
void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> const& nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
std::vector<SP<CWLSurfaceResource>> nodes2;
nodes2.reserve(nodes.size() * 2);
@@ -401,18 +476,12 @@ CBox CWLSurfaceResource::extends() {
return full.getExtents();
}
void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
void CWLSurfaceResource::commitState(SSurfaceState& state) {
auto lastTexture = current.texture;
if (state.newBuffer) {
state.newBuffer = false;
current = state;
state.damage.clear();
state.bufferDamage.clear();
state.buffer.reset();
}
current.updateFrom(state);
if (current.buffer) {
if (current.buffer->buffer->isSynchronous())
if (current.buffer->isSynchronous())
current.updateSynchronousTexture(lastTexture);
// if the surface is a cursor, update the shm buffer
@@ -447,7 +516,7 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
// release the buffer if it's synchronous (SHM) as update() has done everything thats needed
// so we can let the app know we're done.
// if it doesn't have a role, we can't release it yet, in case it gets turned into a cursor.
if (current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED)
if (current.buffer && current.buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED)
dropCurrentBuffer();
}
@@ -455,7 +524,7 @@ void CWLSurfaceResource::updateCursorShm(CRegion damage) {
if (damage.empty())
return;
auto buf = current.buffer ? current.buffer->buffer : SP<IHLBuffer>{};
auto buf = current.buffer ? current.buffer : SP<IHLBuffer>{};
if UNLIKELY (!buf)
return;

View File

@@ -9,6 +9,7 @@
*/
#include <vector>
#include <queue>
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include "../../render/Texture.hpp"
@@ -87,6 +88,7 @@ class CWLSurfaceResource {
} events;
SSurfaceState current, pending;
std::queue<UP<SSurfaceState>> pendingStates;
std::vector<SP<CWLCallbackResource>> callbacks;
WP<CWLSurfaceResource> self;
@@ -103,7 +105,7 @@ class CWLSurfaceResource {
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
SP<CWLSurfaceResource> findFirstPreorder(std::function<bool(SP<CWLSurfaceResource>)> fn);
void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false);
void commitPendingState(SSurfaceState& state);
void commitState(SSurfaceState& state);
// returns a pair: found surface (null if not found) and surface local coords.
// localCoords param is relative to 0,0 of this surface

View File

@@ -420,7 +420,7 @@ void CWLDataDeviceProtocol::destroyResource(CWLDataOfferResource* resource) {
SP<IDataDevice> CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) {
#ifndef NO_XWAYLAND
if (g_pXWayland->pServer && c == g_pXWayland->pServer->xwaylandClient)
if (g_pXWayland && g_pXWayland->pServer && c == g_pXWayland->pServer->xwaylandClient)
return g_pXWayland->pWM->getDataDevice();
#endif
@@ -808,15 +808,19 @@ void CWLDataDeviceProtocol::renderDND(PHLMONITOR pMonitor, timespec* when) {
const auto POS = g_pInputManager->getMouseCoordsInternal();
CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale);
Vector2D surfacePos = POS;
surfacePos += dnd.dndSurface->current.offset;
CBox box = CBox{surfacePos, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition).scale(pMonitor->scale);
CTexPassElement::SRenderData data;
data.tex = dnd.dndSurface->current.texture;
data.box = box;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F).expand(5);
g_pHyprRenderer->damageBox(box);
CBox damageBox = CBox{surfacePos, dnd.dndSurface->current.size}.expand(5);
g_pHyprRenderer->damageBox(damageBox);
dnd.dndSurface->frame(when);
}

View File

@@ -22,7 +22,7 @@ bool CWLTouchResource::good() {
}
void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner)
if (!owner || !surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
@@ -145,7 +145,7 @@ bool CWLPointerResource::good() {
}
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -165,7 +165,7 @@ void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2
}
void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -336,7 +336,7 @@ void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
}
void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
@@ -361,7 +361,7 @@ void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
}
void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))

View File

@@ -40,13 +40,61 @@ void IHLBuffer::onBackendRelease(const std::function<void()>& fn) {
});
}
CHLBufferReference::CHLBufferReference(SP<IHLBuffer> buffer_, SP<CWLSurfaceResource> surface_) : buffer(buffer_), surface(surface_) {
buffer->lock();
CHLBufferReference::CHLBufferReference() : buffer(nullptr) {
;
}
CHLBufferReference::CHLBufferReference(const CHLBufferReference& other) : release(other.release), buffer(other.buffer) {
if (buffer)
buffer->lock();
}
CHLBufferReference::CHLBufferReference(SP<IHLBuffer> buffer_) : buffer(buffer_) {
if (buffer)
buffer->lock();
}
CHLBufferReference::~CHLBufferReference() {
if (buffer)
buffer->unlock();
}
CHLBufferReference& CHLBufferReference::operator=(const CHLBufferReference& other) {
if (other.buffer)
other.buffer->lock();
if (buffer)
buffer->unlock();
buffer = other.buffer;
release = other.release;
return *this;
}
bool CHLBufferReference::operator==(const CHLBufferReference& other) const {
return buffer == other.buffer;
}
bool CHLBufferReference::operator==(const SP<IHLBuffer>& other) const {
return buffer == other;
}
bool CHLBufferReference::operator==(const SP<Aquamarine::IBuffer>& other) const {
return buffer == other;
}
SP<IHLBuffer> CHLBufferReference::operator->() const {
return buffer;
}
CHLBufferReference::operator bool() const {
return buffer;
}
void CHLBufferReference::drop() {
if (!buffer)
return;
buffer->unlock();
buffer->nLocks--;
ASSERT(buffer->nLocks >= 0);
buffer = nullptr;
}

View File

@@ -8,6 +8,7 @@
#include <aquamarine/buffer/Buffer.hpp>
class CSyncReleaser;
class CHLBufferReference;
class IHLBuffer : public Aquamarine::IBuffer {
public:
@@ -36,19 +37,28 @@ class IHLBuffer : public Aquamarine::IBuffer {
private:
int nLocks = 0;
friend class CHLBufferReference;
};
// for ref-counting. Releases in ~dtor
// surface optional
class CHLBufferReference {
public:
CHLBufferReference(SP<IHLBuffer> buffer, SP<CWLSurfaceResource> surface);
CHLBufferReference();
CHLBufferReference(const CHLBufferReference& other);
CHLBufferReference(SP<IHLBuffer> buffer);
~CHLBufferReference();
SP<IHLBuffer> buffer;
UP<CDRMSyncPointState> acquire;
UP<CDRMSyncPointState> release;
CHLBufferReference& operator=(const CHLBufferReference& other);
bool operator==(const CHLBufferReference& other) const;
bool operator==(const SP<IHLBuffer>& other) const;
bool operator==(const SP<Aquamarine::IBuffer>& other) const;
SP<IHLBuffer> operator->() const;
operator bool() const;
private:
WP<CWLSurfaceResource> surface;
// unlock and drop the buffer without sending release
void drop();
CDRMSyncPointState release;
SP<IHLBuffer> buffer;
};

View File

@@ -35,7 +35,7 @@ CRegion SSurfaceState::accumulateBufferDamage() {
}
void SSurfaceState::updateSynchronousTexture(SP<CTexture> lastTexture) {
auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0);
auto [dataPtr, fmt, size] = buffer->beginDataPtr(0);
if (dataPtr) {
auto drmFmt = NFormatUtils::shmToDRM(fmt);
auto stride = bufferSize.y ? size / bufferSize.y : 0;
@@ -45,14 +45,56 @@ void SSurfaceState::updateSynchronousTexture(SP<CTexture> lastTexture) {
} else
texture = makeShared<CTexture>(drmFmt, dataPtr, stride, bufferSize);
}
buffer->buffer->endDataPtr();
buffer->endDataPtr();
}
void SSurfaceState::reset() {
updated.all = false;
// After commit, there is no pending buffer until the next attach.
buffer = {};
// applies only to the buffer that is attached to the surface
acquire = {};
// wl_surface.commit assings pending ... and clears pending damage.
damage.clear();
bufferDamage.clear();
transform = WL_OUTPUT_TRANSFORM_NORMAL;
scale = 1;
offset = {};
size = {};
}
void SSurfaceState::updateFrom(SSurfaceState& ref) {
updated = ref.updated;
if (ref.updated.buffer) {
buffer = ref.buffer;
texture = ref.texture;
size = ref.size;
bufferSize = ref.bufferSize;
}
if (ref.updated.damage) {
damage = ref.damage;
bufferDamage = ref.bufferDamage;
}
if (ref.updated.input)
input = ref.input;
if (ref.updated.opaque)
opaque = ref.opaque;
if (ref.updated.offset)
offset = ref.offset;
if (ref.updated.scale)
scale = ref.scale;
if (ref.updated.transform)
transform = ref.transform;
if (ref.updated.viewport)
viewport = ref.viewport;
if (ref.updated.acquire)
acquire = ref.acquire;
}

View File

@@ -2,30 +2,59 @@
#include "../../helpers/math/Math.hpp"
#include "../WaylandProtocol.hpp"
#include "./Buffer.hpp"
class CHLBufferReference;
class CTexture;
class CDRMSyncPointState;
struct SSurfaceState {
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
int scale = 1;
SP<CHLBufferReference> buffer; // buffer ref will be released once the buffer is no longer locked. For checking if a buffer is attached to this state, check texture.
SP<CTexture> texture;
Vector2D offset;
Vector2D size, bufferSize;
union {
uint16_t all = 0;
struct {
bool buffer : 1;
bool damage : 1;
bool opaque : 1;
bool input : 1;
bool transform : 1;
bool scale : 1;
bool offset : 1;
bool viewport : 1;
bool acquire : 1;
};
} updated;
bool rejected = false;
// initial values, copied from protocol text
CHLBufferReference buffer = {}; // The initial surface contents are void
CRegion damage, bufferDamage; // The initial value for pending damage is empty
CRegion opaque; // The initial value for an opaque region is empty
CRegion input = CBox{{}, {INT32_MAX, INT32_MAX}}; // The initial value for an input region is infinite
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; // A newly created surface has its buffer transformation set to normal
int scale = 1; // A newly created surface has its buffer scale set to 1
// these don't have well defined initial values in the protocol, but these work
Vector2D size, bufferSize;
Vector2D offset;
// viewporter protocol surface state
struct {
bool hasDestination = false;
bool hasSource = false;
Vector2D destination;
CBox source;
} viewport;
bool rejected = false;
bool newBuffer = false;
Vector2D sourceSize();
// Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage
CRegion accumulateBufferDamage();
void updateSynchronousTexture(SP<CTexture> lastTexture);
void reset();
// drm syncobj protocol surface state
CDRMSyncPointState acquire;
// texture of surface content, used for rendering
SP<CTexture> texture;
void updateSynchronousTexture(SP<CTexture> lastTexture);
// helpers
CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage
void updateFrom(SSurfaceState& ref); // updates this state based on a reference state.
void reset(); // resets pending state after commit
};

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#include "../helpers/sync/SyncTimeline.hpp"
#include <cstdint>
#include <list>
#include <string>
#include <unordered_map>
#include <map>
@@ -78,6 +79,26 @@ enum eMonitorExtraRenderFBs : uint8_t {
FB_MONITOR_RENDER_EXTRA_BLUR,
};
struct SPreparedShaders {
std::string TEXVERTSRC;
std::string TEXVERTSRC300;
std::string TEXVERTSRC320;
CShader m_shQUAD;
CShader m_shRGBA;
CShader m_shPASSTHRURGBA;
CShader m_shMATTE;
CShader m_shRGBX;
CShader m_shEXT;
CShader m_shBLUR1;
CShader m_shBLUR2;
CShader m_shBLURPREPARE;
CShader m_shBLURFINISH;
CShader m_shSHADOW;
CShader m_shBORDER1;
CShader m_shGLITCH;
CShader m_shCM;
};
struct SMonitorRenderData {
CFramebuffer offloadFB;
CFramebuffer mirrorFB; // these are used for some effects,
@@ -90,23 +111,6 @@ struct SMonitorRenderData {
bool blurFBDirty = true;
bool blurFBShouldRender = false;
// Shaders
bool m_bShadersInitialized = false;
CShader m_shQUAD;
CShader m_shRGBA;
CShader m_shPASSTHRURGBA;
CShader m_shMATTE;
CShader m_shRGBX;
CShader m_shEXT;
CShader m_shBLUR1;
CShader m_shBLUR2;
CShader m_shBLURPREPARE;
CShader m_shBLURFINISH;
CShader m_shSHADOW;
CShader m_shBORDER1;
CShader m_shGLITCH;
CShader m_shCM;
};
struct SCurrentRenderData {
@@ -232,6 +236,10 @@ class CHyprOpenGLImpl {
EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs);
SP<CEGLSync> createEGLSync(int fence = -1);
bool initShaders();
bool m_bShadersInitialized = false;
SP<SPreparedShaders> m_shaders;
SCurrentRenderData m_RenderData;
Hyprutils::OS::CFileDescriptor m_iGBMFD;
@@ -275,7 +283,17 @@ class CHyprOpenGLImpl {
bool EXT_create_context_robustness = false;
} m_sExts;
SP<CTexture> m_pScreencopyDeniedTexture;
private:
enum eEGLContextVersion : uint8_t {
EGL_CONTEXT_GLES_2_0 = 0,
EGL_CONTEXT_GLES_3_0,
EGL_CONTEXT_GLES_3_2,
};
eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2;
std::list<GLuint> m_lBuffers;
std::list<GLuint> m_lTextures;
@@ -297,11 +315,10 @@ class CHyprOpenGLImpl {
SP<CTexture> m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; // TODO: don't always load lock
void logShaderError(const GLuint&, bool program = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false);
void logShaderError(const GLuint&, bool program = false, bool silent = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false, bool silent = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false, bool silent = false);
void createBGTextureForMonitor(PHLMONITOR);
void initShaders();
void initDRMFormats();
void initEGL(bool gbm);
EGLDeviceEXT eglDeviceFromDRMFD(int drmFD);
@@ -314,6 +331,9 @@ class CHyprOpenGLImpl {
// returns the out FB, can be either Mirror or MirrorSwap
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);
void passCMUniforms(const CShader&, const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription,
bool modifySDR = false);
void passCMUniforms(const CShader&, const NColorManagement::SImageDescription& imageDescription);
void renderTextureInternalWithDamage(SP<CTexture>, const CBox& box, float a, const CRegion& damage, int round = 0, float roundingPower = 2.0f, bool discardOpaque = false,
bool noAA = false, bool allowCustomUV = false, bool allowDim = false);
void renderTexturePrimitive(SP<CTexture> tex, const CBox& box);

View File

@@ -360,7 +360,7 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo
EMIT_HOOK_EVENT("render", RENDER_PRE_WINDOWS);
std::vector<PHLWINDOWREF> windows;
std::vector<PHLWINDOWREF> windows, tiledFadingOut;
windows.reserve(g_pCompositor->m_vWindows.size());
for (auto const& w : g_pCompositor->m_vWindows) {
@@ -390,6 +390,13 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo
continue;
}
// render tiled fading out after others
if (w->m_bFadingOut) {
tiledFadingOut.emplace_back(w);
w.reset();
continue;
}
// render the bad boy
renderWindow(w.lock(), pMonitor, time, true, RENDER_PASS_MAIN);
w.reset();
@@ -400,6 +407,11 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo
lastWindow.reset();
// render tiled windows that are fading out after other tiled to not hide them behind
for (auto& w : tiledFadingOut) {
renderWindow(w.lock(), pMonitor, time, true, RENDER_PASS_MAIN);
}
// Non-floating popup
for (auto& w : windows) {
if (!w)
@@ -1411,11 +1423,16 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}};
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ)
return NO_HDR_METADATA; // empty metadata for SDR
uint8_t eotf = 0;
switch (settings.transferFunction) {
case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now
case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break;
// case CM_TRANSFER_FUNCTION_HLG: eotf = 3; break; TODO check display capabilities first
default: return NO_HDR_METADATA; // empty metadata for SDR
}
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ?
@@ -1429,7 +1446,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = 2,
.eotf = eotf,
.metadata_type = 0,
.display_primaries =
{
@@ -1454,25 +1471,42 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) {
// HDR metadata determined by
// PPASS = 0 monitor settings
// PPASS = 1
// windowed: monitor settings
// fullscreen surface: surface settings FIXME: fullscreen SDR surface passthrough - pass degamma, ctm, gamma if needed
// PPASS = 2
// windowed: monitor settings
// fullscreen SDR surface: monitor settings
// fullscreen HDR surface: surface settings
bool wantHDR = PHDR;
bool hdrIsHandled = false;
if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
const auto SURF =
ROOT_SURF->findFirstPreorder([ROOT_SURF](SP<CWLSurfaceResource> surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); });
const bool wantHDR = PHDR && *PPASS == 2;
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) {
wantHDR = PHDR && *PPASS == 2;
// we have a surface with image description and it's allowed by wantHDR
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription() &&
(!wantHDR || SURF->colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) {
bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW;
if (SURF->colorManagement->needsHdrMetadataUpdate())
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
if (needsHdrMetadataUpdate)
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR)
pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
hdrIsHandled = true;
}
pMonitor->m_previousFSWindow = WINDOW;
} else {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
}
if (!hdrIsHandled) {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR)
pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
pMonitor->m_previousFSWindow.reset();
}
}
@@ -1531,10 +1565,10 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync");
else {
for (auto const& e : explicitPresented) {
if (!e->current.buffer || !e->current.buffer->buffer->syncReleaser)
if (!e->current.buffer || !e->current.buffer->syncReleaser)
continue;
e->current.buffer->buffer->syncReleaser->addReleaseSync(pMonitor->eglSync);
e->current.buffer->syncReleaser->addReleaseSync(pMonitor->eglSync);
}
}

View File

@@ -1,17 +1,5 @@
#include "Shader.hpp"
GLint CShader::getUniformLocation(const std::string& unif) {
const auto itpos = m_muUniforms.find(unif);
if (itpos == m_muUniforms.end()) {
const auto unifLoc = glGetUniformLocation(program, unif.c_str());
m_muUniforms[unif] = unifLoc;
return unifLoc;
}
return itpos->second;
}
CShader::~CShader() {
destroy();
}

View File

@@ -12,6 +12,7 @@ class CShader {
GLint color = -1;
GLint alphaMatte = -1;
GLint texType = -1;
GLint skipCM = -1;
GLint sourceTF = -1;
GLint targetTF = -1;
GLint sourcePrimaries = -1;
@@ -74,8 +75,6 @@ class CShader {
GLint brightness = -1;
GLint noise = -1;
GLint getUniformLocation(const std::string&);
void destroy();
private:

View File

@@ -1,5 +0,0 @@
#pragma once
#include "shaders/Textures.hpp"
#include "shaders/Shadow.hpp"
#include "shaders/Border.hpp"

View File

@@ -147,7 +147,9 @@ eDecorationLayer CHyprBorderDecoration::getDecorationLayer() {
}
uint64_t CHyprBorderDecoration::getDecorationFlags() {
return !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0;
static auto PPARTOFWINDOW = CConfigValue<Hyprlang::INT>("decoration:border_part_of_window");
return *PPARTOFWINDOW && !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0;
}
std::string CHyprBorderDecoration::getDisplayName() {

View File

@@ -34,6 +34,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
static auto PPRIORITY = CConfigValue<Hyprlang::INT>("group:groupbar:priority");
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
static auto POUTERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_out");
static auto PKEEPUPPERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:keep_upper_gap");
SDecorationPositioningInfo info;
info.policy = DECORATION_POSITION_STICKY;
@@ -44,9 +45,9 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
if (*PENABLED && m_pWindow->m_sWindowData.decorate.valueOrDefault()) {
if (*PSTACKED) {
const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP}, {0, 0}};
info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + (*PKEEPUPPERGAP * *POUTERGAP)}, {0, 0}};
} else
info.desiredExtents = {{0, *POUTERGAP * 2 + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}, {0, 0}};
info.desiredExtents = {{0, *POUTERGAP * (1 + *PKEEPUPPERGAP) + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}, {0, 0}};
} else
info.desiredExtents = {{0, 0}, {0, 0}};
return info;
@@ -117,6 +118,8 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
static auto PGROUPCOLINACTIVELOCKED = CConfigValue<Hyprlang::CUSTOMTYPE>("group:groupbar:col.locked_inactive");
static auto POUTERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_out");
static auto PINNERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_in");
static auto PKEEPUPPERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:keep_upper_gap");
static auto PTEXTOFFSET = CConfigValue<Hyprlang::INT>("group:groupbar:text_offset");
auto* const GROUPCOLACTIVE = (CGradientValueData*)(PGROUPCOLACTIVE.ptr())->getData();
auto* const GROUPCOLINACTIVE = (CGradientValueData*)(PGROUPCOLINACTIVE.ptr())->getData();
auto* const GROUPCOLACTIVELOCKED = (CGradientValueData*)(PGROUPCOLACTIVELOCKED.ptr())->getData();
@@ -126,9 +129,9 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
m_fBarWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - *PINNERGAP * (barsToDraw - 1)) / barsToDraw;
m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - 2 - *POUTERGAP) - *POUTERGAP * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - *POUTERGAP;
m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - *POUTERGAP * *PKEEPUPPERGAP) - *POUTERGAP * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - *POUTERGAP * *PKEEPUPPERGAP;
const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP : *POUTERGAP * 2L + ONEBARHEIGHT;
const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + *POUTERGAP * *PKEEPUPPERGAP : *POUTERGAP * (1 + *PKEEPUPPERGAP) + ONEBARHEIGHT;
if (DESIREDHEIGHT != ASSIGNEDBOX.h)
g_pDecorationPositioner->repositionDeco(this);
@@ -142,7 +145,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
*PINDICATORHEIGHT};
rect.scale(pMonitor->scale);
rect.scale(pMonitor->scale).round();
const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked;
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE;
@@ -232,7 +235,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
.emplace_back(makeUnique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2L * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
.get();
rect.y += std::ceil((rect.height - pTitleTex->texSize.y) / 2.0);
rect.y += std::ceil(((rect.height - pTitleTex->texSize.y) / 2.0) - (*PTEXTOFFSET * pMonitor->scale));
rect.height = pTitleTex->texSize.y;
rect.width = pTitleTex->texSize.x;
rect.x += std::round(((m_fBarWidth * pMonitor->scale) / 2.0) - (pTitleTex->texSize.x / 2.0));
@@ -278,7 +281,7 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
const CHyprColor COLOR = CHyprColor(*PTEXTCOLOR);
const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT;
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE * monitorScale, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
if (tex)
texSize = tex->m_vSize;
@@ -573,5 +576,5 @@ CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
if (PWORKSPACE && !m_pWindow->m_bPinned)
box.translate(PWORKSPACE->m_vRenderOffset->value());
return box;
return box.round();
}

View File

@@ -33,5 +33,13 @@ std::optional<CBox> CRectPassElement::boundingBox() {
}
CRegion CRectPassElement::opaqueRegion() {
return data.color.a >= 1.F ? boundingBox()->expand(-data.round) : CRegion{};
if (data.color.a < 1.F)
return CRegion{};
CRegion rg = boundingBox()->expand(-data.round);
if (!data.clipBox.empty())
rg.intersect(data.clipBox);
return rg;
}

View File

@@ -22,8 +22,7 @@ void CTexPassElement::draw(const CRegion& damage) {
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection;
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower, data.syncTimeline,
data.syncPoint);
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower);
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
}

View File

@@ -16,8 +16,6 @@ class CTexPassElement : public IPassElement {
int round = 0;
float roundingPower = 2.0f;
bool flipEndFrame = false;
SP<CSyncTimeline> syncTimeline;
int64_t syncPoint = 0;
std::optional<Mat3x3> replaceProjection;
CBox clipBox;
};

View File

@@ -1,3 +0,0 @@
#pragma once
constexpr float SHADER_ROUNDED_SMOOTHING_FACTOR = M_PI / 5.34665792551;

View File

@@ -1,541 +0,0 @@
#pragma once
#include <string>
#include <format>
#include "SharedValues.hpp"
inline static constexpr auto ROUNDED_SHADER_FUNC = [](const std::string colorVarName) -> std::string {
return R"#(
// shoutout me: I fixed this shader being a bit pixelated while watching hentai
highp vec2 pixCoord = vec2(gl_FragCoord);
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoord -= fullSize * 0.5 - radius;
pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left
// smoothing constant for the edge: more = blurrier, but smoother
const float SMOOTHING_CONSTANT = )#" +
std::format("{:.7f}", SHADER_ROUNDED_SMOOTHING_FACTOR) + R"#(;
if (pixCoord.x + pixCoord.y > radius) {
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower);
if (dist > radius + SMOOTHING_CONSTANT)
discard;
float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
)#" +
colorVarName + R"#( = )#" + colorVarName + R"#( * normalized;
}
)#";
};
inline const std::string QUADVERTSRC = R"#(
uniform mat3 proj;
uniform vec4 color;
attribute vec2 pos;
attribute vec2 texcoord;
attribute vec2 texcoordMatte;
varying vec4 v_color;
varying vec2 v_texcoord;
varying vec2 v_texcoordMatte;
void main() {
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
v_color = color;
v_texcoord = texcoord;
v_texcoordMatte = texcoordMatte;
})#";
inline const std::string QUADFRAGSRC = R"#(
precision highp float;
varying vec4 v_color;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
void main() {
vec4 pixColor = v_color;
if (radius > 0.0) {
)#" +
ROUNDED_SHADER_FUNC("pixColor") + R"#(
}
gl_FragColor = pixColor;
})#";
inline const std::string TEXVERTSRC = R"#(
uniform mat3 proj;
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
v_texcoord = texcoord;
})#";
inline const std::string TEXVERTSRC320 = R"#(#version 320 es
uniform mat3 proj;
in vec2 pos;
in vec2 texcoord;
out vec2 v_texcoord;
void main() {
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
v_texcoord = texcoord;
})#";
inline const std::string TEXFRAGSRCCM =
#include "CM.frag"
;
inline const std::string TEXFRAGSRCRGBA = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float alpha;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0) {
)#" +
ROUNDED_SHADER_FUNC("pixColor") + R"#(
}
gl_FragColor = pixColor * alpha;
})#";
inline const std::string TEXFRAGSRCRGBAPASSTHRU = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
})#";
inline const std::string TEXFRAGSRCRGBAMATTE = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform sampler2D texMatte;
void main() {
gl_FragColor = texture2D(tex, v_texcoord) * texture2D(texMatte, v_texcoord)[0]; // I know it only uses R, but matte should be black/white anyways.
})#";
inline const std::string TEXFRAGSRCRGBX = R"#(
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D tex;
uniform float alpha;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform int discardOpaque;
uniform int discardAlpha;
uniform int discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
void main() {
if (discardOpaque == 1 && alpha == 1.0)
discard;
vec4 pixColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0);
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0) {
)#" +
ROUNDED_SHADER_FUNC("pixColor") + R"#(
}
gl_FragColor = pixColor * alpha;
})#";
inline const std::string FRAGBLUR1 = R"#(
#version 100
precision highp float;
varying highp vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float radius;
uniform vec2 halfpixel;
uniform int passes;
uniform float vibrancy;
uniform float vibrancy_darkness;
// see http://alienryderflex.com/hsp.html
const float Pr = 0.299;
const float Pg = 0.587;
const float Pb = 0.114;
// Y is "v" ( brightness ). X is "s" ( saturation )
// see https://www.desmos.com/3d/a88652b9a4
// Determines if high brightness or high saturation is more important
const float a = 0.93;
const float b = 0.11;
const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors
//
// http://www.flong.com/archive/texts/code/shapers_circ/
float doubleCircleSigmoid(float x, float a) {
a = clamp(a, 0.0, 1.0);
float y = .0;
if (x <= a) {
y = a - sqrt(a * a - x * x);
} else {
y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.));
}
return y;
}
vec3 rgb2hsl(vec3 col) {
float red = col.r;
float green = col.g;
float blue = col.b;
float minc = min(col.r, min(col.g, col.b));
float maxc = max(col.r, max(col.g, col.b));
float delta = maxc - minc;
float lum = (minc + maxc) * 0.5;
float sat = 0.0;
float hue = 0.0;
if (lum > 0.0 && lum < 1.0) {
float mul = (lum < 0.5) ? (lum) : (1.0 - lum);
sat = delta / (mul * 2.0);
}
if (delta > 0.0) {
vec3 maxcVec = vec3(maxc);
vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red)));
vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta;
hue += dot(adds, masks);
hue /= 6.0;
if (hue < 0.0)
hue += 1.0;
}
return vec3(hue, sat, lum);
}
vec3 hsl2rgb(vec3 col) {
const float onethird = 1.0 / 3.0;
const float twothird = 2.0 / 3.0;
const float rcpsixth = 6.0;
float hue = col.x;
float sat = col.y;
float lum = col.z;
vec3 xt = vec3(0.0);
if (hue < onethird) {
xt.r = rcpsixth * (onethird - hue);
xt.g = rcpsixth * hue;
xt.b = 0.0;
} else if (hue < twothird) {
xt.r = 0.0;
xt.g = rcpsixth * (twothird - hue);
xt.b = rcpsixth * (hue - onethird);
} else
xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue));
xt = min(xt, 1.0);
float sat2 = 2.0 * sat;
float satinv = 1.0 - sat;
float luminv = 1.0 - lum;
float lum2m1 = (2.0 * lum) - 1.0;
vec3 ct = (sat2 * xt) + satinv;
vec3 rgb;
if (lum >= 0.5)
rgb = (luminv * ct) + lum2m1;
else
rgb = lum * ct;
return rgb;
}
void main() {
vec2 uv = v_texcoord * 2.0;
vec4 sum = texture2D(tex, uv) * 4.0;
sum += texture2D(tex, uv - halfpixel.xy * radius);
sum += texture2D(tex, uv + halfpixel.xy * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
vec4 color = sum / 8.0;
if (vibrancy == 0.0) {
gl_FragColor = color;
} else {
// Invert it so that it correctly maps to the config setting
float vibrancy_darkness1 = 1.0 - vibrancy_darkness;
// Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest.
vec3 hsl = rgb2hsl(color.rgb);
// Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow
float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1);
float b1 = b * vibrancy_darkness1;
float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0;
float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0);
vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2]));
gl_FragColor = vec4(newColor, color[3]);
}
}
)#";
inline const std::string FRAGBLUR2 = R"#(
#version 100
precision highp float;
varying highp vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float radius;
uniform vec2 halfpixel;
void main() {
vec2 uv = v_texcoord / 2.0;
vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
gl_FragColor = sum / 12.0;
}
)#";
inline const std::string FRAGBLURPREPARE = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float contrast;
uniform float brightness;
float gain(float x, float k) {
float a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k);
return (x < 0.5) ? a : 1.0 - a;
}
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
// contrast
if (contrast != 1.0) {
pixColor.r = gain(pixColor.r, contrast);
pixColor.g = gain(pixColor.g, contrast);
pixColor.b = gain(pixColor.b, contrast);
}
// brightness
if (brightness > 1.0) {
pixColor.rgb *= brightness;
}
gl_FragColor = pixColor;
}
)#";
inline const std::string FRAGBLURFINISH = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float noise;
uniform float brightness;
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
// noise
float noiseHash = hash(v_texcoord);
float noiseAmount = (mod(noiseHash, 1.0) - 0.5);
pixColor.rgb += noiseAmount * noise;
// brightness
if (brightness < 1.0) {
pixColor.rgb *= brightness;
}
gl_FragColor = pixColor;
}
)#";
inline const std::string TEXFRAGSRCEXT = R"#(
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 v_texcoord;
uniform samplerExternalOES texture0;
uniform float alpha;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform int discardOpaque;
uniform int discardAlpha;
uniform int discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = texture2D(texture0, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0) {
)#" +
ROUNDED_SHADER_FUNC("pixColor") + R"#(
}
gl_FragColor = pixColor * alpha;
}
)#";
static const std::string FRAGGLITCH = R"#(
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D tex;
uniform float time; // quirk: time is set to 0 at the beginning, should be around 10 when crash.
uniform float distort;
uniform vec2 screenSize;
float rand(float co) {
return fract(sin(dot(vec2(co, co), vec2(12.9898, 78.233))) * 43758.5453);
}
float rand(vec2 co) {
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}
float noise(vec2 point) {
vec2 floored = floor(point);
vec2 fractal = fract(point);
fractal = fractal * fractal * (3.0 - 2.0 * fractal);
float mixed = mix(
mix(rand(floored), rand(floored + vec2(1.0, 0.0)), fractal.x),
mix(rand(floored + vec2(0.0,1.0)), rand(floored + vec2(1.0,1.0)), fractal.x), fractal.y);
return mixed * mixed;
}
void main() {
float ABERR_OFFSET = 4.0 * (distort / 5.5) * time;
float TEAR_AMOUNT = 9000.0 * (1.0 - (distort / 5.5));
float TEAR_BANDS = 108.0 / 2.0 * (distort / 5.5) * 2.0;
float MELT_AMOUNT = (distort * 8.0) / screenSize.y;
float NOISE = abs(mod(noise(v_texcoord) * distort * time * 2.771, 1.0)) * time / 10.0;
if (time < 2.0)
NOISE = 0.0;
float offset = (mod(rand(floor(v_texcoord.y * TEAR_BANDS)) * 318.772 * time, 20.0) - 10.0) / TEAR_AMOUNT;
vec2 blockOffset = vec2(((abs(mod(rand(floor(v_texcoord.x * 37.162)) * 721.43, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0),
((abs(mod(rand(floor(v_texcoord.y * 45.882)) * 733.923, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0));
if (time < 3.0)
blockOffset = vec2(0,0);
float meltSeed = abs(mod(rand(floor(v_texcoord.x * screenSize.x * 17.719)) * 281.882, 1.0));
if (meltSeed < 0.8) {
meltSeed = 0.0;
} else {
meltSeed *= 25.0 * NOISE;
}
float meltAmount = MELT_AMOUNT * meltSeed;
vec2 pixCoord = vec2(v_texcoord.x + offset + NOISE * 3.0 / screenSize.x + blockOffset.x, v_texcoord.y - meltAmount + 0.02 * NOISE / screenSize.x + NOISE * 3.0 / screenSize.y + blockOffset.y);
vec4 pixColor = texture2D(tex, pixCoord);
vec4 pixColorLeft = texture2D(tex, pixCoord + vec2(ABERR_OFFSET / screenSize.x, 0));
vec4 pixColorRight = texture2D(tex, pixCoord + vec2(-ABERR_OFFSET / screenSize.x, 0));
pixColor[0] = pixColorLeft[0];
pixColor[2] = pixColorRight[2];
pixColor[0] += distort / 90.0;
gl_FragColor = pixColor;
}
)#";

View File

@@ -0,0 +1,55 @@
#version 300 es
//#extension GL_OES_EGL_image_external : require
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord;
uniform sampler2D tex;
//uniform samplerExternalOES texture0;
uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
// uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;
uniform float alpha;
uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
#include "rounding.glsl"
#include "CM.glsl"
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor;
if (texType == 1)
pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);
// else if (texType == 2)
// pixColor = texture(texture0, v_texcoord);
else // assume rgba
pixColor = texture(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
// this shader shouldn't be used when skipCM == 1
pixColor = doColorManagement(pixColor, sourceTF, sourcePrimaries, targetTF, targetPrimaries);
if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);
if (radius > 0.0)
pixColor = rounding(pixColor);
fragColor = pixColor * alpha;
}

View File

@@ -1,37 +1,9 @@
R"#(
#version 320 es
//#extension GL_OES_EGL_image_external : require
precision highp float;
in vec2 v_texcoord;
uniform sampler2D tex;
//uniform samplerExternalOES texture0;
uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;
uniform float maxLuminance;
uniform float dstMaxLuminance;
uniform float dstRefLuminance;
uniform float sdrSaturation;
uniform float sdrBrightnessMultiplier;
uniform float alpha;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
//enum eTransferFunction
#define CM_TRANSFER_FUNCTION_BT1886 1
#define CM_TRANSFER_FUNCTION_GAMMA22 2
@@ -78,31 +50,7 @@ uniform vec3 tint;
#define HDR_MAX_LUMINANCE 10000.0
#define HLG_MAX_LUMINANCE 1000.0
// smoothing constant for the edge: more = blurrier, but smoother
#define M_PI 3.1415926535897932384626433832795
#define M_E 2.718281828459045
#define SMOOTHING_CONSTANT (M_PI / 5.34665792551)
vec4 rounding(vec4 color) {
highp vec2 pixCoord = vec2(gl_FragCoord);
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoord -= fullSize * 0.5 - radius;
pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left
if (pixCoord.x + pixCoord.y > radius) {
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower);
if (dist > radius + SMOOTHING_CONSTANT)
discard;
float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
color *= normalized;
}
return color;
}
vec3 xy2xyz(vec2 xy) {
if (xy.y == 0.0)
@@ -391,50 +339,26 @@ vec4 tonemap(vec4 color, mat3 dstXYZ) {
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]);
}
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor;
if (texType == 1)
pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);
// else if (texType == 2)
// pixColor = texture(texture0, v_texcoord);
else // assume rgba
pixColor = texture(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
}
pixColor = toNit(pixColor, sourceTF);
pixColor.rgb *= pixColor.a;
pixColor = tonemap(pixColor, dstxyz);
if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ)
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
pixColor *= sdrBrightnessMultiplier;
pixColor = fromLinearNit(pixColor, targetTF);
if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);
if (radius > 0.0)
pixColor = rounding(pixColor);
fragColor = pixColor * alpha;
vec4 doColorManagement(vec4 pixColor, int srcTF, mat4x2 srcPrimaries, int dstTF, mat4x2 dstPrimaries) {
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF);
mat3 srcxyz = primaries2xyz(srcPrimaries);
mat3 dstxyz;
if (srcPrimaries == dstPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(dstPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, srcPrimaries[3], dstxyz, dstPrimaries[3]);
}
pixColor = toNit(pixColor, srcTF);
pixColor.rgb *= pixColor.a;
pixColor = tonemap(pixColor, dstxyz);
pixColor = fromLinearNit(pixColor, dstTF);
if (srcTF == CM_TRANSFER_FUNCTION_SRGB && dstTF == CM_TRANSFER_FUNCTION_ST2084_PQ) {
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
pixColor.rgb /= pixColor.a;
pixColor.rgb *= sdrBrightnessMultiplier;
pixColor.rgb *= pixColor.a;
}
return pixColor;
}
)#"

View File

@@ -0,0 +1,141 @@
#version 100
precision highp float;
varying highp vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float radius;
uniform vec2 halfpixel;
uniform int passes;
uniform float vibrancy;
uniform float vibrancy_darkness;
// see http://alienryderflex.com/hsp.html
const float Pr = 0.299;
const float Pg = 0.587;
const float Pb = 0.114;
// Y is "v" ( brightness ). X is "s" ( saturation )
// see https://www.desmos.com/3d/a88652b9a4
// Determines if high brightness or high saturation is more important
const float a = 0.93;
const float b = 0.11;
const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors
//
// http://www.flong.com/archive/texts/code/shapers_circ/
float doubleCircleSigmoid(float x, float a) {
a = clamp(a, 0.0, 1.0);
float y = .0;
if (x <= a) {
y = a - sqrt(a * a - x * x);
} else {
y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.));
}
return y;
}
vec3 rgb2hsl(vec3 col) {
float red = col.r;
float green = col.g;
float blue = col.b;
float minc = min(col.r, min(col.g, col.b));
float maxc = max(col.r, max(col.g, col.b));
float delta = maxc - minc;
float lum = (minc + maxc) * 0.5;
float sat = 0.0;
float hue = 0.0;
if (lum > 0.0 && lum < 1.0) {
float mul = (lum < 0.5) ? (lum) : (1.0 - lum);
sat = delta / (mul * 2.0);
}
if (delta > 0.0) {
vec3 maxcVec = vec3(maxc);
vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red)));
vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta;
hue += dot(adds, masks);
hue /= 6.0;
if (hue < 0.0)
hue += 1.0;
}
return vec3(hue, sat, lum);
}
vec3 hsl2rgb(vec3 col) {
const float onethird = 1.0 / 3.0;
const float twothird = 2.0 / 3.0;
const float rcpsixth = 6.0;
float hue = col.x;
float sat = col.y;
float lum = col.z;
vec3 xt = vec3(0.0);
if (hue < onethird) {
xt.r = rcpsixth * (onethird - hue);
xt.g = rcpsixth * hue;
xt.b = 0.0;
} else if (hue < twothird) {
xt.r = 0.0;
xt.g = rcpsixth * (twothird - hue);
xt.b = rcpsixth * (hue - onethird);
} else
xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue));
xt = min(xt, 1.0);
float sat2 = 2.0 * sat;
float satinv = 1.0 - sat;
float luminv = 1.0 - lum;
float lum2m1 = (2.0 * lum) - 1.0;
vec3 ct = (sat2 * xt) + satinv;
vec3 rgb;
if (lum >= 0.5)
rgb = (luminv * ct) + lum2m1;
else
rgb = lum * ct;
return rgb;
}
void main() {
vec2 uv = v_texcoord * 2.0;
vec4 sum = texture2D(tex, uv) * 4.0;
sum += texture2D(tex, uv - halfpixel.xy * radius);
sum += texture2D(tex, uv + halfpixel.xy * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
vec4 color = sum / 8.0;
if (vibrancy == 0.0) {
gl_FragColor = color;
} else {
// Invert it so that it correctly maps to the config setting
float vibrancy_darkness1 = 1.0 - vibrancy_darkness;
// Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest.
vec3 hsl = rgb2hsl(color.rgb);
// Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow
float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1);
float b1 = b * vibrancy_darkness1;
float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0;
float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0);
vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2]));
gl_FragColor = vec4(newColor, color[3]);
}
}

View File

@@ -0,0 +1,23 @@
#version 100
precision highp float;
varying highp vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float radius;
uniform vec2 halfpixel;
void main() {
vec2 uv = v_texcoord / 2.0;
vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
gl_FragColor = sum / 12.0;
}

View File

@@ -0,0 +1,32 @@
#version 300 es
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float noise;
uniform float brightness;
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
// noise
float noiseHash = hash(v_texcoord);
float noiseAmount = (mod(noiseHash, 1.0) - 0.5);
pixColor.rgb += noiseAmount * noise;
// brightness
if (brightness < 1.0) {
pixColor.rgb *= brightness;
}
fragColor = pixColor;
}

View File

@@ -0,0 +1,28 @@
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float noise;
uniform float brightness;
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
// noise
float noiseHash = hash(v_texcoord);
float noiseAmount = (mod(noiseHash, 1.0) - 0.5);
pixColor.rgb += noiseAmount * noise;
// brightness
if (brightness < 1.0) {
pixColor.rgb *= brightness;
}
gl_FragColor = pixColor;
}

View File

@@ -0,0 +1,58 @@
#version 300 es
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float contrast;
uniform float brightness;
uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;
#include "CM.glsl"
float gain(float x, float k) {
float a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k);
return (x < 0.5) ? a : 1.0 - a;
}
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
if (skipCM == 0) {
if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) {
pixColor.rgb /= sdrBrightnessMultiplier;
}
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
}
pixColor = toNit(pixColor, sourceTF);
pixColor = fromLinearNit(pixColor, targetTF);
}
// contrast
if (contrast != 1.0) {
pixColor.r = gain(pixColor.r, contrast);
pixColor.g = gain(pixColor.g, contrast);
pixColor.b = gain(pixColor.b, contrast);
}
// brightness
if (brightness > 1.0) {
pixColor.rgb *= brightness;
}
fragColor = pixColor;
}

View File

@@ -0,0 +1,29 @@
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float contrast;
uniform float brightness;
float gain(float x, float k) {
float a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k);
return (x < 0.5) ? a : 1.0 - a;
}
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
// contrast
if (contrast != 1.0) {
pixColor.r = gain(pixColor.r, contrast);
pixColor.g = gain(pixColor.g, contrast);
pixColor.b = gain(pixColor.b, contrast);
}
// brightness
if (brightness > 1.0) {
pixColor.rgb *= brightness;
}
gl_FragColor = pixColor;
}

View File

@@ -0,0 +1,183 @@
#version 300 es
#extension GL_ARB_shading_language_include : enable
precision highp float;
in vec2 v_texcoord;
uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;
uniform vec2 fullSizeUntransformed;
uniform float radiusOuter;
uniform float thick;
// Gradients are in OkLabA!!!! {l, a, b, alpha}
uniform vec4 gradient[10];
uniform vec4 gradient2[10];
uniform int gradientLength;
uniform int gradient2Length;
uniform float angle;
uniform float angle2;
uniform float gradientLerp;
uniform float alpha;
#include "rounding.glsl"
#include "CM.glsl"
vec4 okLabAToSrgb(vec4 lab) {
float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0);
float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0);
float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0);
return vec4(fromLinearRGB(
vec3(
l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292,
l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965),
l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010
), CM_TRANSFER_FUNCTION_SRGB
), lab[3]);
}
vec4 getOkColorForCoordArray1(vec2 normalizedCoord) {
if (gradientLength < 2)
return gradient[0];
float finalAng = 0.0;
if (angle > 4.71 /* 270 deg */) {
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = 6.28 - angle;
} else if (angle > 3.14 /* 180 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = angle - 3.14;
} else if (angle > 1.57 /* 90 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
finalAng = 3.14 - angle;
} else {
finalAng = angle;
}
float sine = sin(finalAng);
float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1);
int bottom = int(floor(progress));
int top = bottom + 1;
return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress);
}
vec4 getOkColorForCoordArray2(vec2 normalizedCoord) {
if (gradient2Length < 2)
return gradient2[0];
float finalAng = 0.0;
if (angle2 > 4.71 /* 270 deg */) {
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = 6.28 - angle;
} else if (angle2 > 3.14 /* 180 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
normalizedCoord[1] = 1.0 - normalizedCoord[1];
finalAng = angle - 3.14;
} else if (angle2 > 1.57 /* 90 deg */) {
normalizedCoord[0] = 1.0 - normalizedCoord[0];
finalAng = 3.14 - angle2;
} else {
finalAng = angle2;
}
float sine = sin(finalAng);
float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1);
int bottom = int(floor(progress));
int top = bottom + 1;
return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress);
}
vec4 getColorForCoord(vec2 normalizedCoord) {
vec4 result1 = getOkColorForCoordArray1(normalizedCoord);
if (gradient2Length <= 0)
return okLabAToSrgb(result1);
vec4 result2 = getOkColorForCoordArray2(normalizedCoord);
return okLabAToSrgb(mix(result1, result2, gradientLerp));
}
layout(location = 0) out vec4 fragColor;
void main() {
highp vec2 pixCoord = vec2(gl_FragCoord);
highp vec2 pixCoordOuter = pixCoord;
highp vec2 originalPixCoord = v_texcoord;
originalPixCoord *= fullSizeUntransformed;
float additionalAlpha = 1.0;
vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0);
bool done = false;
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoordOuter = pixCoord;
pixCoord -= fullSize * 0.5 - radius;
pixCoordOuter -= fullSize * 0.5 - radiusOuter;
// center the pixes dont make it top-left
pixCoord += vec2(1.0, 1.0) / fullSize;
pixCoordOuter += vec2(1.0, 1.0) / fullSize;
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) {
float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower);
float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower);
float h = (thick / 2.0);
if (dist < radius - h) {
// lower
float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) {
// higher
float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
additionalAlpha *= normalized;
done = true;
} else if (distOuter < radiusOuter - h) {
additionalAlpha = 1.0;
done = true;
}
}
// now check for other shit
if (!done) {
// distance to all straight bb borders
float distanceT = originalPixCoord[1];
float distanceB = fullSizeUntransformed[1] - originalPixCoord[1];
float distanceL = originalPixCoord[0];
float distanceR = fullSizeUntransformed[0] - originalPixCoord[0];
// get the smallest
float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR));
if (smallest > thick)
discard;
}
if (additionalAlpha == 0.0)
discard;
pixColor = getColorForCoord(v_texcoord);
pixColor.rgb *= pixColor[3];
if (skipCM == 0)
pixColor = doColorManagement(pixColor, sourceTF, sourcePrimaries, targetTF, targetPrimaries);
pixColor *= alpha * additionalAlpha;
fragColor = pixColor;
}

View File

@@ -1,21 +1,11 @@
#pragma once
#extension GL_ARB_shading_language_include : enable
#include <string>
#include <format>
#include "SharedValues.hpp"
// makes a stencil without corners
inline const std::string FRAGBORDER1 = R"#(
precision highp float;
varying vec4 v_color;
varying vec2 v_texcoord;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform vec2 fullSizeUntransformed;
uniform float radius;
uniform float radiusOuter;
uniform float roundingPower;
uniform float thick;
// Gradients are in OkLabA!!!! {l, a, b, alpha}
@@ -28,6 +18,8 @@ uniform float angle2;
uniform float gradientLerp;
uniform float alpha;
#include "rounding.glsl"
float linearToGamma(float x) {
return x >= 0.0031308 ? 1.055 * pow(x, 0.416666666) - 0.055 : 12.92 * x;
}
@@ -135,13 +127,9 @@ void main() {
pixCoordOuter += vec2(1.0, 1.0) / fullSize;
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) {
// smoothing constant for the edge: more = blurrier, but smoother
const float SMOOTHING_CONSTANT = )#" +
std::format("{:.7f}", SHADER_ROUNDED_SMOOTHING_FACTOR) + R"#(;
float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower);
float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower);
float h = (thick / 2.0);
float h = (thick / 2.0);
if (dist < radius - h) {
// lower
@@ -184,4 +172,3 @@ void main() {
gl_FragColor = pixColor;
}
)#";

View File

@@ -0,0 +1,35 @@
#extension GL_ARB_shading_language_include : enable
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 v_texcoord;
uniform samplerExternalOES texture0;
uniform float alpha;
#include "rounding.glsl"
uniform int discardOpaque;
uniform int discardAlpha;
uniform int discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = texture2D(texture0, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0)
pixColor = rounding(pixColor);
gl_FragColor = pixColor * alpha;
}

View File

@@ -0,0 +1,64 @@
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D tex;
uniform float time; // quirk: time is set to 0 at the beginning, should be around 10 when crash.
uniform float distort;
uniform vec2 screenSize;
float rand(float co) {
return fract(sin(dot(vec2(co, co), vec2(12.9898, 78.233))) * 43758.5453);
}
float rand(vec2 co) {
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}
float noise(vec2 point) {
vec2 floored = floor(point);
vec2 fractal = fract(point);
fractal = fractal * fractal * (3.0 - 2.0 * fractal);
float mixed = mix(
mix(rand(floored), rand(floored + vec2(1.0, 0.0)), fractal.x),
mix(rand(floored + vec2(0.0,1.0)), rand(floored + vec2(1.0,1.0)), fractal.x), fractal.y);
return mixed * mixed;
}
void main() {
float ABERR_OFFSET = 4.0 * (distort / 5.5) * time;
float TEAR_AMOUNT = 9000.0 * (1.0 - (distort / 5.5));
float TEAR_BANDS = 108.0 / 2.0 * (distort / 5.5) * 2.0;
float MELT_AMOUNT = (distort * 8.0) / screenSize.y;
float NOISE = abs(mod(noise(v_texcoord) * distort * time * 2.771, 1.0)) * time / 10.0;
if (time < 2.0)
NOISE = 0.0;
float offset = (mod(rand(floor(v_texcoord.y * TEAR_BANDS)) * 318.772 * time, 20.0) - 10.0) / TEAR_AMOUNT;
vec2 blockOffset = vec2(((abs(mod(rand(floor(v_texcoord.x * 37.162)) * 721.43, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0),
((abs(mod(rand(floor(v_texcoord.y * 45.882)) * 733.923, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0));
if (time < 3.0)
blockOffset = vec2(0,0);
float meltSeed = abs(mod(rand(floor(v_texcoord.x * screenSize.x * 17.719)) * 281.882, 1.0));
if (meltSeed < 0.8) {
meltSeed = 0.0;
} else {
meltSeed *= 25.0 * NOISE;
}
float meltAmount = MELT_AMOUNT * meltSeed;
vec2 pixCoord = vec2(v_texcoord.x + offset + NOISE * 3.0 / screenSize.x + blockOffset.x, v_texcoord.y - meltAmount + 0.02 * NOISE / screenSize.x + NOISE * 3.0 / screenSize.y + blockOffset.y);
vec4 pixColor = texture2D(tex, pixCoord);
vec4 pixColorLeft = texture2D(tex, pixCoord + vec2(ABERR_OFFSET / screenSize.x, 0));
vec4 pixColorRight = texture2D(tex, pixCoord + vec2(-ABERR_OFFSET / screenSize.x, 0));
pixColor[0] = pixColorLeft[0];
pixColor[2] = pixColorRight[2];
pixColor[0] += distort / 90.0;
gl_FragColor = pixColor;
}

View File

@@ -0,0 +1,7 @@
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}

View File

@@ -0,0 +1,14 @@
#extension GL_ARB_shading_language_include : enable
precision highp float;
varying vec4 v_color;
#include "rounding.glsl"
void main() {
vec4 pixColor = v_color;
if (radius > 0.0)
pixColor = rounding(pixColor);
gl_FragColor = pixColor;
}

View File

@@ -0,0 +1,36 @@
#extension GL_ARB_shading_language_include : enable
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform float alpha;
#include "rounding.glsl"
uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = texture2D(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
if (radius > 0.0)
pixColor = rounding(pixColor);
gl_FragColor = pixColor * alpha;
}

View File

@@ -0,0 +1,8 @@
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;
uniform sampler2D texMatte;
void main() {
gl_FragColor = texture2D(tex, v_texcoord) * texture2D(texMatte, v_texcoord)[0]; // I know it only uses R, but matte should be black/white anyways.
}

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