1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

gamma/nightmode: use dms V6 implementation - Scraps gammastep depednency

This commit is contained in:
bbedward
2025-10-20 18:23:26 -04:00
parent 9a305355c2
commit 7bf73ab14d
9 changed files with 312 additions and 204 deletions

View File

@@ -106,7 +106,6 @@ jobs:
Recommends: hyprpicker
Recommends: matugen
Recommends: wl-clipboard
Recommends: gammastep
Recommends: NetworkManager
Recommends: qt6-qtmultimedia
Suggests: qt6ct

View File

@@ -49,6 +49,7 @@ Singleton {
property int nightModeEndMinute: 0
property real latitude: 0.0
property real longitude: 0.0
property bool nightModeUseIPLocation: false
property string nightModeLocationProvider: ""
property var pinnedApps: []
@@ -112,6 +113,7 @@ Singleton {
}
latitude = settings.latitude !== undefined ? settings.latitude : 0.0
longitude = settings.longitude !== undefined ? settings.longitude : 0.0
nightModeUseIPLocation = settings.nightModeUseIPLocation !== undefined ? settings.nightModeUseIPLocation : false
nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : ""
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
selectedGpuIndex = settings.selectedGpuIndex !== undefined ? settings.selectedGpuIndex : 0
@@ -171,6 +173,7 @@ Singleton {
"nightModeEndMinute": nightModeEndMinute,
"latitude": latitude,
"longitude": longitude,
"nightModeUseIPLocation": nightModeUseIPLocation,
"nightModeLocationProvider": nightModeLocationProvider,
"pinnedApps": pinnedApps,
"selectedGpuIndex": selectedGpuIndex,
@@ -536,6 +539,11 @@ Singleton {
saveSettings()
}
function setNightModeUseIPLocation(use) {
nightModeUseIPLocation = use
saveSettings()
}
function setLatitude(lat) {
console.log("SessionData: Setting latitude to", lat)
latitude = lat

View File

@@ -116,8 +116,9 @@ Item {
width: parent.width
text: I18n.tr("Night Mode")
description: I18n.tr("Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.")
description: DisplayService.gammaControlAvailable ? I18n.tr("Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.") : I18n.tr("Gamma control not available. Requires DMS API v6+.")
checked: DisplayService.nightModeEnabled
enabled: DisplayService.gammaControlAvailable
onToggled: checked => {
DisplayService.toggleNightMode()
}
@@ -136,6 +137,7 @@ Item {
spacing: 0
leftPadding: Theme.spacingM
rightPadding: Theme.spacingM
visible: DisplayService.gammaControlAvailable
DankDropdown {
width: parent.width - parent.leftPadding - parent.rightPadding
@@ -162,6 +164,7 @@ Item {
text: I18n.tr("Automatic Control")
description: I18n.tr("Only adjust gamma based on time or location rules.")
checked: SessionData.nightModeAutoEnabled
visible: DisplayService.gammaControlAvailable
onToggled: checked => {
if (checked && !DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode()
@@ -183,7 +186,7 @@ Item {
id: automaticSettings
width: parent.width
spacing: Theme.spacingS
visible: SessionData.nightModeAutoEnabled
visible: SessionData.nightModeAutoEnabled && DisplayService.gammaControlAvailable
Connections {
target: SessionData
@@ -360,27 +363,28 @@ Item {
width: parent.width
DankToggle {
id: ipLocationToggle
width: parent.width
text: I18n.tr("Auto-location")
description: DisplayService.geoclueAvailable ? I18n.tr("Use automatic location detection (geoclue2)") : I18n.tr("Geoclue service not running - cannot auto-detect location")
checked: SessionData.nightModeLocationProvider === "geoclue2"
enabled: DisplayService.geoclueAvailable
text: I18n.tr("Use IP Location")
description: I18n.tr("Automatically detect location based on IP address")
checked: SessionData.nightModeUseIPLocation || false
onToggled: checked => {
if (checked && DisplayService.geoclueAvailable) {
SessionData.setNightModeLocationProvider("geoclue2")
SessionData.setLatitude(0.0)
SessionData.setLongitude(0.0)
} else {
SessionData.setNightModeLocationProvider("")
}
}
SessionData.setNightModeUseIPLocation(checked)
}
Connections {
target: SessionData
function onNightModeUseIPLocationChanged() {
ipLocationToggle.checked = SessionData.nightModeUseIPLocation
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: SessionData.nightModeLocationProvider !== "geoclue2"
leftPadding: Theme.spacingM
visible: !SessionData.nightModeUseIPLocation
StyledText {
text: I18n.tr("Manual Coordinates")

View File

@@ -333,7 +333,6 @@ sudo sh -c "curl -L https://github.com/AvengeMedia/dgop/releases/latest/download
- `wl-clipboard`: Required for copying various elements to clipboard.
- `cava`: Audio visualizer
- `cliphist`: Clipboard history
- `gammastep`: Night mode support
- `qt6-multimedia`: System sound support
## Compositor Configuration

View File

@@ -38,13 +38,8 @@ Singleton {
property bool nightModeEnabled: false
property bool automationAvailable: false
property bool geoclueAvailable: false
property bool isAutomaticNightTime: false
function buildGammastepCommand(gammastepArgs) {
const commandStr = "pkill gammastep; " + ["gammastep"].concat(gammastepArgs).join(" ")
return ["sh", "-c", commandStr]
}
property bool gammaControlAvailable: false
readonly property int dayTemp: 6500
function setBrightnessInternal(percentage, device) {
const clampedValue = Math.max(1, Math.min(100, percentage))
@@ -216,34 +211,49 @@ Singleton {
// Night Mode Functions - Simplified
function enableNightMode() {
if (!automationAvailable) {
gammaStepTestProcess.running = true
if (!gammaControlAvailable) {
ToastService.showWarning("Night mode failed: DMS gamma control not available")
return
}
nightModeEnabled = true
SessionData.setNightModeEnabled(true)
// Apply immediately or start automation
if (SessionData.nightModeAutoEnabled) {
startAutomation()
} else {
applyNightModeDirectly()
}
DMSService.sendRequest("wayland.gamma.setEnabled", {
"enabled": true
}, response => {
if (response.error) {
console.error("DisplayService: Failed to enable gamma control:", response.error)
ToastService.showError("Failed to enable night mode: " + response.error)
nightModeEnabled = false
SessionData.setNightModeEnabled(false)
return
}
if (SessionData.nightModeAutoEnabled) {
startAutomation()
} else {
applyNightModeDirectly()
}
})
}
function disableNightMode() {
nightModeEnabled = false
SessionData.setNightModeEnabled(false)
stopAutomation()
// Nuclear approach - kill ALL gammastep processes multiple times
Quickshell.execDetached(["pkill", "-f", "gammastep"])
Quickshell.execDetached(["pkill", "-9", "gammastep"])
Quickshell.execDetached(["killall", "gammastep"])
// Also stop all related processes
gammaStepProcess.running = false
automationProcess.running = false
gammaStepTestProcess.running = false
if (!gammaControlAvailable) {
return
}
DMSService.sendRequest("wayland.gamma.setEnabled", {
"enabled": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable gamma control:", response.error)
ToastService.showError("Failed to disable night mode: " + response.error)
}
})
}
function toggleNightMode() {
@@ -255,14 +265,35 @@ Singleton {
}
function applyNightModeDirectly() {
const temperature = SessionData.nightModeTemperature || 4500
gammaStepProcess.command = buildGammastepCommand(["-m", "wayland", "-O", String(temperature)])
gammaStepProcess.running = true
}
const temperature = SessionData.nightModeTemperature || 4000
function resetToNormalMode() {
// Just kill gammastep to return to normal display temperature
Quickshell.execDetached(["pkill", "gammastep"])
DMSService.sendRequest("wayland.gamma.setManualTimes", {
"sunrise": null,
"sunset": null
}, response => {
if (response.error) {
console.error("DisplayService: Failed to clear manual times:", response.error)
return
}
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error)
return
}
DMSService.sendRequest("wayland.gamma.setTemperature", {
"temp": temperature
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error)
ToastService.showError("Failed to set night mode temperature: " + response.error)
}
})
})
})
}
function startAutomation() {
@@ -282,70 +313,103 @@ Singleton {
}
}
function stopAutomation() {
automationProcess.running = false
gammaStepProcess.running = false
isAutomaticNightTime = false
// Nuclear approach - kill ALL gammastep processes multiple times
Quickshell.execDetached(["pkill", "-f", "gammastep"])
Quickshell.execDetached(["pkill", "-9", "gammastep"])
Quickshell.execDetached(["killall", "gammastep"])
}
function startTimeBasedMode() {
checkTimeBasedMode()
const temperature = SessionData.nightModeTemperature || 4000
const sunriseHour = SessionData.nightModeEndHour
const sunriseMinute = SessionData.nightModeEndMinute
const sunsetHour = SessionData.nightModeStartHour
const sunsetMinute = SessionData.nightModeStartMinute
const sunrise = `${String(sunriseHour).padStart(2, '0')}:${String(sunriseMinute).padStart(2, '0')}`
const sunset = `${String(sunsetHour).padStart(2, '0')}:${String(sunsetMinute).padStart(2, '0')}`
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error)
return
}
DMSService.sendRequest("wayland.gamma.setTemperature", {
"low": temperature,
"high": dayTemp
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error)
ToastService.showError("Failed to set night mode temperature: " + response.error)
return
}
DMSService.sendRequest("wayland.gamma.setManualTimes", {
"sunrise": sunrise,
"sunset": sunset
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set manual times:", response.error)
ToastService.showError("Failed to set night mode schedule: " + response.error)
}
})
})
})
}
function startLocationBasedMode() {
const temperature = SessionData.nightModeTemperature || 4500
const temperature = SessionData.nightModeTemperature || 4000
const dayTemp = 6500
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
automationProcess.command = buildGammastepCommand(["-m", "wayland", "-l", `${SessionData.latitude.toFixed(6)}:${SessionData.longitude.toFixed(6)}`, "-t", `${dayTemp}:${temperature}`, "-v"])
automationProcess.running = true
return
}
if (SessionData.nightModeLocationProvider === "geoclue2") {
automationProcess.command = buildGammastepCommand(["-m", "wayland", "-l", "geoclue2", "-t", `${dayTemp}:${temperature}`, "-v"])
automationProcess.running = true
return
}
console.warn("DisplayService: Location mode selected but no coordinates or geoclue provider set")
}
function checkTimeBasedMode() {
if (!nightModeEnabled || !SessionData.nightModeAutoEnabled || SessionData.nightModeAutoMode !== "time") {
return
}
const currentTime = systemClock.hours * 60 + systemClock.minutes
const startMinutes = SessionData.nightModeStartHour * 60 + SessionData.nightModeStartMinute
const endMinutes = SessionData.nightModeEndHour * 60 + SessionData.nightModeEndMinute
let shouldBeNight = false
if (startMinutes > endMinutes) {
shouldBeNight = (currentTime >= startMinutes) || (currentTime < endMinutes)
} else {
shouldBeNight = (currentTime >= startMinutes) && (currentTime < endMinutes)
}
if (shouldBeNight !== isAutomaticNightTime) {
isAutomaticNightTime = shouldBeNight
if (shouldBeNight) {
applyNightModeDirectly()
} else {
resetToNormalMode()
DMSService.sendRequest("wayland.gamma.setManualTimes", {
"sunrise": null,
"sunset": null
}, response => {
if (response.error) {
console.error("DisplayService: Failed to clear manual times:", response.error)
return
}
}
}
function detectLocationProviders() {
geoclueDetectionProcess.running = true
DMSService.sendRequest("wayland.gamma.setTemperature", {
"low": temperature,
"high": dayTemp
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error)
ToastService.showError("Failed to set night mode temperature: " + response.error)
return
}
if (SessionData.nightModeUseIPLocation) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": true
}, response => {
if (response.error) {
console.error("DisplayService: Failed to enable IP location:", response.error)
ToastService.showError("Failed to enable IP location: " + response.error)
}
})
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error)
return
}
DMSService.sendRequest("wayland.gamma.setLocation", {
"latitude": SessionData.latitude,
"longitude": SessionData.longitude
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set location:", response.error)
ToastService.showError("Failed to set night mode location: " + response.error)
}
})
})
} else {
console.warn("DisplayService: Location mode selected but no coordinates set and IP location disabled")
}
})
})
}
function setNightModeAutomationMode(mode) {
@@ -353,9 +417,6 @@ Singleton {
}
function evaluateNightMode() {
// Always stop all processes first to clean slate
stopAutomation()
if (!nightModeEnabled) {
return
}
@@ -369,8 +430,50 @@ Singleton {
}
}
function checkNightModeAvailability() {
gammastepAvailabilityProcess.running = true
function checkGammaControlAvailability() {
if (!DMSService.isConnected) {
return
}
if (DMSService.apiVersion < 6) {
gammaControlAvailable = false
automationAvailable = false
return
}
if (!DMSService.capabilities.includes("gamma")) {
gammaControlAvailable = false
automationAvailable = false
return
}
DMSService.sendRequest("wayland.gamma.getState", null, response => {
if (response.error) {
gammaControlAvailable = false
automationAvailable = false
console.error("DisplayService: Gamma control not available:", response.error)
} else {
gammaControlAvailable = true
automationAvailable = true
if (nightModeEnabled) {
DMSService.sendRequest("wayland.gamma.setEnabled", {
"enabled": true
}, enableResponse => {
if (enableResponse.error) {
console.error("DisplayService: Failed to enable gamma control on startup:", enableResponse.error)
return
}
if (SessionData.nightModeAutoEnabled) {
startAutomation()
} else {
applyNightModeDirectly()
}
})
}
}
})
}
Timer {
@@ -392,27 +495,9 @@ Singleton {
Component.onCompleted: {
ddcDetectionProcess.running = true
refreshDevices()
checkNightModeAvailability()
// Initialize night mode state from session
nightModeEnabled = SessionData.nightModeEnabled
}
Component.onDestruction: {
gammaStepProcess.running = false
automationProcess.running = false
}
SystemClock {
id: systemClock
precision: SystemClock.Minutes
onDateChanged: {
if (nightModeEnabled && SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
checkTimeBasedMode()
}
}
}
Process {
id: ddcDetectionProcess
@@ -679,83 +764,20 @@ Singleton {
}
}
Process {
id: gammastepAvailabilityProcess
command: ["which", "gammastep"]
running: false
Connections {
target: DMSService
onExited: function (exitCode) {
automationAvailable = (exitCode === 0)
if (automationAvailable) {
detectLocationProviders()
// If night mode should be enabled on startup
if (nightModeEnabled && SessionData.nightModeAutoEnabled) {
startAutomation()
} else if (nightModeEnabled) {
applyNightModeDirectly()
}
function onConnectionStateChanged() {
if (DMSService.isConnected) {
checkGammaControlAvailability()
} else {
console.log("DisplayService: gammastep not available")
gammaControlAvailable = false
automationAvailable = false
}
}
}
Process {
id: geoclueDetectionProcess
command: ["sh", "-c", "busctl --system list | grep -qF org.freedesktop.GeoClue2"]
running: false
onExited: function (exitCode) {
geoclueAvailable = (exitCode === 0)
}
}
Process {
id: gammaStepTestProcess
command: ["which", "gammastep"]
running: false
onExited: function (exitCode) {
if (exitCode === 0) {
automationAvailable = true
nightModeEnabled = true
SessionData.setNightModeEnabled(true)
if (SessionData.nightModeAutoEnabled) {
startAutomation()
} else {
applyNightModeDirectly()
}
} else {
console.warn("DisplayService: gammastep not found")
ToastService.showWarning("Night mode failed: gammastep not found")
}
}
}
Process {
id: gammaStepProcess
running: false
onExited: function (exitCode) {
if (nightModeEnabled && exitCode !== 0 && exitCode !== 15) {
console.warn("DisplayService: Night mode process failed:", exitCode)
}
}
}
Process {
id: automationProcess
running: false
property string processType: "automation"
onExited: function (exitCode) {
if (nightModeEnabled && SessionData.nightModeAutoEnabled && exitCode !== 0 && exitCode !== 15) {
console.warn("DisplayService: Night mode automation failed:", exitCode)
// Location mode failed
console.warn("DisplayService: Location-based night mode failed")
}
function onCapabilitiesReceived() {
checkGammaControlAvailability()
}
}
@@ -795,7 +817,7 @@ Singleton {
function onLongitudeChanged() {
evaluateNightMode()
}
function onNightModeLocationProviderChanged() {
function onNightModeUseIPLocationChanged() {
evaluateNightMode()
}
}

View File

@@ -43,7 +43,6 @@ Recommends: quickshell-git
Recommends: wl-clipboard
# Recommended system packages
Recommends: gammastep
Recommends: NetworkManager
Recommends: qt6-qtmultimedia
Suggests: qt6ct

View File

@@ -151,7 +151,6 @@ in {
++ lib.optionals cfg.enableClipboard [pkgs.cliphist pkgs.wl-clipboard]
++ lib.optionals cfg.enableVPN [pkgs.glib pkgs.networkmanager]
++ lib.optional cfg.enableBrightnessControl pkgs.brightnessctl
++ lib.optional cfg.enableNightMode pkgs.gammastep
++ lib.optional cfg.enableDynamicTheming pkgs.matugen
++ lib.optional cfg.enableAudioWavelength pkgs.cava
++ lib.optional cfg.enableCalendarEvents pkgs.khal

View File

@@ -386,7 +386,7 @@
{
"term": "Back",
"context": "Back",
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:454",
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:467",
"comment": ""
},
{
@@ -860,7 +860,7 @@
{
"term": "DEMO MODE - Click anywhere to exit",
"context": "DEMO MODE - Click anywhere to exit",
"reference": "Modules/Lock/LockScreenContent.qml:634",
"reference": "Modules/Lock/LockScreenContent.qml:628",
"comment": ""
},
{
@@ -1187,6 +1187,18 @@
"reference": "Modals/FileBrowser/FileInfo.qml:200",
"comment": ""
},
{
"term": "Failed to set profile image",
"context": "Failed to set profile image",
"reference": "Services/PortalService.qml:150",
"comment": ""
},
{
"term": "Failed to set profile image: ",
"context": "Failed to set profile image: ",
"reference": "Services/PortalService.qml:159",
"comment": ""
},
{
"term": "Feels Like",
"context": "Feels Like",
@@ -2141,6 +2153,12 @@
"reference": "Modules/Settings/DankBarTab.qml:88",
"comment": ""
},
{
"term": "Permission denied to set profile image.",
"context": "Permission denied to set profile image.",
"reference": "Services/PortalService.qml:155",
"comment": ""
},
{
"term": "Personalization",
"context": "Personalization",
@@ -2297,6 +2315,18 @@
"reference": "Modules/ProcessList/ProcessListView.qml:41",
"comment": ""
},
{
"term": "Profile Image Error",
"context": "Profile Image Error",
"reference": "Services/PortalService.qml:162",
"comment": ""
},
{
"term": "Profile image is too large. Please use a smaller image.",
"context": "Profile image is too large. Please use a smaller image.",
"reference": "Services/PortalService.qml:153",
"comment": ""
},
{
"term": "QML, JavaScript, Go",
"context": "QML, JavaScript, Go",
@@ -2549,6 +2579,12 @@
"reference": "Modules/Settings/PersonalizationTab.qml:930",
"comment": ""
},
{
"term": "Selected image file not found.",
"context": "Selected image file not found.",
"reference": "Services/PortalService.qml:157",
"comment": ""
},
{
"term": "Separator",
"context": "Separator",

View File

@@ -1385,6 +1385,20 @@
"reference": "",
"comment": ""
},
{
"term": "Failed to set profile image",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Failed to set profile image: ",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Feels Like",
"translation": "",
@@ -2498,6 +2512,13 @@
"reference": "",
"comment": ""
},
{
"term": "Permission denied to set profile image.",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Personalization",
"translation": "",
@@ -2680,6 +2701,20 @@
"reference": "",
"comment": ""
},
{
"term": "Profile Image Error",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Profile image is too large. Please use a smaller image.",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "QML, JavaScript, Go",
"translation": "",
@@ -2974,6 +3009,13 @@
"reference": "",
"comment": ""
},
{
"term": "Selected image file not found.",
"translation": "",
"context": "",
"reference": "",
"comment": ""
},
{
"term": "Separator",
"translation": "",