add anti-flashbang using shader

This commit is contained in:
end-4
2026-03-19 12:34:32 +01:00
parent 5ac40d5445
commit 1225c014e8
4 changed files with 109 additions and 6 deletions
@@ -8,10 +8,10 @@ QuickToggleModel {
name: Translation.tr("Anti-flashbang")
tooltipText: Translation.tr("Anti-flashbang")
icon: "flash_off"
toggled: Config.options.light.antiFlashbang.enable
toggled: HyprlandAntiFlashbangShader.enabled
mainAction: () => {
Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable;
HyprlandAntiFlashbangShader.toggle()
}
hasMenu: true
}
@@ -42,7 +42,7 @@ WindowDialog {
right: parent.right
}
iconSize: Appearance.font.pixelSize.larger
buttonIcon: "lightbulb"
buttonIcon: "check"
text: Translation.tr("Enable now")
checked: Hyprsunset.active
onCheckedChanged: {
@@ -102,14 +102,32 @@ WindowDialog {
right: parent.right
}
iconSize: Appearance.font.pixelSize.larger
buttonIcon: "flash_off"
text: Translation.tr("Enable")
buttonIcon: "filter"
text: Translation.tr("Content adjustment")
checked: HyprlandAntiFlashbangShader.enabled
onCheckedChanged: {
if (checked) HyprlandAntiFlashbangShader.enable()
else HyprlandAntiFlashbangShader.disable()
}
StyledToolTip {
text: Translation.tr("<b>Dims screen content</b> as needed.<br><br>Pros: Immediately responsive<br>Cons: Expensive and can hurt color accuracy<br><br><i>Uses a Hyprland screen shader</i>")
}
}
ConfigSwitch {
anchors {
left: parent.left
right: parent.right
}
iconSize: Appearance.font.pixelSize.larger
buttonIcon: "light_mode"
text: Translation.tr("Brightness adjustment")
checked: Config.options.light.antiFlashbang.enable
onCheckedChanged: {
Config.options.light.antiFlashbang.enable = checked;
}
StyledToolTip {
text: Translation.tr("Example use case: eroge on one workspace, dark Discord window on another")
text: Translation.tr("Adapts the <b>display (physical screen) brightness</b><br><br>Pros: Less expensive, retains colors<br>Cons: Not immediately responsive<br><br><i>Adjusts display brightness after each Hyprland IPC event</i>")
}
}
}
@@ -0,0 +1,38 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.modules.common.models.hyprland
Singleton {
id: root
readonly property string shaderPath: Quickshell.shellPath("services/hyprlandAntiFlashbangShader/anti-flashbang.glsl")
property bool enabled: confOpt.value == shaderPath
function enable() {
HyprlandConfig.setMany({
"decoration:screen_shader": root.shaderPath,
"debug:damage_tracking": 1, // Turn off dmg tracking to prevent weird flashes. 1 = monitor only
});
}
function disable() {
HyprlandConfig.resetMany([
"decoration:screen_shader",
"debug:damage_tracking"
]);
}
function toggle() {
if (root.enabled) disable()
else enable()
}
HyprlandConfigOption {
id: confOpt
key: "decoration:screen_shader"
}
}
@@ -0,0 +1,47 @@
#version 300 es
precision highp float;
in vec2 v_texcoord;
uniform sampler2D tex;
out vec4 fragColor;
float overlayOpacityForBrightness(float x) {
// Note: range 0 to 1
// Will a fancy curve help?... I'll have to experiment more at night
// float y = pow(x, 2.0) * 0.75;
// float y = (1.0 - exp(-x))*1.15;
// float y = (1.0 - exp(-pow((x-0.15), 0.6)))*1.18;
float y = x*0.75;
return min(max(y, 0.001), 1.0);
}
void main() {
// 1. Get the current pixel color
vec4 pixColor = texture(tex, v_texcoord);
// 2. Calculate average screen brightness
vec3 totalRGB = vec3(0.0);
float samples = 0.0;
// We use a nested loop to create a 10x10 grid (100 samples)
// This is dense enough to catch small icons/text but light enough to run fast.
for(float x = 0.05; x < 1.0; x += 0.1) {
for(float y = 0.05; y < 1.0; y += 0.1) {
totalRGB += texture(tex, vec2(x, y)).rgb;
samples++;
}
}
vec3 avgColor = totalRGB / samples;
float globalBrightness = dot(avgColor, vec3(0.2126, 0.7152, 0.0722));
// 3. Get the specific opacity for this brightness level
float opacity = overlayOpacityForBrightness(globalBrightness);
// 4. Apply the "black overlay" effect
vec3 outColor = mix(pixColor.rgb, vec3(0.0), opacity);
fragColor = vec4(outColor, pixColor.a);
}