mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-31 20:03:48 -07:00
Compare commits
64 Commits
v0.48.0
...
revert-999
Author | SHA1 | Date | |
---|---|---|---|
|
6310b8a1fe | ||
|
b83c9f5c6f | ||
|
d775686380 | ||
|
0dc531c4a7 | ||
|
0a7e2cb152 | ||
|
4f991610d0 | ||
|
ea852965ff | ||
|
260d8e1f71 | ||
|
642f394eb3 | ||
|
b15c2bfff6 | ||
|
da86db43d4 | ||
|
70ae99f521 | ||
|
a8eda7f978 | ||
|
8a8f394da7 | ||
|
51838fb5f5 | ||
|
85f874d10f | ||
|
9b3925009a | ||
|
3c128679ee | ||
|
e96b8ce4cc | ||
|
433b7881a3 | ||
|
ed05f14300 | ||
|
c62fb08da6 | ||
|
8ba20fcae1 | ||
|
ff97d18c4c | ||
|
5e8bb71785 | ||
|
b496e2c718 | ||
|
a41b8d5e97 | ||
|
8654029f86 | ||
|
a4e6c5d678 | ||
|
3a47c73f34 | ||
|
1f0fd79b91 | ||
|
d1a59ec39e | ||
|
4c987b20e2 | ||
|
2309270752 | ||
|
79b526a041 | ||
|
075bbecabd | ||
|
8aaffda969 | ||
|
10a335631e | ||
|
da2d7c3971 | ||
|
05eb0aa43d | ||
|
fc7223edc0 | ||
|
86c279d7d0 | ||
|
46b00a4a86 | ||
|
4a79eea6dc | ||
|
7374a023ef | ||
|
a46576afc3 | ||
|
10035a85cc | ||
|
c93140a5f1 | ||
|
5380cbcdda | ||
|
9ea76428b6 | ||
|
0cd04bd666 | ||
|
1c2b9a9ce3 | ||
|
cec084c178 | ||
|
c2ef8fcc00 | ||
|
3fc3521a97 | ||
|
9a67354fa2 | ||
|
f7ba86d1f3 | ||
|
f3db1b172c | ||
|
2a6d070774 | ||
|
aec69131cd | ||
|
4b968e5bc1 | ||
|
a852461c7d | ||
|
e4abf26069 | ||
|
006bd9eef5 |
116
.github/ISSUE_TEMPLATE/bug.yml
vendored
116
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -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
|
||||
|
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -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
|
||||
|
28
.github/workflows/stale.yml
vendored
28
.github/workflows/stale.yml
vendored
@@ -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
2
.gitignore
vendored
@@ -28,6 +28,8 @@ protocols/*.c*
|
||||
protocols/*.h*
|
||||
.ccls-cache
|
||||
*.so
|
||||
src/render/shaders/*.inc
|
||||
src/render/shaders/Shaders.hpp
|
||||
|
||||
hyprctl/hyprctl
|
||||
|
||||
|
@@ -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"
|
||||
)
|
||||
|
2
Makefile
2
Makefile
@@ -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
|
||||
|
@@ -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
30
flake.lock
generated
@@ -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": {
|
||||
|
@@ -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)"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
})
|
||||
|
@@ -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 {
|
||||
|
@@ -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'
|
||||
|
24
scripts/generateShaderIncludes.sh
Executable file
24
scripts/generateShaderIncludes.sh
Executable 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
|
@@ -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)
|
||||
|
@@ -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 don’t 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.",
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 ###
|
||||
#####################
|
||||
|
@@ -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);
|
||||
|
@@ -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});
|
||||
|
@@ -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};
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
123
src/helpers/AsyncDialogBox.cpp
Normal file
123
src/helpers/AsyncDialogBox.cpp
Normal 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;
|
||||
}
|
45
src/helpers/AsyncDialogBox.hpp
Normal file
45
src/helpers/AsyncDialogBox.hpp
Normal 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;
|
||||
};
|
@@ -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());
|
||||
|
@@ -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();
|
||||
}
|
@@ -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;
|
@@ -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");
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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};
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -68,6 +68,7 @@ class CEventLoopManager {
|
||||
wl_event_source* m_configWatcherInotifySource = nullptr;
|
||||
|
||||
friend class CSyncTimeline;
|
||||
friend class CAsyncDialogBox;
|
||||
};
|
||||
|
||||
inline UP<CEventLoopManager> g_pEventLoopManager;
|
||||
|
@@ -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() {
|
||||
|
@@ -114,7 +114,7 @@ class CInputManager {
|
||||
|
||||
Vector2D getMouseCoordsInternal();
|
||||
void refocus();
|
||||
void refocusLastWindow(PHLMONITOR pMonitor);
|
||||
bool refocusLastWindow(PHLMONITOR pMonitor);
|
||||
void simulateMouseMovement();
|
||||
void sendMotionEventsToFocused();
|
||||
|
||||
|
@@ -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);
|
||||
|
234
src/managers/permissions/DynamicPermissionManager.cpp
Normal file
234
src/managers/permissions/DynamicPermissionManager.cpp
Normal 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; });
|
||||
}
|
85
src/managers/permissions/DynamicPermissionManager.hpp
Normal file
85
src/managers/permissions/DynamicPermissionManager.hpp
Normal 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;
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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() {
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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");
|
||||
});
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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))
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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:
|
||||
|
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "shaders/Textures.hpp"
|
||||
#include "shaders/Shadow.hpp"
|
||||
#include "shaders/Border.hpp"
|
@@ -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() {
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
constexpr float SHADER_ROUNDED_SMOOTHING_FACTOR = M_PI / 5.34665792551;
|
@@ -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;
|
||||
}
|
||||
)#";
|
55
src/render/shaders/glsl/CM.frag
Normal file
55
src/render/shaders/glsl/CM.frag
Normal 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;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
)#"
|
141
src/render/shaders/glsl/blur1.frag
Normal file
141
src/render/shaders/glsl/blur1.frag
Normal 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]);
|
||||
}
|
||||
}
|
23
src/render/shaders/glsl/blur2.frag
Normal file
23
src/render/shaders/glsl/blur2.frag
Normal 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;
|
||||
}
|
32
src/render/shaders/glsl/blurfinish.frag
Normal file
32
src/render/shaders/glsl/blurfinish.frag
Normal 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;
|
||||
}
|
28
src/render/shaders/glsl/blurfinish_legacy.frag
Normal file
28
src/render/shaders/glsl/blurfinish_legacy.frag
Normal 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;
|
||||
}
|
58
src/render/shaders/glsl/blurprepare.frag
Normal file
58
src/render/shaders/glsl/blurprepare.frag
Normal 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;
|
||||
}
|
29
src/render/shaders/glsl/blurprepare_legacy.frag
Normal file
29
src/render/shaders/glsl/blurprepare_legacy.frag
Normal 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;
|
||||
}
|
183
src/render/shaders/glsl/border.frag
Normal file
183
src/render/shaders/glsl/border.frag
Normal 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;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
)#";
|
35
src/render/shaders/glsl/ext.frag
Normal file
35
src/render/shaders/glsl/ext.frag
Normal 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;
|
||||
}
|
64
src/render/shaders/glsl/glitch.frag
Normal file
64
src/render/shaders/glsl/glitch.frag
Normal 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;
|
||||
}
|
7
src/render/shaders/glsl/passthru.frag
Normal file
7
src/render/shaders/glsl/passthru.frag
Normal 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);
|
||||
}
|
14
src/render/shaders/glsl/quad.frag
Normal file
14
src/render/shaders/glsl/quad.frag
Normal 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;
|
||||
}
|
36
src/render/shaders/glsl/rgba.frag
Normal file
36
src/render/shaders/glsl/rgba.frag
Normal 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;
|
||||
}
|
8
src/render/shaders/glsl/rgbamatte.frag
Normal file
8
src/render/shaders/glsl/rgbamatte.frag
Normal 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
Reference in New Issue
Block a user