mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
night mode repairs
This commit is contained in:
@@ -19,11 +19,14 @@ Singleton {
|
|||||||
property bool nightModeEnabled: false
|
property bool nightModeEnabled: false
|
||||||
property int nightModeTemperature: 4500
|
property int nightModeTemperature: 4500
|
||||||
property bool nightModeAutoEnabled: false
|
property bool nightModeAutoEnabled: false
|
||||||
property string nightModeAutoMode: "manual"
|
property string nightModeAutoMode: "time"
|
||||||
property string nightModeStartTime: "20:00"
|
property int nightModeStartHour: 18
|
||||||
property string nightModeEndTime: "06:00"
|
property int nightModeStartMinute: 0
|
||||||
|
property int nightModeEndHour: 6
|
||||||
|
property int nightModeEndMinute: 0
|
||||||
property real latitude: 0.0
|
property real latitude: 0.0
|
||||||
property real longitude: 0.0
|
property real longitude: 0.0
|
||||||
|
property string nightModeLocationProvider: ""
|
||||||
property var pinnedApps: []
|
property var pinnedApps: []
|
||||||
property int selectedGpuIndex: 0
|
property int selectedGpuIndex: 0
|
||||||
property bool nvidiaGpuTempEnabled: false
|
property bool nvidiaGpuTempEnabled: false
|
||||||
@@ -61,13 +64,27 @@ Singleton {
|
|||||||
nightModeAutoEnabled = settings.nightModeAutoEnabled
|
nightModeAutoEnabled = settings.nightModeAutoEnabled
|
||||||
!== undefined ? settings.nightModeAutoEnabled : false
|
!== undefined ? settings.nightModeAutoEnabled : false
|
||||||
nightModeAutoMode = settings.nightModeAutoMode
|
nightModeAutoMode = settings.nightModeAutoMode
|
||||||
!== undefined ? settings.nightModeAutoMode : "manual"
|
!== undefined ? settings.nightModeAutoMode : "time"
|
||||||
nightModeStartTime = settings.nightModeStartTime
|
// Handle legacy time format
|
||||||
!== undefined ? settings.nightModeStartTime : "20:00"
|
if (settings.nightModeStartTime !== undefined) {
|
||||||
nightModeEndTime = settings.nightModeEndTime
|
const parts = settings.nightModeStartTime.split(":")
|
||||||
!== undefined ? settings.nightModeEndTime : "06:00"
|
nightModeStartHour = parseInt(parts[0]) || 18
|
||||||
|
nightModeStartMinute = parseInt(parts[1]) || 0
|
||||||
|
} else {
|
||||||
|
nightModeStartHour = settings.nightModeStartHour !== undefined ? settings.nightModeStartHour : 18
|
||||||
|
nightModeStartMinute = settings.nightModeStartMinute !== undefined ? settings.nightModeStartMinute : 0
|
||||||
|
}
|
||||||
|
if (settings.nightModeEndTime !== undefined) {
|
||||||
|
const parts = settings.nightModeEndTime.split(":")
|
||||||
|
nightModeEndHour = parseInt(parts[0]) || 6
|
||||||
|
nightModeEndMinute = parseInt(parts[1]) || 0
|
||||||
|
} else {
|
||||||
|
nightModeEndHour = settings.nightModeEndHour !== undefined ? settings.nightModeEndHour : 6
|
||||||
|
nightModeEndMinute = settings.nightModeEndMinute !== undefined ? settings.nightModeEndMinute : 0
|
||||||
|
}
|
||||||
latitude = settings.latitude !== undefined ? settings.latitude : 0.0
|
latitude = settings.latitude !== undefined ? settings.latitude : 0.0
|
||||||
longitude = settings.longitude !== undefined ? settings.longitude : 0.0
|
longitude = settings.longitude !== undefined ? settings.longitude : 0.0
|
||||||
|
nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : ""
|
||||||
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
|
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
|
||||||
selectedGpuIndex = settings.selectedGpuIndex
|
selectedGpuIndex = settings.selectedGpuIndex
|
||||||
!== undefined ? settings.selectedGpuIndex : 0
|
!== undefined ? settings.selectedGpuIndex : 0
|
||||||
@@ -104,10 +121,13 @@ Singleton {
|
|||||||
"nightModeTemperature": nightModeTemperature,
|
"nightModeTemperature": nightModeTemperature,
|
||||||
"nightModeAutoEnabled": nightModeAutoEnabled,
|
"nightModeAutoEnabled": nightModeAutoEnabled,
|
||||||
"nightModeAutoMode": nightModeAutoMode,
|
"nightModeAutoMode": nightModeAutoMode,
|
||||||
"nightModeStartTime": nightModeStartTime,
|
"nightModeStartHour": nightModeStartHour,
|
||||||
"nightModeEndTime": nightModeEndTime,
|
"nightModeStartMinute": nightModeStartMinute,
|
||||||
|
"nightModeEndHour": nightModeEndHour,
|
||||||
|
"nightModeEndMinute": nightModeEndMinute,
|
||||||
"latitude": latitude,
|
"latitude": latitude,
|
||||||
"longitude": longitude,
|
"longitude": longitude,
|
||||||
|
"nightModeLocationProvider": nightModeLocationProvider,
|
||||||
"pinnedApps": pinnedApps,
|
"pinnedApps": pinnedApps,
|
||||||
"selectedGpuIndex": selectedGpuIndex,
|
"selectedGpuIndex": selectedGpuIndex,
|
||||||
"nvidiaGpuTempEnabled": nvidiaGpuTempEnabled,
|
"nvidiaGpuTempEnabled": nvidiaGpuTempEnabled,
|
||||||
@@ -152,13 +172,23 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNightModeStartTime(time) {
|
function setNightModeStartHour(hour) {
|
||||||
nightModeStartTime = time
|
nightModeStartHour = hour
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNightModeEndTime(time) {
|
function setNightModeStartMinute(minute) {
|
||||||
nightModeEndTime = time
|
nightModeStartMinute = minute
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeEndHour(hour) {
|
||||||
|
nightModeEndHour = hour
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeEndMinute(minute) {
|
||||||
|
nightModeEndMinute = minute
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +204,11 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setNightModeLocationProvider(provider) {
|
||||||
|
nightModeLocationProvider = provider
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
function setWallpaperPath(path) {
|
function setWallpaperPath(path) {
|
||||||
wallpaperPath = path
|
wallpaperPath = path
|
||||||
saveSettings()
|
saveSettings()
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ DankPopout {
|
|||||||
width: parent.width - Theme.spacingL * 2
|
width: parent.width - Theme.spacingL * 2
|
||||||
x: Theme.spacingL
|
x: Theme.spacingL
|
||||||
y: Theme.spacingL
|
y: Theme.spacingL
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -630,16 +630,30 @@ DankPopout {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
height: audioSliderRow.implicitHeight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: audioSliderRow
|
||||||
|
x: -Theme.spacingS
|
||||||
|
width: parent.width + Theme.spacingS * 2
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
AudioSliderRow {
|
AudioSliderRow {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
BrightnessSliderRow {
|
Item {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
BrightnessSliderRow {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
x: -Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,11 +790,11 @@ DankPopout {
|
|||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
iconName: DisplayService.nightModeActive ? "nightlight" : "dark_mode"
|
iconName: DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||||
text: "Night Mode"
|
text: "Night Mode"
|
||||||
secondaryText: DisplayService.nightModeActive ? "On" : "Off"
|
secondaryText: DisplayService.nightModeEnabled ? "On" : "Off"
|
||||||
isActive: true
|
isActive: DisplayService.nightModeEnabled
|
||||||
enabled: DisplayService.brightnessAvailable
|
enabled: DisplayService.automationAvailable
|
||||||
onClicked: DisplayService.toggleNightMode()
|
onClicked: DisplayService.toggleNightMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ Row {
|
|||||||
|
|
||||||
property var defaultSink: AudioService.sink
|
property var defaultSink: AudioService.sink
|
||||||
|
|
||||||
height: 60
|
height: 40
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Theme.iconSize + Theme.spacingS * 2
|
width: Theme.iconSize + Theme.spacingS * 2
|
||||||
@@ -58,10 +58,7 @@ Row {
|
|||||||
|
|
||||||
DankSlider {
|
DankSlider {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: {
|
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM
|
||||||
if (parent.width <= 0) return 80
|
|
||||||
return Math.max(80, Math.min(400, parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM))
|
|
||||||
}
|
|
||||||
enabled: defaultSink !== null
|
enabled: defaultSink !== null
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import qs.Widgets
|
|||||||
Row {
|
Row {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
height: 60
|
height: 40
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Theme.iconSize + Theme.spacingS * 2
|
width: Theme.iconSize + Theme.spacingS * 2
|
||||||
height: Theme.iconSize + Theme.spacingS * 2
|
height: Theme.iconSize + Theme.spacingS * 2
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse && DisplayService.devices.length > 1
|
color: iconArea.containsMouse
|
||||||
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||||
: "transparent"
|
: "transparent"
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ Row {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: DisplayService.devices.length > 1
|
hoverEnabled: true
|
||||||
cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: function(event) {
|
onClicked: function(event) {
|
||||||
if (DisplayService.devices.length > 1) {
|
if (DisplayService.devices.length > 1) {
|
||||||
@@ -57,55 +57,27 @@ Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
DankSlider {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM
|
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM
|
||||||
spacing: 0
|
enabled: DisplayService.brightnessAvailable
|
||||||
|
minimum: 1
|
||||||
DankSlider {
|
maximum: 100
|
||||||
width: parent.width
|
value: {
|
||||||
enabled: DisplayService.brightnessAvailable
|
let level = DisplayService.brightnessLevel
|
||||||
minimum: 1
|
if (level > 100) {
|
||||||
maximum: 100
|
let deviceInfo = DisplayService.getCurrentDeviceInfo()
|
||||||
value: {
|
if (deviceInfo && deviceInfo.max > 0) {
|
||||||
let level = DisplayService.brightnessLevel
|
return Math.round((level / deviceInfo.max) * 100)
|
||||||
if (level > 100) {
|
|
||||||
let deviceInfo = DisplayService.getCurrentDeviceInfo()
|
|
||||||
if (deviceInfo && deviceInfo.max > 0) {
|
|
||||||
return Math.round((level / deviceInfo.max) * 100)
|
|
||||||
}
|
|
||||||
return 50
|
|
||||||
}
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
onSliderValueChanged: function(newValue) {
|
|
||||||
if (DisplayService.brightnessAvailable) {
|
|
||||||
DisplayService.setBrightness(newValue)
|
|
||||||
}
|
}
|
||||||
|
return 50
|
||||||
}
|
}
|
||||||
|
return level
|
||||||
}
|
}
|
||||||
|
onSliderValueChanged: function(newValue) {
|
||||||
StyledText {
|
if (DisplayService.brightnessAvailable) {
|
||||||
visible: {
|
DisplayService.setBrightness(newValue)
|
||||||
if (DisplayService.devices.length <= 1) return false
|
|
||||||
if (!DisplayService.currentDevice) return false
|
|
||||||
|
|
||||||
let currentIndex = -1
|
|
||||||
for (let i = 0; i < DisplayService.devices.length; i++) {
|
|
||||||
if (DisplayService.devices[i].name === DisplayService.currentDevice) {
|
|
||||||
currentIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentIndex !== 0
|
|
||||||
}
|
}
|
||||||
width: parent.width
|
|
||||||
text: DisplayService.currentDevice || ""
|
|
||||||
font.pixelSize: Theme.fontSizeSmall - 2
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
topPadding: 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -608,35 +608,26 @@ Item {
|
|||||||
id: nightModeToggle
|
id: nightModeToggle
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: "Night Mode (Manual)"
|
text: "Night Mode"
|
||||||
description: SessionData.nightModeAutoEnabled ? "Manual control - automation will override when scheduled" : "Apply warm color temperature to reduce eye strain"
|
description: "Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates."
|
||||||
checked: DisplayService.nightModeActive
|
checked: DisplayService.nightModeEnabled
|
||||||
opacity: SessionData.nightModeAutoEnabled ? 0.7 : 1
|
|
||||||
onToggled: (checked) => {
|
onToggled: (checked) => {
|
||||||
if (checked !== DisplayService.nightModeActive) {
|
DisplayService.toggleNightMode();
|
||||||
if (checked)
|
|
||||||
DisplayService.enableNightMode();
|
|
||||||
else
|
|
||||||
DisplayService.disableNightMode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNightModeActiveChanged() {
|
function onNightModeEnabledChanged() {
|
||||||
nightModeToggle.checked = DisplayService.nightModeActive;
|
nightModeToggle.checked = DisplayService.nightModeEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
target: DisplayService
|
target: DisplayService
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankDropdown {
|
DankDropdown {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: "Night Mode Temperature"
|
text: "Temperature"
|
||||||
description: DisplayService.nightModeActive ? "Disable night mode to adjust" : "Set temperature for night mode"
|
description: "Color temperature for night mode"
|
||||||
enabled: !DisplayService.nightModeActive
|
|
||||||
opacity: !DisplayService.nightModeActive ? 1 : 0.6
|
|
||||||
currentValue: SessionData.nightModeTemperature + "K"
|
currentValue: SessionData.nightModeTemperature + "K"
|
||||||
options: {
|
options: {
|
||||||
var temps = [];
|
var temps = [];
|
||||||
@@ -659,287 +650,274 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DankToggle {
|
DankToggle {
|
||||||
|
id: automaticToggle
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: "Night Mode Automation"
|
text: "Automatic Control"
|
||||||
description: "Automatically enable/disable night mode based on time or location, independent of the manual toggle."
|
description: "Let the system automatically turn night mode on and off"
|
||||||
checked: SessionData.nightModeAutoEnabled
|
checked: SessionData.nightModeAutoEnabled
|
||||||
onToggled: (checked) => {
|
onToggled: (checked) => {
|
||||||
SessionData.setNightModeAutoEnabled(checked);
|
SessionData.setNightModeAutoEnabled(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onNightModeAutoEnabledChanged() {
|
||||||
|
automaticToggle.checked = SessionData.nightModeAutoEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
id: automaticSettings
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingS
|
||||||
visible: SessionData.nightModeAutoEnabled
|
visible: SessionData.nightModeAutoEnabled
|
||||||
leftPadding: Theme.spacingM
|
leftPadding: Theme.spacingM
|
||||||
|
|
||||||
Row {
|
Connections {
|
||||||
spacing: Theme.spacingL
|
target: SessionData
|
||||||
width: parent.width - parent.leftPadding
|
function onNightModeAutoEnabledChanged() {
|
||||||
|
automaticSettings.visible = SessionData.nightModeAutoEnabled;
|
||||||
StyledText {
|
|
||||||
text: "Mode:"
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTabBar {
|
|
||||||
width: 200
|
|
||||||
height: 32
|
|
||||||
model: [{
|
|
||||||
"text": "Time"
|
|
||||||
}, {
|
|
||||||
"text": "Location"
|
|
||||||
}]
|
|
||||||
currentIndex: SessionData.nightModeAutoMode === "location" ? 1 : 0
|
|
||||||
onTabClicked: (index) => {
|
|
||||||
SessionData.setNightModeAutoMode(index === 1 ? "location" : "time");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
DankTabBar {
|
||||||
spacing: Theme.spacingM
|
id: modeTabBarNight
|
||||||
visible: SessionData.nightModeAutoMode === "time"
|
width: 200
|
||||||
width: parent.width - parent.leftPadding
|
height: 32
|
||||||
topPadding: Theme.spacingL
|
model: [{
|
||||||
|
"text": "Time"
|
||||||
StyledText {
|
}, {
|
||||||
text: "Start:"
|
"text": "Location"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
}]
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Component.onCompleted: {
|
||||||
|
currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DankTextField {
|
onTabClicked: (index) => {
|
||||||
id: startTimeField
|
console.log("Tab clicked:", index, "Setting mode to:", index === 1 ? "location" : "time");
|
||||||
width: 80
|
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time");
|
||||||
height: 32
|
currentIndex = index;
|
||||||
text: SessionData.nightModeStartTime
|
|
||||||
placeholderText: "20:00"
|
|
||||||
maximumLength: 5
|
|
||||||
topPadding: Theme.spacingXS
|
|
||||||
bottomPadding: Theme.spacingXS
|
|
||||||
normalBorderColor: {
|
|
||||||
if (text.length === 0) return Theme.outlineStrong;
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
return isValid ? Theme.success : Theme.error;
|
|
||||||
}
|
|
||||||
onAccepted: {
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
if (isValid) {
|
|
||||||
SessionData.setNightModeStartTime(text);
|
|
||||||
} else {
|
|
||||||
text = SessionData.nightModeStartTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
if (isValid) {
|
|
||||||
SessionData.setNightModeStartTime(text);
|
|
||||||
} else {
|
|
||||||
text = SessionData.nightModeStartTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
validator: RegularExpressionValidator {
|
|
||||||
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "End:"
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
DankTextField {
|
|
||||||
id: endTimeField
|
|
||||||
width: 80
|
|
||||||
height: 32
|
|
||||||
text: SessionData.nightModeEndTime
|
|
||||||
placeholderText: "06:00"
|
|
||||||
maximumLength: 5
|
|
||||||
topPadding: Theme.spacingXS
|
|
||||||
bottomPadding: Theme.spacingXS
|
|
||||||
normalBorderColor: {
|
|
||||||
if (text.length === 0) return Theme.outlineStrong;
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
return isValid ? Theme.success : Theme.error;
|
|
||||||
}
|
|
||||||
onAccepted: {
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
if (isValid) {
|
|
||||||
SessionData.setNightModeEndTime(text);
|
|
||||||
} else {
|
|
||||||
text = SessionData.nightModeEndTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text);
|
|
||||||
if (isValid) {
|
|
||||||
SessionData.setNightModeEndTime(text);
|
|
||||||
} else {
|
|
||||||
text = SessionData.nightModeEndTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
validator: RegularExpressionValidator {
|
|
||||||
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width - parent.leftPadding
|
property bool isTimeMode: SessionData.nightModeAutoMode === "time"
|
||||||
spacing: Theme.spacingXS
|
visible: isTimeMode
|
||||||
visible: SessionData.nightModeAutoMode === "location"
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
// Header row
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
height: 20
|
||||||
|
leftPadding: 45
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Hour"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: 50
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Minute"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: 50
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start time row
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
height: 32
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: startLabel
|
||||||
|
text: "Start"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: 50
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
width: 60
|
||||||
|
height: 32
|
||||||
|
text: ""
|
||||||
|
currentValue: SessionData.nightModeStartHour.toString()
|
||||||
|
options: {
|
||||||
|
var hours = [];
|
||||||
|
for (var i = 0; i < 24; i++) {
|
||||||
|
hours.push(i.toString());
|
||||||
|
}
|
||||||
|
return hours;
|
||||||
|
}
|
||||||
|
onValueChanged: (value) => {
|
||||||
|
SessionData.setNightModeStartHour(parseInt(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
width: 60
|
||||||
|
height: 32
|
||||||
|
text: ""
|
||||||
|
currentValue: SessionData.nightModeStartMinute.toString().padStart(2, '0')
|
||||||
|
options: {
|
||||||
|
var minutes = [];
|
||||||
|
for (var i = 0; i < 60; i += 5) {
|
||||||
|
minutes.push(i.toString().padStart(2, '0'));
|
||||||
|
}
|
||||||
|
return minutes;
|
||||||
|
}
|
||||||
|
onValueChanged: (value) => {
|
||||||
|
SessionData.setNightModeStartMinute(parseInt(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End time row
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
height: 32
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "End"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: startLabel.width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
width: 60
|
||||||
|
height: 32
|
||||||
|
text: ""
|
||||||
|
currentValue: SessionData.nightModeEndHour.toString()
|
||||||
|
options: {
|
||||||
|
var hours = [];
|
||||||
|
for (var i = 0; i < 24; i++) {
|
||||||
|
hours.push(i.toString());
|
||||||
|
}
|
||||||
|
return hours;
|
||||||
|
}
|
||||||
|
onValueChanged: (value) => {
|
||||||
|
SessionData.setNightModeEndHour(parseInt(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
width: 60
|
||||||
|
height: 32
|
||||||
|
text: ""
|
||||||
|
currentValue: SessionData.nightModeEndMinute.toString().padStart(2, '0')
|
||||||
|
options: {
|
||||||
|
var minutes = [];
|
||||||
|
for (var i = 0; i < 60; i += 5) {
|
||||||
|
minutes.push(i.toString().padStart(2, '0'));
|
||||||
|
}
|
||||||
|
return minutes;
|
||||||
|
}
|
||||||
|
onValueChanged: (value) => {
|
||||||
|
SessionData.setNightModeEndMinute(parseInt(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
property bool isLocationMode: SessionData.nightModeAutoMode === "location"
|
||||||
|
visible: isLocationMode
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: "Auto-location"
|
||||||
|
description: DisplayService.geoclueAvailable ? "Use automatic location detection (geoclue2)" : "Geoclue service not running - cannot auto-detect location"
|
||||||
|
checked: SessionData.nightModeLocationProvider === "geoclue2"
|
||||||
|
enabled: DisplayService.geoclueAvailable
|
||||||
|
onToggled: (checked) => {
|
||||||
|
if (checked && DisplayService.geoclueAvailable) {
|
||||||
|
SessionData.setNightModeLocationProvider("geoclue2")
|
||||||
|
SessionData.setLatitude(0.0)
|
||||||
|
SessionData.setLongitude(0.0)
|
||||||
|
} else {
|
||||||
|
SessionData.setNightModeLocationProvider("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Uses automatic location detection for sunrise/sunset times. If automatic detection fails, enter your coordinates manually below (e.g., NYC: 40.7128, -74.0060)."
|
text: "Manual Coordinates"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: SessionData.nightModeLocationProvider !== "geoclue2"
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
visible: SessionData.nightModeLocationProvider !== "geoclue2"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Latitude"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
width: 120
|
||||||
|
height: 40
|
||||||
|
text: SessionData.latitude.toString()
|
||||||
|
placeholderText: "0.0"
|
||||||
|
onTextChanged: {
|
||||||
|
const lat = parseFloat(text) || 0.0
|
||||||
|
if (lat >= -90 && lat <= 90) {
|
||||||
|
SessionData.setLatitude(lat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Longitude"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
width: 120
|
||||||
|
height: 40
|
||||||
|
text: SessionData.longitude.toString()
|
||||||
|
placeholderText: "0.0"
|
||||||
|
onTextChanged: {
|
||||||
|
const lon = parseFloat(text) || 0.0
|
||||||
|
if (lon >= -180 && lon <= 180) {
|
||||||
|
SessionData.setLongitude(lon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Uses sunrise/sunset times to automatically adjust night mode based on your location."
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
visible: SessionData.nightModeAutoMode === "location"
|
|
||||||
width: parent.width - parent.leftPadding
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Coordinates:"
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
DankTextField {
|
|
||||||
id: latitudeField
|
|
||||||
width: 90
|
|
||||||
height: 32
|
|
||||||
text: SessionData.latitude ? SessionData.latitude.toString() : ""
|
|
||||||
placeholderText: "40.7128"
|
|
||||||
maximumLength: 10
|
|
||||||
topPadding: Theme.spacingXS
|
|
||||||
bottomPadding: Theme.spacingXS
|
|
||||||
normalBorderColor: {
|
|
||||||
if (text.length === 0) return Theme.outlineStrong;
|
|
||||||
var lat = parseFloat(text);
|
|
||||||
return (!isNaN(lat) && lat >= -90 && lat <= 90) ? Theme.success : Theme.error;
|
|
||||||
}
|
|
||||||
onAccepted: {
|
|
||||||
var lat = parseFloat(text);
|
|
||||||
if (!isNaN(lat) && lat >= -90 && lat <= 90) {
|
|
||||||
SessionData.setLatitude(lat);
|
|
||||||
} else {
|
|
||||||
text = SessionData.latitude ? SessionData.latitude.toString() : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
var lat = parseFloat(text);
|
|
||||||
if (!isNaN(lat) && lat >= -90 && lat <= 90) {
|
|
||||||
SessionData.setLatitude(lat);
|
|
||||||
} else {
|
|
||||||
text = SessionData.latitude ? SessionData.latitude.toString() : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: ","
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
DankTextField {
|
|
||||||
id: longitudeField
|
|
||||||
width: 90
|
|
||||||
height: 32
|
|
||||||
text: SessionData.longitude ? SessionData.longitude.toString() : ""
|
|
||||||
placeholderText: "-74.0060"
|
|
||||||
maximumLength: 11
|
|
||||||
topPadding: Theme.spacingXS
|
|
||||||
bottomPadding: Theme.spacingXS
|
|
||||||
normalBorderColor: {
|
|
||||||
if (text.length === 0) return Theme.outlineStrong;
|
|
||||||
var lon = parseFloat(text);
|
|
||||||
return (!isNaN(lon) && lon >= -180 && lon <= 180) ? Theme.success : Theme.error;
|
|
||||||
}
|
|
||||||
onAccepted: {
|
|
||||||
var lon = parseFloat(text);
|
|
||||||
if (!isNaN(lon) && lon >= -180 && lon <= 180) {
|
|
||||||
SessionData.setLongitude(lon);
|
|
||||||
} else {
|
|
||||||
text = SessionData.longitude ? SessionData.longitude.toString() : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
var lon = parseFloat(text);
|
|
||||||
if (!isNaN(lon) && lon >= -180 && lon <= 180) {
|
|
||||||
SessionData.setLongitude(lon);
|
|
||||||
} else {
|
|
||||||
text = SessionData.longitude ? SessionData.longitude.toString() : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "(lat, lng)"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledRect {
|
|
||||||
width: 60
|
|
||||||
height: 32
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.surfaceVariant
|
|
||||||
border.color: Theme.outline
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Clear"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
stateColor: Theme.error
|
|
||||||
cornerRadius: parent.radius
|
|
||||||
onClicked: {
|
|
||||||
console.log("PersonalizationTab: Clearing location coordinates via UI")
|
|
||||||
SessionData.setLatitude(0.0);
|
|
||||||
SessionData.setLongitude(0.0);
|
|
||||||
latitudeField.text = "";
|
|
||||||
longitudeField.text = "";
|
|
||||||
// Also call the service clear function
|
|
||||||
if (typeof globalThis !== 'undefined' && globalThis.clearNightModeLocation) {
|
|
||||||
globalThis.clearNightModeLocation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import QtQuick
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
@@ -33,7 +32,18 @@ Singleton {
|
|||||||
signal brightnessChanged
|
signal brightnessChanged
|
||||||
signal deviceSwitched
|
signal deviceSwitched
|
||||||
|
|
||||||
property bool nightModeActive: false
|
property bool nightModeActive: nightModeEnabled
|
||||||
|
|
||||||
|
// Night Mode Properties
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
function setBrightnessInternal(percentage, device) {
|
function setBrightnessInternal(percentage, device) {
|
||||||
const clampedValue = Math.max(1, Math.min(100, percentage))
|
const clampedValue = Math.max(1, Math.min(100, percentage))
|
||||||
@@ -214,58 +224,211 @@ Singleton {
|
|||||||
ddcInitialBrightnessProcess.running = true
|
ddcInitialBrightnessProcess.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Night Mode Functions - Simplified
|
||||||
function enableNightMode() {
|
function enableNightMode() {
|
||||||
if (nightModeActive)
|
if (!automationAvailable) {
|
||||||
|
gammaStepTestProcess.running = true
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Test if gammastep exists before enabling
|
nightModeEnabled = true
|
||||||
gammaStepTestProcess.running = true
|
SessionData.setNightModeEnabled(true)
|
||||||
}
|
|
||||||
|
// Apply immediately or start automation
|
||||||
function updateNightModeTemperature(temperature) {
|
if (SessionData.nightModeAutoEnabled) {
|
||||||
SessionData.setNightModeTemperature(temperature)
|
startAutomation()
|
||||||
|
} else {
|
||||||
// If night mode is active, restart it with new temperature
|
applyNightModeDirectly()
|
||||||
if (nightModeActive) {
|
|
||||||
// Temporarily disable and re-enable to restart with new temp
|
|
||||||
nightModeActive = false
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (SessionData.nightModeEnabled) {
|
|
||||||
nightModeActive = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableNightMode() {
|
function disableNightMode() {
|
||||||
nightModeActive = false
|
nightModeEnabled = false
|
||||||
SessionData.setNightModeEnabled(false)
|
SessionData.setNightModeEnabled(false)
|
||||||
|
stopAutomation()
|
||||||
// Also kill any stray gammastep processes
|
// Nuclear approach - kill ALL gammastep processes multiple times
|
||||||
Quickshell.execDetached(["pkill", "gammastep"])
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleNightMode() {
|
function toggleNightMode() {
|
||||||
// Check if automation is active - show warning if trying to manually toggle
|
if (nightModeEnabled) {
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
|
||||||
ToastService.showWarning("Night mode is in automatic mode. Disable automation in settings to control manually.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nightModeActive) {
|
|
||||||
disableNightMode()
|
disableNightMode()
|
||||||
} else {
|
} else {
|
||||||
enableNightMode()
|
enableNightMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyNightModeDirectly() {
|
||||||
|
const temperature = SessionData.nightModeTemperature || 4500
|
||||||
|
gammaStepProcess.command = buildGammastepCommand([
|
||||||
|
"-m", "wayland",
|
||||||
|
"-O", String(temperature)
|
||||||
|
])
|
||||||
|
gammaStepProcess.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetToNormalMode() {
|
||||||
|
// Just kill gammastep to return to normal display temperature
|
||||||
|
Quickshell.execDetached(["pkill", "gammastep"])
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAutomation() {
|
||||||
|
if (!automationAvailable) return
|
||||||
|
|
||||||
|
const mode = SessionData.nightModeAutoMode || "time"
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case "time":
|
||||||
|
startTimeBasedMode()
|
||||||
|
break
|
||||||
|
case "location":
|
||||||
|
startLocationBasedMode()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLocationBasedMode() {
|
||||||
|
const temperature = SessionData.nightModeTemperature || 4500
|
||||||
|
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 now = new Date()
|
||||||
|
const currentTime = now.getHours() * 60 + now.getMinutes()
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectLocationProviders() {
|
||||||
|
geoclueDetectionProcess.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeAutomationMode(mode) {
|
||||||
|
console.log("DisplayService: Setting night mode automation mode to:", mode)
|
||||||
|
SessionData.setNightModeAutoMode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateNightMode() {
|
||||||
|
console.log("DisplayService: Evaluating night mode state - enabled:", nightModeEnabled, "auto:", SessionData.nightModeAutoEnabled)
|
||||||
|
|
||||||
|
// Always stop all processes first to clean slate
|
||||||
|
stopAutomation()
|
||||||
|
|
||||||
|
if (!nightModeEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SessionData.nightModeAutoEnabled) {
|
||||||
|
restartTimer.nextAction = "automation"
|
||||||
|
restartTimer.start()
|
||||||
|
} else {
|
||||||
|
restartTimer.nextAction = "direct"
|
||||||
|
restartTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNightModeAvailability() {
|
||||||
|
gammastepAvailabilityProcess.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: restartTimer
|
||||||
|
property string nextAction: ""
|
||||||
|
interval: 100
|
||||||
|
repeat: false
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
if (nextAction === "automation") {
|
||||||
|
startAutomation()
|
||||||
|
} else if (nextAction === "direct") {
|
||||||
|
applyNightModeDirectly()
|
||||||
|
}
|
||||||
|
nextAction = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
ddcDetectionProcess.running = true
|
ddcDetectionProcess.running = true
|
||||||
refreshDevices()
|
refreshDevices()
|
||||||
|
checkNightModeAvailability()
|
||||||
|
|
||||||
// Check if night mode was enabled on startup
|
// Initialize night mode state from session
|
||||||
if (SessionData.nightModeEnabled) {
|
nightModeEnabled = SessionData.nightModeEnabled
|
||||||
enableNightMode()
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
precision: SystemClock.Minutes
|
||||||
|
onDateChanged: {
|
||||||
|
if (nightModeEnabled && SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
|
||||||
|
checkTimeBasedMode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,27 +556,11 @@ Singleton {
|
|||||||
"max": parseInt(parts[4])
|
"max": parseInt(parts[4])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Store brightnessctl devices separately, will be combined with DDC
|
// Store brightnessctl devices separately
|
||||||
const brightnessCtlDevices = newDevices
|
devices = newDevices
|
||||||
devices = brightnessCtlDevices
|
|
||||||
|
// Always refresh to combine with DDC devices and set up device selection
|
||||||
// If we have DDC devices, combine them
|
refreshDevicesInternal()
|
||||||
if (ddcDevices.length > 0) {
|
|
||||||
refreshDevicesInternal()
|
|
||||||
} else if (devices.length > 0 && !currentDevice) {
|
|
||||||
// Try to restore last selected device, fallback to first device
|
|
||||||
const lastDevice = SessionData.lastBrightnessDevice || ""
|
|
||||||
const deviceExists = devices.some(
|
|
||||||
d => d.name === lastDevice)
|
|
||||||
if (deviceExists) {
|
|
||||||
setCurrentDevice(lastDevice, false)
|
|
||||||
} else {
|
|
||||||
const nonKbdDevice = devices.find(
|
|
||||||
d => !d.name.includes("kbd"))
|
|
||||||
|| devices[0]
|
|
||||||
setCurrentDevice(nonKbdDevice.name, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,46 +711,108 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: gammaStepTestProcess
|
id: gammastepAvailabilityProcess
|
||||||
|
command: ["which", "gammastep"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function(exitCode) {
|
||||||
|
automationAvailable = (exitCode === 0)
|
||||||
|
if (automationAvailable) {
|
||||||
|
console.log("DisplayService: gammastep available")
|
||||||
|
detectLocationProviders()
|
||||||
|
|
||||||
|
// If night mode should be enabled on startup
|
||||||
|
if (nightModeEnabled && SessionData.nightModeAutoEnabled) {
|
||||||
|
startAutomation()
|
||||||
|
} else if (nightModeEnabled) {
|
||||||
|
applyNightModeDirectly()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("DisplayService: gammastep not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: geoclueDetectionProcess
|
||||||
|
command: ["sh", "-c", "busctl --system list | grep -qF org.freedesktop.GeoClue2"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function (exitCode) {
|
||||||
|
geoclueAvailable = (exitCode === 0)
|
||||||
|
console.log("DisplayService: geoclue available:", geoclueAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: gammaStepTestProcess
|
||||||
command: ["which", "gammastep"]
|
command: ["which", "gammastep"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
// gammastep exists, enable night mode
|
automationAvailable = true
|
||||||
nightModeActive = true
|
nightModeEnabled = true
|
||||||
SessionData.setNightModeEnabled(true)
|
SessionData.setNightModeEnabled(true)
|
||||||
|
|
||||||
|
if (SessionData.nightModeAutoEnabled) {
|
||||||
|
startAutomation()
|
||||||
|
} else {
|
||||||
|
applyNightModeDirectly()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// gammastep not found
|
|
||||||
console.warn("DisplayService: gammastep not found")
|
console.warn("DisplayService: gammastep not found")
|
||||||
ToastService.showWarning(
|
ToastService.showWarning("Night mode failed: gammastep not found")
|
||||||
"Night mode failed: gammastep not found")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: gammaStepProcess
|
id: gammaStepProcess
|
||||||
|
running: false
|
||||||
command: {
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
return ["gammastep", "-m", "wayland", "-O", String(temperature)]
|
|
||||||
}
|
|
||||||
running: nightModeActive
|
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
// If process exits with non-zero code while we think it should be running
|
if (nightModeEnabled && exitCode !== 0 && exitCode !== 15) {
|
||||||
if (nightModeActive && exitCode !== 0) {
|
console.warn("DisplayService: Night mode process failed:", exitCode)
|
||||||
console.warn("DisplayService: Night mode process crashed with exit code:",
|
|
||||||
exitCode)
|
|
||||||
nightModeActive = false
|
|
||||||
SessionData.setNightModeEnabled(false)
|
|
||||||
ToastService.showWarning("Night mode failed: process crashed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session Data Connections
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
|
||||||
|
function onNightModeEnabledChanged() {
|
||||||
|
nightModeEnabled = SessionData.nightModeEnabled
|
||||||
|
evaluateNightMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNightModeAutoEnabledChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeAutoModeChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeStartHourChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeStartMinuteChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeEndHourChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeEndMinuteChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeTemperatureChanged() { evaluateNightMode() }
|
||||||
|
function onLatitudeChanged() { evaluateNightMode() }
|
||||||
|
function onLongitudeChanged() { evaluateNightMode() }
|
||||||
|
function onNightModeLocationProviderChanged() { evaluateNightMode() }
|
||||||
|
}
|
||||||
|
|
||||||
// IPC Handler for external control
|
// IPC Handler for external control
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function set(percentage: string, device: string): string {
|
function set(percentage: string, device: string): string {
|
||||||
@@ -702,9 +911,9 @@ Singleton {
|
|||||||
if (!root.brightnessAvailable)
|
if (!root.brightnessAvailable)
|
||||||
return "No brightness devices available"
|
return "No brightness devices available"
|
||||||
|
|
||||||
let result = "Available devices:\n"
|
let result = "Available devices:\\n"
|
||||||
for (const device of root.devices) {
|
for (const device of root.devices) {
|
||||||
result += device.name + " (" + device.class + ")\n"
|
result += device.name + " (" + device.class + ")\\n"
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -716,7 +925,7 @@ Singleton {
|
|||||||
IpcHandler {
|
IpcHandler {
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
root.toggleNightMode()
|
root.toggleNightMode()
|
||||||
return root.nightModeActive ? "Night mode enabled" : "Night mode disabled"
|
return root.nightModeEnabled ? "Night mode enabled" : "Night mode disabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
function enable(): string {
|
function enable(): string {
|
||||||
@@ -730,7 +939,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function status(): string {
|
function status(): string {
|
||||||
return root.nightModeActive ? "Night mode is enabled" : "Night mode is disabled"
|
return root.nightModeEnabled ? "Night mode is enabled" : "Night mode is disabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
function temperature(value: string): string {
|
function temperature(value: string): string {
|
||||||
@@ -753,12 +962,13 @@ Singleton {
|
|||||||
|
|
||||||
SessionData.setNightModeTemperature(rounded)
|
SessionData.setNightModeTemperature(rounded)
|
||||||
|
|
||||||
// If night mode is active, restart it with new temperature
|
// Restart night mode with new temperature if active
|
||||||
if (root.nightModeActive) {
|
if (root.nightModeEnabled) {
|
||||||
root.nightModeActive = false
|
if (SessionData.nightModeAutoEnabled) {
|
||||||
Qt.callLater(() => {
|
root.startAutomation()
|
||||||
root.nightModeActive = true
|
} else {
|
||||||
})
|
root.applyNightModeDirectly()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rounded !== temp) {
|
if (rounded !== temp) {
|
||||||
@@ -770,4 +980,4 @@ Singleton {
|
|||||||
|
|
||||||
target: "night"
|
target: "night"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,505 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import qs.Common
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool automationAvailable: false
|
|
||||||
property bool locationProviderAvailable: false
|
|
||||||
property var availableProviders: []
|
|
||||||
property string currentProvider: ""
|
|
||||||
property bool isAutomaticNightTime: false
|
|
||||||
property string currentLocation: ""
|
|
||||||
property real latitude: 0.0
|
|
||||||
property real longitude: 0.0
|
|
||||||
|
|
||||||
function testGeoclueConnection() {
|
|
||||||
geoclueTestProcess.running = false
|
|
||||||
geoclueTestProcess.command = [
|
|
||||||
"timeout", "32",
|
|
||||||
"gammastep",
|
|
||||||
"-m", "wayland",
|
|
||||||
"-l", "geoclue2",
|
|
||||||
"-O", "6500",
|
|
||||||
"-v"
|
|
||||||
]
|
|
||||||
geoclueTestProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
checkAvailability()
|
|
||||||
updateFromSessionData()
|
|
||||||
|
|
||||||
if (typeof globalThis !== 'undefined') {
|
|
||||||
globalThis.testNightMode = manualNightModeTest
|
|
||||||
globalThis.resetNightMode = manualResetTest
|
|
||||||
globalThis.clearNightModeLocation = clearLocation
|
|
||||||
globalThis.nightModeService = root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkAvailability() {
|
|
||||||
gammaStepTestProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function startAutomation() {
|
|
||||||
if (!automationAvailable) {
|
|
||||||
console.warn("NightModeAutomationService: Gammastep not available")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop any existing automation processes first
|
|
||||||
stopAutomation()
|
|
||||||
|
|
||||||
const mode = SessionData.nightModeAutoMode || "manual"
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case "time":
|
|
||||||
startTimeBasedMode()
|
|
||||||
break
|
|
||||||
case "location":
|
|
||||||
startLocationBasedMode()
|
|
||||||
break
|
|
||||||
case "manual":
|
|
||||||
default:
|
|
||||||
stopAutomation()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopAutomation() {
|
|
||||||
|
|
||||||
// Stop the unified process
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
|
|
||||||
isAutomaticNightTime = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function startTimeBasedMode() {
|
|
||||||
checkTimeBasedMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
function startLocationBasedMode() {
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
const dayTemp = 6500
|
|
||||||
|
|
||||||
gammaStepProcess.processType = "automation"
|
|
||||||
|
|
||||||
if (latitude !== 0.0 && longitude !== 0.0) {
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-l", `${latitude.toFixed(6)}:${longitude.toFixed(6)}`,
|
|
||||||
"-t", `${dayTemp}:${temperature}`,
|
|
||||||
"-v"
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if location providers are available
|
|
||||||
if (!locationProviderAvailable) {
|
|
||||||
console.warn("NightModeAutomationService: No location provider available, falling back to time-based mode")
|
|
||||||
SessionData.setNightModeAutoMode("time")
|
|
||||||
startTimeBasedMode()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentProvider === "geoclue2") {
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
const dayTemp = 6500
|
|
||||||
|
|
||||||
gammaStepProcess.processType = "automation"
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-l", "geoclue2",
|
|
||||||
"-t", `${dayTemp}:${temperature}`,
|
|
||||||
"-v"
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
console.warn("NightModeAutomationService: No working location provider, falling back to time-based mode")
|
|
||||||
SessionData.setNightModeAutoMode("time")
|
|
||||||
startTimeBasedMode()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkTimeBasedMode() {
|
|
||||||
if (!SessionData.nightModeAutoEnabled || SessionData.nightModeAutoMode !== "time") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date()
|
|
||||||
const currentHour = now.getHours()
|
|
||||||
const currentMinute = now.getMinutes()
|
|
||||||
const currentTime = currentHour * 60 + currentMinute
|
|
||||||
|
|
||||||
const startTime = SessionData.nightModeStartTime || "20:00"
|
|
||||||
const endTime = SessionData.nightModeEndTime || "06:00"
|
|
||||||
|
|
||||||
const startParts = startTime.split(":")
|
|
||||||
const endParts = endTime.split(":")
|
|
||||||
|
|
||||||
const startMinutes = parseInt(startParts[0]) * 60 + parseInt(startParts[1])
|
|
||||||
const endMinutes = parseInt(endParts[0]) * 60 + parseInt(endParts[1])
|
|
||||||
|
|
||||||
let shouldBeNight = false
|
|
||||||
|
|
||||||
if (startMinutes > endMinutes) {
|
|
||||||
shouldBeNight = (currentTime >= startMinutes) || (currentTime < endMinutes)
|
|
||||||
} else {
|
|
||||||
shouldBeNight = (currentTime >= startMinutes) && (currentTime < endMinutes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldBeNight !== isAutomaticNightTime) {
|
|
||||||
isAutomaticNightTime = shouldBeNight
|
|
||||||
|
|
||||||
if (shouldBeNight) {
|
|
||||||
requestNightModeActivation()
|
|
||||||
} else {
|
|
||||||
requestNightModeDeactivation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestNightModeActivation() {
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
gammaStepProcess.processType = "activate"
|
|
||||||
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-O", String(temperature)
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode !== "manual") {
|
|
||||||
SessionData.setNightModeEnabled(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestNightModeDeactivation() {
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
|
|
||||||
gammaStepProcess.processType = "reset"
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-O", "6500"
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode !== "manual") {
|
|
||||||
SessionData.setNightModeEnabled(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLocation(lat, lon) {
|
|
||||||
latitude = lat
|
|
||||||
longitude = lon
|
|
||||||
currentLocation = `${lat.toFixed(6)},${lon.toFixed(6)}`
|
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
|
|
||||||
startLocationBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearLocation() {
|
|
||||||
latitude = 0.0
|
|
||||||
longitude = 0.0
|
|
||||||
currentLocation = ""
|
|
||||||
|
|
||||||
SessionData.setLatitude(0.0)
|
|
||||||
SessionData.setLongitude(0.0)
|
|
||||||
SessionData.saveSettings()
|
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
|
|
||||||
startLocationBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGammastepCommand(gammastepArgs) {
|
|
||||||
const commandStr = "pkill gammastep; " + ["gammastep"].concat(gammastepArgs).join(" ")
|
|
||||||
return ["sh", "-c", commandStr]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function updateFromSessionData() {
|
|
||||||
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
|
||||||
setLocation(SessionData.latitude, SessionData.longitude)
|
|
||||||
} else {
|
|
||||||
latitude = 0.0
|
|
||||||
longitude = 0.0
|
|
||||||
currentLocation = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectLocationProviders() {
|
|
||||||
locationProviderDetectionProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function manualNightModeTest() {
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
gammaStepProcess.processType = "test"
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-O", String(temperature)
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
|
|
||||||
testFeedbackTimer.interval = 2000
|
|
||||||
testFeedbackTimer.feedbackMessage = "Night mode test applied"
|
|
||||||
testFeedbackTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
function manualResetTest() {
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
gammaStepProcess.processType = "test"
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-O", "6500"
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
|
|
||||||
testFeedbackTimer.interval = 2000
|
|
||||||
testFeedbackTimer.feedbackMessage = "Screen reset to normal temperature"
|
|
||||||
testFeedbackTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemClock {
|
|
||||||
id: systemClock
|
|
||||||
precision: SystemClock.Minutes
|
|
||||||
onDateChanged: {
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
|
|
||||||
checkTimeBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gammaStepTestProcess
|
|
||||||
command: ["which", "gammastep"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
automationAvailable = (exitCode === 0)
|
|
||||||
if (automationAvailable) {
|
|
||||||
detectLocationProviders()
|
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
|
||||||
startAutomation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: locationProviderDetectionProcess
|
|
||||||
command: ["gammastep", "-l", "list"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim()) {
|
|
||||||
// Parse provider names - they start with whitespace and are single words
|
|
||||||
const lines = text.trim().split('\n')
|
|
||||||
const providers = lines.filter(line => {
|
|
||||||
const trimmed = line.trim()
|
|
||||||
// Provider names are single words that start with whitespace in original line
|
|
||||||
return line.startsWith(' ') &&
|
|
||||||
trimmed.length > 0 &&
|
|
||||||
!trimmed.includes(' ') &&
|
|
||||||
!trimmed.includes(':') &&
|
|
||||||
!trimmed.includes('.')
|
|
||||||
}).map(line => line.trim())
|
|
||||||
|
|
||||||
availableProviders = providers
|
|
||||||
locationProviderAvailable = providers.length > 0
|
|
||||||
|
|
||||||
if (locationProviderAvailable && !currentProvider) {
|
|
||||||
currentProvider = providers[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Providers detected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
locationProviderAvailable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: geoclueTestProcess
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
const temperature = SessionData.nightModeTemperature || 4500
|
|
||||||
const dayTemp = 6500
|
|
||||||
|
|
||||||
gammaStepProcess.processType = "automation"
|
|
||||||
gammaStepProcess.command = buildGammastepCommand([
|
|
||||||
"-m", "wayland",
|
|
||||||
"-l", "geoclue2",
|
|
||||||
"-t", `${dayTemp}:${temperature}`,
|
|
||||||
"-v"
|
|
||||||
])
|
|
||||||
gammaStepProcess.running = true
|
|
||||||
} else {
|
|
||||||
SessionData.setNightModeAutoMode("time")
|
|
||||||
startTimeBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gammaStepProcess
|
|
||||||
running: false
|
|
||||||
|
|
||||||
property string processType: "" // "automation", "activate", "reset", "test"
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
stderr: StdioCollector {
|
|
||||||
onStreamFinished: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (running) {
|
|
||||||
// Start timeout only for test commands - automation, activate, and reset should not timeout
|
|
||||||
if (processType === "test") {
|
|
||||||
processTimeoutTimer.start()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Stop timeout when process ends
|
|
||||||
processTimeoutTimer.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
processTimeoutTimer.stop()
|
|
||||||
|
|
||||||
const isSuccessfulCompletion = (exitCode === 0) ||
|
|
||||||
((processType === "activate" || processType === "reset") && exitCode === 15)
|
|
||||||
|
|
||||||
if (!isSuccessfulCompletion) {
|
|
||||||
switch(processType) {
|
|
||||||
case "automation":
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
|
|
||||||
if (exitCode === 15 || exitCode === 124) {
|
|
||||||
SessionData.setNightModeAutoMode("time")
|
|
||||||
startTimeBasedMode()
|
|
||||||
} else {
|
|
||||||
restartTimer.start()
|
|
||||||
}
|
|
||||||
} else if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
|
|
||||||
restartTimer.start()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: processTimeoutTimer
|
|
||||||
interval: 30000 // 30 second timeout (increased from 10s for one-shot commands)
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (gammaStepProcess.running) {
|
|
||||||
console.warn("NightModeAutomationService: Test process timed out, killing process")
|
|
||||||
// Only kill test processes that have timed out
|
|
||||||
if (gammaStepProcess.processType === "test") {
|
|
||||||
gammaStepProcess.running = false
|
|
||||||
} else {
|
|
||||||
console.warn("NightModeAutomationService: Non-test process still running after timeout, but not killing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: restartTimer
|
|
||||||
interval: 10000
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "location") {
|
|
||||||
startLocationBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: testFeedbackTimer
|
|
||||||
interval: 2000
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
property string feedbackMessage: ""
|
|
||||||
onTriggered: {
|
|
||||||
if (feedbackMessage.length > 0) {
|
|
||||||
console.log(feedbackMessage)
|
|
||||||
feedbackMessage = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: SessionData
|
|
||||||
function onNightModeAutoEnabledChanged() {
|
|
||||||
console.log("NightModeAutomationService: Auto enabled changed to", SessionData.nightModeAutoEnabled)
|
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
|
||||||
startAutomation()
|
|
||||||
} else {
|
|
||||||
stopAutomation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onNightModeAutoModeChanged() {
|
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
|
||||||
startAutomation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onNightModeStartTimeChanged() {
|
|
||||||
console.log("NightModeAutomationService: Start time changed to", SessionData.nightModeStartTime)
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
|
|
||||||
checkTimeBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onNightModeEndTimeChanged() {
|
|
||||||
console.log("NightModeAutomationService: End time changed to", SessionData.nightModeEndTime)
|
|
||||||
if (SessionData.nightModeAutoEnabled && SessionData.nightModeAutoMode === "time") {
|
|
||||||
checkTimeBasedMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onNightModeTemperatureChanged() {
|
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
|
||||||
startAutomation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onLatitudeChanged() {
|
|
||||||
updateFromSessionData()
|
|
||||||
}
|
|
||||||
function onLongitudeChanged() {
|
|
||||||
updateFromSessionData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,6 +49,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: dropdown.left
|
anchors.right: dropdown.left
|
||||||
@@ -77,7 +78,7 @@ Rectangle {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: dropdown
|
id: dropdown
|
||||||
|
|
||||||
width: 180
|
width: root.width <= 60 ? root.width : 180
|
||||||
height: 36
|
height: 36
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
@@ -123,16 +124,18 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
size: 18
|
size: 18
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: name !== ""
|
visible: name !== "" && root.width > 60
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: root.currentValue
|
text: root.currentValue
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
width: dropdown.width - contentRow.x - expandIcon.width
|
width: root.width <= 60 ?
|
||||||
- Theme.spacingM - Theme.spacingS
|
(dropdown.width - expandIcon.width - Theme.spacingS * 2) :
|
||||||
elide: Text.ElideRight
|
(dropdown.width - contentRow.x - expandIcon.width - Theme.spacingM - Theme.spacingS)
|
||||||
|
elide: root.width <= 60 ? Text.ElideNone : Text.ElideRight
|
||||||
|
horizontalAlignment: root.width <= 60 ? Text.AlignHCenter : Text.AlignLeft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ ShellRoot {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
PortalService.init()
|
PortalService.init()
|
||||||
// Initialize NightModeAutomationService to trigger its Component.onCompleted
|
// Initialize DisplayService night mode functionality
|
||||||
NightModeAutomationService.automationAvailable
|
DisplayService.nightModeEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
WallpaperBackground {
|
WallpaperBackground {
|
||||||
|
|||||||
Reference in New Issue
Block a user