1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00
Files
2026-07-26 23:22:22 -04:00

542 lines
18 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("ThemeAutoService")
property bool active: false
Component.onCompleted: {
if (typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled) {
start();
}
}
Connections {
target: SessionData
enabled: typeof SessionData !== "undefined"
function onThemeModeAutoEnabledChanged() {
if (SessionData.themeModeAutoEnabled) {
root.start();
} else {
root.stop();
}
}
function onThemeModeAutoModeChanged() {
if (root.active) {
root.evaluate();
root.syncTimeSchedule();
root.syncLocationSchedule();
}
}
function onThemeModeStartHourChanged() {
if (root.active && !SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onThemeModeStartMinuteChanged() {
if (root.active && !SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onThemeModeEndHourChanged() {
if (root.active && !SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onThemeModeEndMinuteChanged() {
if (root.active && !SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onThemeModeShareGammaSettingsChanged() {
if (root.active) {
root.evaluate();
root.syncTimeSchedule();
root.syncLocationSchedule();
}
}
function onNightModeStartHourChanged() {
if (root.active && SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onNightModeStartMinuteChanged() {
if (root.active && SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onNightModeEndHourChanged() {
if (root.active && SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onNightModeEndMinuteChanged() {
if (root.active && SessionData.themeModeShareGammaSettings) {
root.evaluate();
root.syncTimeSchedule();
}
}
function onLatitudeChanged() {
if (root.active && SessionData.themeModeAutoMode === "location") {
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
});
}
root.evaluate();
root.syncLocationSchedule();
}
}
function onLongitudeChanged() {
if (root.active && SessionData.themeModeAutoMode === "location") {
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
});
}
root.evaluate();
root.syncLocationSchedule();
}
}
function onNightModeUseIPLocationChanged() {
if (root.active && SessionData.themeModeAutoMode === "location") {
if (typeof DMSService !== "undefined") {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": SessionData.nightModeUseIPLocation
}, response => {
if (!response.error && !SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
});
}
});
}
root.evaluate();
root.syncLocationSchedule();
}
}
}
Connections {
target: DisplayService
enabled: typeof DisplayService !== "undefined" && typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location" && !root.backendAvailable()
function onGammaIsDayChanged() {
if (Theme.isLightMode !== DisplayService.gammaIsDay) {
Theme.setLightMode(DisplayService.gammaIsDay, true, true);
}
}
}
Connections {
target: DMSService
function onThemeAutoStateUpdate(data) {
if (!SessionData.themeModeAutoEnabled) {
return;
}
root.applyBackendState(data);
}
function onConnectionStateChanged() {
if (DMSService.isConnected && SessionData.themeModeAutoMode === "time") {
root.syncTimeSchedule();
}
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
root.syncLocationSchedule();
}
if (root.backendAvailable() && SessionData.themeModeAutoEnabled) {
DMSService.sendRequest("theme.auto.getState", null, response => {
if (response && response.result) {
root.applyBackendState(response.result);
}
});
}
if (!SessionData.themeModeAutoEnabled) {
return;
}
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
if (SessionData.nightModeUseIPLocation) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": true
}, response => {
if (!response.error) {
root.log.info("Theme automation: IP location enabled after connection");
}
});
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": false
}, response => {
if (!response.error) {
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
}, locationResponse => {
if (locationResponse?.error) {
root.log.warn("Theme automation: Failed to set location", locationResponse.error);
}
});
}
});
} else {
root.log.warn("Theme automation: No location configured");
}
}
}
}
Connections {
target: SessionService
enabled: SessionData.themeModeAutoEnabled
function onSessionUnlocked() {
root.refresh();
}
function onSessionResumed() {
root.refresh();
}
}
function refresh() {
if (!backendAvailable()) {
evaluate();
return;
}
DMSService.sendRequest("theme.auto.trigger", {});
}
function backendAvailable() {
return typeof DMSService !== "undefined" && DMSService.isConnected && Array.isArray(DMSService.capabilities) && DMSService.capabilities.includes("theme.auto");
}
function applyBackendState(state) {
if (!state) {
return;
}
if (state.config && state.config.mode && state.config.mode !== SessionData.themeModeAutoMode) {
return;
}
if (typeof SessionData !== "undefined" && state.nextTransition !== undefined) {
SessionData.themeModeNextTransition = state.nextTransition || "";
}
if (state.isLight !== undefined && Theme.isLightMode !== state.isLight) {
Theme.setLightMode(state.isLight, true, true);
}
}
function syncTimeSchedule() {
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
return;
}
if (!DMSService.isConnected) {
return;
}
const timeModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "time";
if (!timeModeActive) {
return;
}
DMSService.sendRequest("theme.auto.setMode", {
"mode": "time"
});
const shareSettings = SessionData.themeModeShareGammaSettings;
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
DMSService.sendRequest("theme.auto.setSchedule", {
"startHour": startHour,
"startMinute": startMinute,
"endHour": endHour,
"endMinute": endMinute
}, response => {
if (response && response.error) {
log.error("Theme automation: Failed to sync time schedule:", response.error);
}
});
DMSService.sendRequest("theme.auto.setEnabled", {
"enabled": true
});
DMSService.sendRequest("theme.auto.trigger", {});
}
function syncLocationSchedule() {
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
return;
}
if (!DMSService.isConnected) {
return;
}
const locationModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location";
if (!locationModeActive) {
return;
}
DMSService.sendRequest("theme.auto.setMode", {
"mode": "location"
});
if (SessionData.nightModeUseIPLocation) {
DMSService.sendRequest("theme.auto.setUseIPLocation", {
"use": true
});
} else {
DMSService.sendRequest("theme.auto.setUseIPLocation", {
"use": false
});
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
DMSService.sendRequest("theme.auto.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
});
}
}
DMSService.sendRequest("theme.auto.setEnabled", {
"enabled": true
});
DMSService.sendRequest("theme.auto.trigger", {});
}
function evaluate() {
if (typeof SessionData === "undefined" || !SessionData.themeModeAutoEnabled) {
return;
}
if (backendAvailable()) {
DMSService.sendRequest("theme.auto.getState", null, response => {
if (response && response.result) {
applyBackendState(response.result);
}
});
return;
}
const mode = SessionData.themeModeAutoMode;
if (mode === "location") {
evaluateLocation();
} else {
evaluateTime();
}
}
function evaluateLocation() {
if (typeof DisplayService !== "undefined") {
const shouldBeLight = DisplayService.gammaIsDay;
if (Theme.isLightMode !== shouldBeLight) {
Theme.setLightMode(shouldBeLight, true, true);
}
return;
}
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
const shouldBeLight = calculateIsDaytime(SessionData.latitude, SessionData.longitude);
if (Theme.isLightMode !== shouldBeLight) {
Theme.setLightMode(shouldBeLight, true, true);
}
return;
}
if (root.active) {
if (SessionData.nightModeUseIPLocation) {
log.warn("Theme automation: Waiting for IP location from backend");
} else {
log.warn("Theme automation: Location mode requires coordinates");
}
}
}
function evaluateTime() {
const shareSettings = SessionData.themeModeShareGammaSettings;
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
const now = new Date();
const currentMinutes = now.getHours() * 60 + now.getMinutes();
const startMinutes = startHour * 60 + startMinute;
const endMinutes = endHour * 60 + endMinute;
let shouldBeLight;
if (startMinutes < endMinutes) {
shouldBeLight = currentMinutes < startMinutes || currentMinutes >= endMinutes;
} else {
shouldBeLight = currentMinutes >= endMinutes && currentMinutes < startMinutes;
}
if (Theme.isLightMode !== shouldBeLight) {
Theme.setLightMode(shouldBeLight, true, true);
}
}
function calculateIsDaytime(lat, lng) {
const now = new Date();
const start = new Date(now.getFullYear(), 0, 0);
const diff = now - start;
const dayOfYear = Math.floor(diff / 86400000);
const latRad = lat * Math.PI / 180;
const declination = 23.45 * Math.sin((360 / 365) * (dayOfYear - 81) * Math.PI / 180);
const declinationRad = declination * Math.PI / 180;
const cosHourAngle = -Math.tan(latRad) * Math.tan(declinationRad);
if (cosHourAngle > 1) {
return false;
}
if (cosHourAngle < -1) {
return true;
}
const hourAngle = Math.acos(cosHourAngle);
const hourAngleDeg = hourAngle * 180 / Math.PI;
const sunriseHour = 12 - hourAngleDeg / 15;
const sunsetHour = 12 + hourAngleDeg / 15;
const timeZoneOffset = now.getTimezoneOffset() / 60;
const localSunrise = sunriseHour - lng / 15 - timeZoneOffset;
const localSunset = sunsetHour - lng / 15 - timeZoneOffset;
const currentHour = now.getHours() + now.getMinutes() / 60;
const normalizeSunrise = ((localSunrise % 24) + 24) % 24;
const normalizeSunset = ((localSunset % 24) + 24) % 24;
return currentHour >= normalizeSunrise && currentHour < normalizeSunset;
}
function sendLocationToBackend() {
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
return false;
}
if (!DMSService.isConnected) {
return false;
}
if (SessionData.nightModeUseIPLocation) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": true
}, response => {
if (response?.error) {
log.warn("Theme automation: Failed to enable IP location", response.error);
}
});
return true;
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": false
}, response => {
if (!response.error) {
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
}, locResp => {
if (locResp?.error) {
log.warn("Theme automation: Failed to set location", locResp.error);
}
});
}
});
return true;
}
return false;
}
Timer {
id: locationRetryTimer
interval: 1000
repeat: true
running: false
property int retryCount: 0
onTriggered: {
if (root.sendLocationToBackend()) {
stop();
retryCount = 0;
root.evaluate();
} else {
retryCount++;
if (retryCount >= 10) {
stop();
retryCount = 0;
}
}
}
}
function start() {
root.active = true;
syncTimeSchedule();
syncLocationSchedule();
const sent = sendLocationToBackend();
if (!sent && typeof SessionData !== "undefined" && SessionData.themeModeAutoMode === "location") {
locationRetryTimer.start();
} else {
evaluate();
}
}
function stop() {
root.active = false;
if (typeof DMSService !== "undefined" && DMSService.isConnected) {
DMSService.sendRequest("theme.auto.setEnabled", {
"enabled": false
});
}
}
}