From 574a2a11e76f1c843ab4c7a58f5fec236fecd9d9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:00:31 +0700 Subject: [PATCH] night light: use hyprsunset <- gammastep --- .config/hypr/hyprland/execs.conf | 2 +- .../quickshell/ii/modules/common/Config.qml | 12 +- .../sidebarRight/quickToggles/NightLight.qml | 39 ++---- .config/quickshell/ii/services/DateTime.qml | 9 +- .config/quickshell/ii/services/Hyprsunset.qml | 117 ++++++++++++++++++ .config/quickshell/ii/shell.qml | 3 +- .../illogical-impulse-backlight/PKGBUILD | 1 - 7 files changed, 148 insertions(+), 35 deletions(-) create mode 100644 .config/quickshell/ii/services/Hyprsunset.qml diff --git a/.config/hypr/hyprland/execs.conf b/.config/hypr/hyprland/execs.conf index 0d04c512..6d92de1c 100644 --- a/.config/hypr/hyprland/execs.conf +++ b/.config/hypr/hyprland/execs.conf @@ -1,5 +1,5 @@ # Bar, wallpaper -exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh & gammastep +exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh exec-once = qs -c $qsConfig & # Input method diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index e97a537c..7462d26e 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -8,6 +8,7 @@ Singleton { id: root property string filePath: Directories.shellConfigPath property alias options: configOptionsJsonAdapter + property bool ready: false function setNestedValue(nestedKey, value) { let keys = nestedKey.split("."); @@ -41,10 +42,10 @@ Singleton { FileView { path: root.filePath - watchChanges: true onFileChanged: reload() onAdapterUpdated: writeAdapter() + onLoaded: root.ready = true onLoadFailed: error => { if (error == FileViewError.FileNotFound) { writeAdapter(); @@ -171,6 +172,15 @@ Singleton { } } + property JsonObject light: JsonObject { + property JsonObject night: JsonObject { + property bool automatic: true + property string from: "19:00" // Format: "HH:mm", 24-hour time + property string to: "06:30" // Format: "HH:mm", 24-hour time + property int colorTemperature: 5000 + } + } + property JsonObject networking: JsonObject { property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml index 6cab856c..f0265126 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml @@ -1,41 +1,28 @@ +import QtQuick import qs.modules.common import qs.modules.common.widgets import qs +import qs.services import Quickshell.Io QuickToggleButton { id: nightLightButton - property bool enabled: false + property bool enabled: Hyprsunset.active toggled: enabled - buttonIcon: "nightlight" + buttonIcon: Config.options.light.night.automatic ? "night_sight_auto" : "bedtime" onClicked: { - nightLightButton.enabled = !nightLightButton.enabled - if (enabled) { - nightLightOn.startDetached() - } - else { - nightLightOff.startDetached() - } + Hyprsunset.toggle() } - Process { - id: nightLightOn - command: ["gammastep"] + + altAction: () => { + Config.options.light.night.automatic = !Config.options.light.night.automatic } - Process { - id: nightLightOff - command: ["pkill", "gammastep"] - } - Process { - id: updateNightLightState - running: true - command: ["pidof", "gammastep"] - stdout: SplitParser { - onRead: (data) => { // if not empty then set toggled to true - nightLightButton.enabled = data.length > 0 - } - } + + Component.onCompleted: { + Hyprsunset.fetchState() } + StyledToolTip { - content: Translation.tr("Night Light") + content: Translation.tr("Night Light | Right-click to toggle Auto mode") } } diff --git a/.config/quickshell/ii/services/DateTime.qml b/.config/quickshell/ii/services/DateTime.qml index e1b0afa3..16dc6c4e 100644 --- a/.config/quickshell/ii/services/DateTime.qml +++ b/.config/quickshell/ii/services/DateTime.qml @@ -9,16 +9,15 @@ pragma ComponentBehavior: Bound * A nice wrapper for date and time strings. */ Singleton { + property var clock: SystemClock { + id: clock + precision: SystemClock.Minutes + } property string time: Qt.locale().toString(clock.date, Config.options?.time.format ?? "hh:mm") property string date: Qt.locale().toString(clock.date, Config.options?.time.dateFormat ?? "dddd, dd/MM") property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy") property string uptime: "0h, 0m" - SystemClock { - id: clock - precision: SystemClock.Minutes - } - Timer { interval: 10 running: true diff --git a/.config/quickshell/ii/services/Hyprsunset.qml b/.config/quickshell/ii/services/Hyprsunset.qml new file mode 100644 index 00000000..d6def43c --- /dev/null +++ b/.config/quickshell/ii/services/Hyprsunset.qml @@ -0,0 +1,117 @@ +pragma Singleton + +import QtQuick +import qs.modules.common +import Quickshell +import Quickshell.Io + +/** + * Simple hyprsunset service with automatic mode. + * In theory we don't need this because hyprsunset has a config file, but it somehow doesn't work. + * It should also be possible to control it via hyprctl, but it doesn't work consistently either so we're just killing and launching. + */ +Singleton { + id: root + property var manualActive + property string from: Config.options?.light?.night?.from ?? "19:00" // Default to 7 PM + property string to: Config.options?.light?.night?.to ?? "06:30" // Default to 6:30 AM + property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true) + property int colorTemperature: Config.options?.light?.night?.colorTemperature ?? 5000 // Default color temperature + property bool shouldBeOn + property bool firstEvaluation: true + property bool active: false + + property int fromHour: Number(from.split(":")[0]) + property int fromMinute: Number(from.split(":")[1]) + property int toHour: Number(to.split(":")[0]) + property int toMinute: Number(to.split(":")[1]) + + property int clockHour: DateTime.clock.hours + property int clockMinute: DateTime.clock.minutes + + + function isNoLater(hour1, minute1, hour2, minute2) { + if (hour1 < hour2) + return true; + if (hour1 === hour2 && minute1 < minute2) + return true; + return false; + } + + + onClockMinuteChanged: reEvaluate() + onAutomaticChanged: { + root.manualActive = undefined; + root.firstEvaluation = true; + reEvaluate(); + } + function reEvaluate() { + const toHourIsNextDay = !isNoLater(fromHour, fromMinute, toHour, toMinute); + const toHourWrapped = toHourIsNextDay ? toHour + 24 : toHour; + const toMinuteWrapped = toMinute; + root.shouldBeOn = isNoLater(fromHour, fromMinute, clockHour, clockMinute) && isNoLater(clockHour, clockMinute, toHourWrapped, toMinuteWrapped); + if (firstEvaluation) { + firstEvaluation = false; + root.ensureState(); + } + } + + onShouldBeOnChanged: ensureState() + function ensureState() { + // console.log("[Hyprsunset] Ensuring state:", root.shouldBeOn, "Automatic mode:", root.automatic); + if (!root.automatic || root.manualActive !== undefined) + return; + if (root.shouldBeOn) { + root.enable(); + } else { + root.disable(); + } + } + + function load() { } // Dummy to force init + + function enable() { + root.active = true; + // console.log("[Hyprsunset] Enabling"); + Quickshell.execDetached(["bash", "-c", `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]); + } + + function disable() { + root.active = false; + // console.log("[Hyprsunset] Disabling"); + Quickshell.execDetached(["bash", "-c", `pkill hyprsunset`]); + } + + function fetchState() { + fetchProc.running = true; + } + + Process { + id: fetchProc + running: true + command: ["bash", "-c", "hyprctl hyprsunset temperature"] + stdout: StdioCollector { + id: stateCollector + onStreamFinished: { + const output = stateCollector.text.trim(); + if (output.length == 0 || output.startsWith("Couldn't")) + root.active = false; + else + root.active = (output != "6500"); + // console.log("[Hyprsunset] Fetched state:", output, "->", root.active); + } + } + } + + function toggle() { + if (root.manualActive === undefined) + root.manualActive = root.active; + + root.manualActive = !root.manualActive; + if (root.manualActive) { + root.enable(); + } else { + root.disable(); + } + } +} diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 7cb86a03..9bba7104 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -49,9 +49,10 @@ ShellRoot { // Force initialization of some singletons Component.onCompleted: { - MaterialThemeLoader.reapplyTheme() Cliphist.refresh() FirstRunExperience.load() + Hyprsunset.load() + MaterialThemeLoader.reapplyTheme() } LazyLoader { active: enableBar; component: Bar {} } diff --git a/arch-packages/illogical-impulse-backlight/PKGBUILD b/arch-packages/illogical-impulse-backlight/PKGBUILD index 2525be9d..451c7e5d 100644 --- a/arch-packages/illogical-impulse-backlight/PKGBUILD +++ b/arch-packages/illogical-impulse-backlight/PKGBUILD @@ -5,7 +5,6 @@ pkgdesc='Illogical Impulse Backlight Dependencies' arch=(any) license=(None) depends=( - gammastep geoclue brightnessctl ddcutil