add anti flashbang

could be improve with smooth fading
This commit is contained in:
end-4
2025-10-22 23:56:50 +02:00
parent a4d2a720d0
commit b35ef90916
5 changed files with 105 additions and 8 deletions
@@ -303,6 +303,9 @@ Singleton {
property string to: "06:30" // Format: "HH:mm", 24-hour time
property int colorTemperature: 5000
}
property JsonObject antiFlashbang: JsonObject {
property bool enable: false
}
}
property JsonObject lock: JsonObject {
@@ -50,7 +50,7 @@ Rectangle {
property real targetY: root.height / 2 - root.backgroundHeight / 2
y: root.show ? targetY : (targetY - root.backgroundAnimationMovementDistance)
implicitWidth: 350
implicitHeight: 0
implicitHeight: contentColumn.implicitHeight + dialogBackground.radius * 2
Behavior on implicitHeight {
NumberAnimation {
id: dialogBackgroundHeightAnimation
@@ -34,7 +34,6 @@ WindowDialog {
id: nightLightColumn
Layout.topMargin: -16
Layout.fillWidth: true
Layout.fillHeight: true
ConfigSwitch {
anchors {
@@ -81,6 +80,39 @@ WindowDialog {
}
}
WindowDialogSectionHeader {
text: Translation.tr("Anti-flashbang (experimental)")
}
WindowDialogSeparator {
Layout.topMargin: -22
Layout.leftMargin: 0
Layout.rightMargin: 0
}
Column {
id: antiFlashbangColumn
Layout.topMargin: -16
Layout.fillWidth: true
ConfigSwitch {
anchors {
left: parent.left
right: parent.right
}
iconSize: Appearance.font.pixelSize.larger
buttonIcon: "destruction"
text: Translation.tr("Enable")
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")
}
}
}
WindowDialogSectionHeader {
text: Translation.tr("Brightness")
}
@@ -95,7 +127,7 @@ WindowDialog {
id: brightnessColumn
Layout.topMargin: -16
Layout.fillWidth: true
// Layout.fillHeight: true
Layout.fillHeight: true
WindowDialogSlider {
anchors {
@@ -79,10 +79,7 @@ AbstractQuickPanel {
delegate: ButtonGroup {
id: toggleRow
required property int index
property var modelData: {
print(JSON.stringify(root.toggleRows[index]))
return root.toggleRows[index]
}
property var modelData: root.toggleRows[index]
property int startingIndex: {
const rows = root.toggleRows;
let sum = 0;
@@ -4,6 +4,8 @@ pragma ComponentBehavior: Bound
// From https://github.com/caelestia-dots/shell with modifications.
// License: GPLv3
import qs.modules.common
import qs.modules.common.functions
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
@@ -85,6 +87,8 @@ Singleton {
}
property int rawMaxBrightness: 100
property real brightness
property real brightnessMultiplier: 1.0
property real multipliedBrightness: Math.max(0, Math.min(1, brightness * brightnessMultiplier))
property bool ready: false
onBrightnessChanged: {
@@ -120,7 +124,8 @@ Singleton {
}
function syncBrightness() {
const rounded = Math.round(monitor.brightness * monitor.rawMaxBrightness);
const brightnessValue = monitor.multipliedBrightness
const rounded = Math.round(brightnessValue * monitor.rawMaxBrightness);
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "--class", "backlight", "s", rounded, "--quiet"];
setProc.startDetached();
}
@@ -131,6 +136,11 @@ Singleton {
setTimer.restart();
}
function setBrightnessMultiplier(value: real): void {
monitor.brightnessMultiplier = value;
setTimer.restart();
}
Component.onCompleted: {
initialize();
}
@@ -146,6 +156,61 @@ Singleton {
BrightnessMonitor {}
}
// Anti-flashbang
property string screenshotDir: "/tmp/quickshell/brightness/antiflashbang"
function brightnessMultiplierForLightness(x: real): real {
// 6.600135 + 216.360356 * e^(-0.0811129189x)
// Division by 100 is to normalize to [0, 1]
return (6.600135 + 216.360356 * Math.pow(Math.E, -0.0811129189 * x)) / 100.0;
}
Variants {
model: Quickshell.screens
Scope {
id: screenScope
required property var modelData
property string screenName: modelData.name
property string screenshotPath: `${root.screenshotDir}/screenshot-${screenName}.png`
Connections {
enabled: Config.options.light.antiFlashbang.enable
target: Hyprland
function onRawEvent(event) {
if (["workspacev2"].includes(event.name)) {
screenshotTimer.restart();
}
}
}
Timer {
id: screenshotTimer
interval: 700 // This is what I have for a Hyprland ws anim
onTriggered: {
screenshotProc.running = false;
screenshotProc.running = true;
}
}
Process {
id: screenshotProc
command: ["bash", "-c",
`mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}'`
+ ` && grim -o '${StringUtils.shellSingleQuoteEscape(screenScope.screenName)}' '${StringUtils.shellSingleQuoteEscape(screenScope.screenshotPath)}'`
+ ` && magick '${StringUtils.shellSingleQuoteEscape(screenScope.screenshotPath)}' -colorspace Gray -format "%[fx:mean*100]" info:`
]
stdout: StdioCollector {
id: lightnessCollector
onStreamFinished: {
const lightness = lightnessCollector.text
const newMultiplier = root.brightnessMultiplierForLightness(parseFloat(lightness))
print(lightness, "->", newMultiplier)
Brightness.getMonitorForScreen(screenScope.modelData).setBrightnessMultiplier(newMultiplier)
}
}
}
}
}
// External trigger points
IpcHandler {
target: "brightness"