1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-30 17:42:06 -04:00

logger: add a dedicated QML logging Singleton

- adds log.info/error/debug/warn/fatal
- adds ability to write logs to any file
- add CLI options in addition to env to set log levels
This commit is contained in:
bbedward
2026-04-29 15:40:44 -04:00
parent 3b96c6ab22
commit f76724f7cd
84 changed files with 1764 additions and 1297 deletions

View File

@@ -4,9 +4,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("AppSearchService")
property var applications: []
property var _cachedCategories: null
@@ -811,7 +813,7 @@ Singleton {
});
isPersistent = false;
} catch (e) {
console.warn("AppSearchService: Error creating temporary plugin instance", pluginId, ":", e);
log.warn("Error creating temporary plugin instance", pluginId, ":", e);
return [];
}
}
@@ -831,7 +833,7 @@ Singleton {
instance.destroy();
}
} catch (e) {
console.warn("AppSearchService: Error getting items from plugin", pluginId, ":", e);
log.warn("Error getting items from plugin", pluginId, ":", e);
if (!isPersistent)
instance.destroy();
}
@@ -857,7 +859,7 @@ Singleton {
});
isPersistent = false;
} catch (e) {
console.warn("AppSearchService: Error creating temporary plugin instance for execution", pluginId, ":", e);
log.warn("Error creating temporary plugin instance for execution", pluginId, ":", e);
return false;
}
}
@@ -877,7 +879,7 @@ Singleton {
instance.destroy();
}
} catch (e) {
console.warn("AppSearchService: Error executing item from plugin", pluginId, ":", e);
log.warn("Error executing item from plugin", pluginId, ":", e);
if (!isPersistent)
instance.destroy();
}
@@ -949,7 +951,7 @@ Singleton {
try {
return instance.getCategories() || [];
} catch (e) {
console.warn("AppSearchService: Error getting categories from plugin", pluginId, ":", e);
log.warn("Error getting categories from plugin", pluginId, ":", e);
return [];
}
}
@@ -968,7 +970,7 @@ Singleton {
try {
instance.setCategory(categoryId);
} catch (e) {
console.warn("AppSearchService: Error setting category on plugin", pluginId, ":", e);
log.warn("Error setting category on plugin", pluginId, ":", e);
}
}

View File

@@ -11,6 +11,7 @@ import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("AudioService")
readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource
@@ -143,7 +144,7 @@ Singleton {
function setDeviceAlias(nodeName, customAlias) {
if (!nodeName) {
console.error("AudioService: Cannot set alias - nodeName is empty");
log.error("Cannot set alias - nodeName is empty");
return false;
}
@@ -189,8 +190,8 @@ EOFCONFIG
Proc.runCommand("writeWireplumberConfig", ["sh", "-c", shellCmd], (output, exitCode) => {
if (exitCode !== 0) {
console.error("AudioService: Failed to write WirePlumber config. Exit code:", exitCode);
console.error("AudioService: Error output:", output);
log.error("Failed to write WirePlumber config. Exit code:", exitCode);
log.error("Error output:", output);
ToastService.showError(I18n.tr("Failed to save audio config"), output || "");
return;
}
@@ -305,7 +306,7 @@ EOFCONFIG
ToastService.showInfo(I18n.tr("Audio system restarted"), I18n.tr("Device names updated"));
wireplumberReloadCompleted(true);
} else {
console.error("AudioService: Failed to restart WirePlumber:", output);
log.error("Failed to restart WirePlumber:", output);
ToastService.showError(I18n.tr("Failed to restart audio system"), output);
wireplumberReloadCompleted(false);
}
@@ -317,7 +318,7 @@ EOFCONFIG
Proc.runCommand("readWireplumberConfig", ["cat", configPath], (output, exitCode) => {
if (exitCode !== 0) {
console.log("AudioService: No existing WirePlumber config found");
log.debug("No existing WirePlumber config found");
return;
}
@@ -340,7 +341,7 @@ EOFCONFIG
if (Object.keys(aliases).length > 0) {
deviceAliases = aliases;
console.log("AudioService: Loaded", Object.keys(aliases).length, "device aliases");
log.debug("Loaded", Object.keys(aliases).length, "device aliases");
}
}, 0);
}
@@ -394,13 +395,13 @@ EOFCONFIG
Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => {
if (exitCode === 0 && output.trim()) {
currentSoundTheme = output.trim();
console.log("AudioService: Current system sound theme:", currentSoundTheme);
log.debug("Current system sound theme:", currentSoundTheme);
if (SettingsData.useSystemSoundTheme) {
discoverSoundFiles(currentSoundTheme);
}
} else {
currentSoundTheme = "";
console.log("AudioService: No system sound theme found");
log.debug("No system sound theme found");
}
}, 0);
}
@@ -510,22 +511,22 @@ EOFCONFIG
const themeLower = currentSoundTheme.toLowerCase();
if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) {
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath);
log.debug("Using bundled sound (special condition) for", soundEvent, ":", bundledPath);
return bundledPath;
}
if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) {
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]);
log.debug("Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]);
return soundFilePaths[soundEvent];
}
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath);
log.debug("Using bundled sound for", soundEvent, ":", bundledPath);
return bundledPath;
}
function reloadSounds() {
console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme);
log.debug("Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme);
if (SettingsData.useSystemSoundTheme && currentSoundTheme) {
discoverSoundFiles(currentSoundTheme);
} else {
@@ -549,7 +550,7 @@ EOFCONFIG
MediaDevices {
id: devices
Component.onCompleted: {
console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description)
log.debug("MediaDevices initialized, default output:", defaultAudioOutput?.description)
}
}
`, root, "AudioService.MediaDevices");
@@ -560,7 +561,7 @@ EOFCONFIG
Connections {
target: root.mediaDevices
function onDefaultAudioOutputChanged() {
console.log("AudioService: Default audio output changed, recreating sound players")
log.debug("Default audio output changed, recreating sound players")
root.destroySoundPlayers()
root.createSoundPlayers()
}
@@ -568,7 +569,7 @@ EOFCONFIG
`, root, "AudioService.MediaDevicesConnections");
}
} catch (e) {
console.log("AudioService: MediaDevices not available, using default audio output");
log.debug("MediaDevices not available, using default audio output");
mediaDevices = null;
}
}
@@ -682,7 +683,7 @@ EOFCONFIG
}
`, root, "AudioService.LoginSound");
} catch (e) {
console.warn("AudioService: Error creating sound players:", e);
log.warn("Error creating sound players:", e);
}
}

View File

@@ -1,5 +1,4 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
@@ -19,206 +18,217 @@ Singleton {
readonly property bool enhancedPairingAvailable: DMSService.dmsAvailable && DMSService.apiVersion >= 9 && DMSService.capabilities.includes("bluetooth")
readonly property bool connected: {
if (!adapter || !adapter.devices) {
return false
return false;
}
let isConnected = false
adapter.devices.values.forEach(dev => { if (dev.connected) isConnected = true })
return isConnected
let isConnected = false;
adapter.devices.values.forEach(dev => {
if (dev.connected)
isConnected = true;
});
return isConnected;
}
readonly property var pairedDevices: {
if (!adapter || !adapter.devices) {
return []
return [];
}
return adapter.devices.values.filter(dev => {
return dev && (dev.paired || dev.trusted)
})
return dev && (dev.paired || dev.trusted);
});
}
readonly property var allDevicesWithBattery: {
if (!adapter || !adapter.devices) {
return []
return [];
}
return adapter.devices.values.filter(dev => {
return dev && dev.batteryAvailable && dev.battery > 0
})
return dev && dev.batteryAvailable && dev.battery > 0;
});
}
function sortDevices(devices) {
return devices.sort((a, b) => {
const aName = a.name || a.deviceName || ""
const bName = b.name || b.deviceName || ""
const aAddr = a.address || ""
const bAddr = b.address || ""
const aName = a.name || a.deviceName || "";
const bName = b.name || b.deviceName || "";
const aAddr = a.address || "";
const bAddr = b.address || "";
const aHasRealName = aName.includes(" ") && aName.length > 3
const bHasRealName = bName.includes(" ") && bName.length > 3
const aHasRealName = aName.includes(" ") && aName.length > 3;
const bHasRealName = bName.includes(" ") && bName.length > 3;
if (aHasRealName && !bHasRealName) return -1
if (!aHasRealName && bHasRealName) return 1
if (aHasRealName && !bHasRealName)
return -1;
if (!aHasRealName && bHasRealName)
return 1;
if (aHasRealName && bHasRealName) {
return aName.localeCompare(bName)
}
if (aHasRealName && bHasRealName) {
return aName.localeCompare(bName);
}
return aAddr.localeCompare(bAddr)
})
return aAddr.localeCompare(bAddr);
});
}
function getDeviceIcon(device) {
if (!device) {
return "bluetooth"
return "bluetooth";
}
const name = (device.name || device.deviceName || "").toLowerCase()
const icon = (device.icon || "").toLowerCase()
const name = (device.name || device.deviceName || "").toLowerCase();
const icon = (device.icon || "").toLowerCase();
const audioKeywords = ["headset", "audio", "headphone", "airpod", "arctis"]
const audioKeywords = ["headset", "audio", "headphone", "airpod", "arctis"];
if (audioKeywords.some(keyword => icon.includes(keyword) || name.includes(keyword))) {
return "headset"
return "headset";
}
if (icon.includes("mouse") || name.includes("mouse")) {
return "mouse"
return "mouse";
}
if (icon.includes("keyboard") || name.includes("keyboard")) {
return "keyboard"
return "keyboard";
}
const phoneKeywords = ["phone", "iphone", "android", "samsung"]
const phoneKeywords = ["phone", "iphone", "android", "samsung"];
if (phoneKeywords.some(keyword => icon.includes(keyword) || name.includes(keyword))) {
return "smartphone"
return "smartphone";
}
if (icon.includes("watch") || name.includes("watch")) {
return "watch"
return "watch";
}
if (icon.includes("speaker") || name.includes("speaker")) {
return "speaker"
return "speaker";
}
if (icon.includes("display") || name.includes("tv")) {
return "tv"
return "tv";
}
return "bluetooth"
return "bluetooth";
}
function canConnect(device) {
if (!device) {
return false
return false;
}
return !device.paired && !device.pairing && !device.blocked
return !device.paired && !device.pairing && !device.blocked;
}
function getSignalStrength(device) {
if (!device || device.signalStrength === undefined || device.signalStrength <= 0) {
return "Unknown"
return "Unknown";
}
const signal = device.signalStrength
const signal = device.signalStrength;
if (signal >= 80) {
return "Excellent"
return "Excellent";
}
if (signal >= 60) {
return "Good"
return "Good";
}
if (signal >= 40) {
return "Fair"
return "Fair";
}
if (signal >= 20) {
return "Poor"
return "Poor";
}
return "Very Poor"
return "Very Poor";
}
function getSignalIcon(device) {
if (!device || device.signalStrength === undefined || device.signalStrength <= 0) {
return "signal_cellular_null"
return "signal_cellular_null";
}
const signal = device.signalStrength
const signal = device.signalStrength;
if (signal >= 80) {
return "signal_cellular_4_bar"
return "signal_cellular_4_bar";
}
if (signal >= 60) {
return "signal_cellular_3_bar"
return "signal_cellular_3_bar";
}
if (signal >= 40) {
return "signal_cellular_2_bar"
return "signal_cellular_2_bar";
}
if (signal >= 20) {
return "signal_cellular_1_bar"
return "signal_cellular_1_bar";
}
return "signal_cellular_0_bar"
return "signal_cellular_0_bar";
}
function isDeviceBusy(device) {
if (!device) {
return false
return false;
}
return device.pairing || device.state === BluetoothDeviceState.Disconnecting || device.state === BluetoothDeviceState.Connecting
return device.pairing || device.state === BluetoothDeviceState.Disconnecting || device.state === BluetoothDeviceState.Connecting;
}
function connectDeviceWithTrust(device) {
if (!device) {
return
return;
}
device.trusted = true
device.connect()
device.trusted = true;
device.connect();
}
function pairDevice(device, callback) {
if (!device) {
if (callback) callback({error: "Invalid device"})
return
if (callback)
callback({
error: "Invalid device"
});
return;
}
// The DMS backend actually implements a bluez agent, so we can pair anything
if (enhancedPairingAvailable) {
const devicePath = getDevicePath(device)
DMSService.bluetoothPair(devicePath, callback)
return
const devicePath = getDevicePath(device);
DMSService.bluetoothPair(devicePath, callback);
return;
}
// Quickshell does not implement a bluez agent, so we can try to pair but only with devices that don't require a passcode
device.trusted = true
device.connect()
if (callback) callback({success: true})
device.trusted = true;
device.connect();
if (callback)
callback({
success: true
});
}
function getCardName(device) {
if (!device) {
return ""
return "";
}
return `bluez_card.${device.address.replace(/:/g, "_")}`
return `bluez_card.${device.address.replace(/:/g, "_")}`;
}
function getDevicePath(device) {
if (!device || !device.address) {
return ""
return "";
}
const adapterPath = adapter ? "/org/bluez/hci0" : "/org/bluez/hci0"
return `${adapterPath}/dev_${device.address.replace(/:/g, "_")}`
const adapterPath = adapter ? "/org/bluez/hci0" : "/org/bluez/hci0";
return `${adapterPath}/dev_${device.address.replace(/:/g, "_")}`;
}
function isAudioDevice(device) {
if (!device) {
return false
return false;
}
const icon = getDeviceIcon(device)
return icon === "headset" || icon === "speaker"
const icon = getDeviceIcon(device);
return icon === "headset" || icon === "speaker";
}
function getCodecInfo(codecName) {
const codec = codecName.replace(/-/g, "_").toUpperCase()
const codec = codecName.replace(/-/g, "_").toUpperCase();
const codecMap = {
"LDAC": {
@@ -261,77 +271,77 @@ Singleton {
"description": "Basic speech codec • Legacy compatibility",
"qualityColor": "#9E9E9E"
}
}
};
return codecMap[codec] || {
"name": codecName,
"description": "Unknown codec",
"qualityColor": "#9E9E9E"
}
};
}
property var deviceCodecs: ({})
function updateDeviceCodec(deviceAddress, codec) {
deviceCodecs[deviceAddress] = codec
deviceCodecsChanged()
deviceCodecs[deviceAddress] = codec;
deviceCodecsChanged();
}
function refreshDeviceCodec(device) {
if (!device || !device.connected || !isAudioDevice(device)) {
return
return;
}
const cardName = getCardName(device)
codecQueryProcess.cardName = cardName
codecQueryProcess.deviceAddress = device.address
codecQueryProcess.availableCodecs = []
codecQueryProcess.parsingTargetCard = false
codecQueryProcess.detectedCodec = ""
codecQueryProcess.running = true
const cardName = getCardName(device);
codecQueryProcess.cardName = cardName;
codecQueryProcess.deviceAddress = device.address;
codecQueryProcess.availableCodecs = [];
codecQueryProcess.parsingTargetCard = false;
codecQueryProcess.detectedCodec = "";
codecQueryProcess.running = true;
}
function getCurrentCodec(device, callback) {
if (!device || !device.connected || !isAudioDevice(device)) {
callback("")
return
callback("");
return;
}
const cardName = getCardName(device)
codecQueryProcess.cardName = cardName
codecQueryProcess.callback = callback
codecQueryProcess.availableCodecs = []
codecQueryProcess.parsingTargetCard = false
codecQueryProcess.detectedCodec = ""
codecQueryProcess.running = true
const cardName = getCardName(device);
codecQueryProcess.cardName = cardName;
codecQueryProcess.callback = callback;
codecQueryProcess.availableCodecs = [];
codecQueryProcess.parsingTargetCard = false;
codecQueryProcess.detectedCodec = "";
codecQueryProcess.running = true;
}
function getAvailableCodecs(device, callback) {
if (!device || !device.connected || !isAudioDevice(device)) {
callback([], "")
return
callback([], "");
return;
}
const cardName = getCardName(device)
codecFullQueryProcess.cardName = cardName
codecFullQueryProcess.callback = callback
codecFullQueryProcess.availableCodecs = []
codecFullQueryProcess.parsingTargetCard = false
codecFullQueryProcess.detectedCodec = ""
codecFullQueryProcess.running = true
const cardName = getCardName(device);
codecFullQueryProcess.cardName = cardName;
codecFullQueryProcess.callback = callback;
codecFullQueryProcess.availableCodecs = [];
codecFullQueryProcess.parsingTargetCard = false;
codecFullQueryProcess.detectedCodec = "";
codecFullQueryProcess.running = true;
}
function switchCodec(device, profileName, callback) {
if (!device || !isAudioDevice(device)) {
callback(false, "Invalid device")
return
callback(false, "Invalid device");
return;
}
const cardName = getCardName(device)
codecSwitchProcess.cardName = cardName
codecSwitchProcess.profile = profileName
codecSwitchProcess.callback = callback
codecSwitchProcess.running = true
const cardName = getCardName(device);
codecSwitchProcess.cardName = cardName;
codecSwitchProcess.profile = profileName;
codecSwitchProcess.callback = callback;
codecSwitchProcess.running = true;
}
Process {
@@ -349,67 +359,67 @@ Singleton {
onExited: (exitCode, exitStatus) => {
if (exitCode === 0 && detectedCodec) {
if (deviceAddress) {
root.updateDeviceCodec(deviceAddress, detectedCodec)
root.updateDeviceCodec(deviceAddress, detectedCodec);
}
if (callback) {
callback(detectedCodec)
callback(detectedCodec);
}
} else if (callback) {
callback("")
callback("");
}
parsingTargetCard = false
detectedCodec = ""
availableCodecs = []
deviceAddress = ""
callback = null
parsingTargetCard = false;
detectedCodec = "";
availableCodecs = [];
deviceAddress = "";
callback = null;
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
let line = data.trim()
let line = data.trim();
if (line.includes(`Name: ${codecQueryProcess.cardName}`)) {
codecQueryProcess.parsingTargetCard = true
return
codecQueryProcess.parsingTargetCard = true;
return;
}
if (codecQueryProcess.parsingTargetCard && line.startsWith("Name: ") && !line.includes(codecQueryProcess.cardName)) {
codecQueryProcess.parsingTargetCard = false
return
codecQueryProcess.parsingTargetCard = false;
return;
}
if (codecQueryProcess.parsingTargetCard) {
if (line.startsWith("Active Profile:")) {
let profile = line.split(": ")[1] || ""
let profile = line.split(": ")[1] || "";
let activeCodec = codecQueryProcess.availableCodecs.find(c => {
return c.profile === profile
})
return c.profile === profile;
});
if (activeCodec) {
codecQueryProcess.detectedCodec = activeCodec.name
codecQueryProcess.detectedCodec = activeCodec.name;
}
return
return;
}
if (line.includes("codec") && line.includes("available: yes")) {
let parts = line.split(": ")
let parts = line.split(": ");
if (parts.length >= 2) {
let profile = parts[0].trim()
let description = parts[1]
let codecMatch = description.match(/codec ([^\)\s]+)/i)
let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN"
let codecInfo = root.getCodecInfo(codecName)
let profile = parts[0].trim();
let description = parts[1];
let codecMatch = description.match(/codec ([^\)\s]+)/i);
let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN";
let codecInfo = root.getCodecInfo(codecName);
if (codecInfo && !codecQueryProcess.availableCodecs.some(c => {
return c.profile === profile
})) {
let newCodecs = codecQueryProcess.availableCodecs.slice()
return c.profile === profile;
})) {
let newCodecs = codecQueryProcess.availableCodecs.slice();
newCodecs.push({
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
})
codecQueryProcess.availableCodecs = newCodecs
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
});
codecQueryProcess.availableCodecs = newCodecs;
}
}
}
@@ -431,59 +441,59 @@ Singleton {
onExited: function (exitCode, exitStatus) {
if (callback) {
callback(exitCode === 0 ? availableCodecs : [], exitCode === 0 ? detectedCodec : "")
callback(exitCode === 0 ? availableCodecs : [], exitCode === 0 ? detectedCodec : "");
}
parsingTargetCard = false
detectedCodec = ""
availableCodecs = []
callback = null
parsingTargetCard = false;
detectedCodec = "";
availableCodecs = [];
callback = null;
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
let line = data.trim()
let line = data.trim();
if (line.includes(`Name: ${codecFullQueryProcess.cardName}`)) {
codecFullQueryProcess.parsingTargetCard = true
return
codecFullQueryProcess.parsingTargetCard = true;
return;
}
if (codecFullQueryProcess.parsingTargetCard && line.startsWith("Name: ") && !line.includes(codecFullQueryProcess.cardName)) {
codecFullQueryProcess.parsingTargetCard = false
return
codecFullQueryProcess.parsingTargetCard = false;
return;
}
if (codecFullQueryProcess.parsingTargetCard) {
if (line.startsWith("Active Profile:")) {
let profile = line.split(": ")[1] || ""
let profile = line.split(": ")[1] || "";
let activeCodec = codecFullQueryProcess.availableCodecs.find(c => {
return c.profile === profile
})
return c.profile === profile;
});
if (activeCodec) {
codecFullQueryProcess.detectedCodec = activeCodec.name
codecFullQueryProcess.detectedCodec = activeCodec.name;
}
return
return;
}
if (line.includes("codec") && line.includes("available: yes")) {
let parts = line.split(": ")
let parts = line.split(": ");
if (parts.length >= 2) {
let profile = parts[0].trim()
let description = parts[1]
let codecMatch = description.match(/codec ([^\)\s]+)/i)
let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN"
let codecInfo = root.getCodecInfo(codecName)
let profile = parts[0].trim();
let description = parts[1];
let codecMatch = description.match(/codec ([^\)\s]+)/i);
let codecName = codecMatch ? codecMatch[1].toUpperCase() : "UNKNOWN";
let codecInfo = root.getCodecInfo(codecName);
if (codecInfo && !codecFullQueryProcess.availableCodecs.some(c => {
return c.profile === profile
})) {
let newCodecs = codecFullQueryProcess.availableCodecs.slice()
return c.profile === profile;
})) {
let newCodecs = codecFullQueryProcess.availableCodecs.slice();
newCodecs.push({
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
})
codecFullQueryProcess.availableCodecs = newCodecs
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
});
codecFullQueryProcess.availableCodecs = newCodecs;
}
}
}
@@ -503,21 +513,21 @@ Singleton {
onExited: function (exitCode, exitStatus) {
if (callback) {
callback(exitCode === 0, exitCode === 0 ? "Codec switched successfully" : "Failed to switch codec")
callback(exitCode === 0, exitCode === 0 ? "Codec switched successfully" : "Failed to switch codec");
}
// If successful, refresh the codec for this device
if (exitCode === 0) {
if (root.adapter && root.adapter.devices) {
root.adapter.devices.values.forEach(device => {
if (device && root.getCardName(device) === cardName) {
Qt.callLater(() => root.refreshDeviceCodec(device))
}
})
if (device && root.getCardName(device) === cardName) {
Qt.callLater(() => root.refreshDeviceCodec(device));
}
});
}
}
callback = null
callback = null;
}
}
}

View File

@@ -6,9 +6,11 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland // ! Import is needed despite what qmlls says
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("BlurService")
property bool quickshellSupported: false
property bool compositorSupported: false
@@ -52,7 +54,7 @@ Singleton {
targetWindow.BackgroundEffect.blurRegion = region;
return region;
} catch (e) {
console.warn("BlurService: Failed to create blur region:", e);
log.warn("Failed to create blur region:", e);
return null;
}
}
@@ -84,15 +86,15 @@ Singleton {
onStreamFinished: {
root.compositorSupported = text.trim() === "supported";
if (root.compositorSupported)
console.info("BlurService: Compositor supports ext-background-effect-v1");
log.info("Compositor supports ext-background-effect-v1");
else
console.info("BlurService: Compositor does not support ext-background-effect-v1");
log.info("Compositor does not support ext-background-effect-v1");
}
}
onExited: exitCode => {
if (exitCode !== 0)
console.warn("BlurService: blur probe failed with code:", exitCode);
log.warn("blur probe failed with code:", exitCode);
}
}
@@ -104,10 +106,10 @@ Singleton {
`, root, "BlurAvailabilityTest");
test.destroy();
quickshellSupported = true;
console.info("BlurService: Quickshell blur support available");
log.info("Quickshell blur support available");
blurProbe.running = true;
} catch (e) {
console.info("BlurService: BackgroundEffect not available - blur disabled. Requires a newer version of Quickshell.");
log.info("BackgroundEffect not available - blur disabled. Requires a newer version of Quickshell.");
}
}
}

View File

@@ -19,68 +19,69 @@ Singleton {
function checkKhalAvailability() {
if (!khalCheckProcess.running)
khalCheckProcess.running = true
khalCheckProcess.running = true;
}
function detectKhalDateFormat() {
if (!khalFormatProcess.running)
khalFormatProcess.running = true
khalFormatProcess.running = true;
}
function parseKhalDateFormat(formatExample) {
let qtFormat = formatExample.replace("12", "MM").replace("21", "dd").replace("2013", "yyyy")
return { format: qtFormat, parser: null }
let qtFormat = formatExample.replace("12", "MM").replace("21", "dd").replace("2013", "yyyy");
return {
format: qtFormat,
parser: null
};
}
function loadCurrentMonth() {
if (!root.khalAvailable)
return
let today = new Date()
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0)
return;
let today = new Date();
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
// Add padding
let startDate = new Date(firstDay)
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7)
let endDate = new Date(lastDay)
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7)
loadEvents(startDate, endDate)
let startDate = new Date(firstDay);
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7);
let endDate = new Date(lastDay);
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7);
loadEvents(startDate, endDate);
}
function loadEvents(startDate, endDate) {
if (!root.khalAvailable) {
return
return;
}
if (eventsProcess.running) {
return
return;
}
// Store last requested date range for refresh timer
root.lastStartDate = startDate
root.lastEndDate = endDate
root.isLoading = true
root.lastStartDate = startDate;
root.lastEndDate = endDate;
root.isLoading = true;
// Format dates for khal using detected format
let startDateStr = Qt.formatDate(startDate, root.khalDateFormat)
let endDateStr = Qt.formatDate(endDate, root.khalDateFormat)
eventsProcess.requestStartDate = startDate
eventsProcess.requestEndDate = endDate
eventsProcess.command = ["khal", "list", "--json", "title", "--json", "description", "--json", "start-date", "--json", "start-time", "--json", "end-date", "--json", "end-time", "--json", "all-day", "--json", "location", "--json", "url", startDateStr, endDateStr]
eventsProcess.running = true
let startDateStr = Qt.formatDate(startDate, root.khalDateFormat);
let endDateStr = Qt.formatDate(endDate, root.khalDateFormat);
eventsProcess.requestStartDate = startDate;
eventsProcess.requestEndDate = endDate;
eventsProcess.command = ["khal", "list", "--json", "title", "--json", "description", "--json", "start-date", "--json", "start-time", "--json", "end-date", "--json", "end-time", "--json", "all-day", "--json", "location", "--json", "url", startDateStr, endDateStr];
eventsProcess.running = true;
}
function getEventsForDate(date) {
let dateKey = Qt.formatDate(date, "yyyy-MM-dd")
return root.eventsByDate[dateKey] || []
let dateKey = Qt.formatDate(date, "yyyy-MM-dd");
return root.eventsByDate[dateKey] || [];
}
function hasEventsForDate(date) {
let events = getEventsForDate(date)
return events.length > 0
let events = getEventsForDate(date);
return events.length > 0;
}
// Initialize on component completion
Component.onCompleted: {
detectKhalDateFormat()
detectKhalDateFormat();
}
// Process for detecting khal date format
@@ -91,22 +92,22 @@ Singleton {
running: false
onExited: exitCode => {
if (exitCode !== 0) {
checkKhalAvailability()
checkKhalAvailability();
}
}
stdout: StdioCollector {
onStreamFinished: {
let lines = text.split('\n')
let lines = text.split('\n');
for (let line of lines) {
if (line.startsWith('dateformat:')) {
let formatExample = line.substring(line.indexOf(':') + 1).trim()
let formatInfo = parseKhalDateFormat(formatExample)
root.khalDateFormat = formatInfo.format
break
let formatExample = line.substring(line.indexOf(':') + 1).trim();
let formatInfo = parseKhalDateFormat(formatExample);
root.khalDateFormat = formatInfo.format;
break;
}
}
checkKhalAvailability()
checkKhalAvailability();
}
}
}
@@ -118,9 +119,9 @@ Singleton {
command: ["khal", "list", "today"]
running: false
onExited: exitCode => {
root.khalAvailable = (exitCode === 0)
root.khalAvailable = (exitCode === 0);
if (exitCode === 0) {
loadCurrentMonth()
loadCurrentMonth();
}
}
}
@@ -135,100 +136,96 @@ Singleton {
running: false
onExited: exitCode => {
root.isLoading = false
root.isLoading = false;
if (exitCode !== 0) {
root.lastError = "Failed to load events (exit code: " + exitCode + ")"
return
root.lastError = "Failed to load events (exit code: " + exitCode + ")";
return;
}
try {
let newEventsByDate = {}
let lines = eventsProcess.rawOutput.split('\n')
let newEventsByDate = {};
let lines = eventsProcess.rawOutput.split('\n');
for (let line of lines) {
line = line.trim()
line = line.trim();
if (!line || line === "[]")
continue
continue;
// Parse JSON line
let dayEvents = JSON.parse(line)
let dayEvents = JSON.parse(line);
// Process each event in this day's array
for (let event of dayEvents) {
if (!event.title)
continue
continue;
// Parse start and end dates using detected format
let startDate, endDate
let startDate, endDate;
if (event['start-date']) {
startDate = Date.fromLocaleString(I18n.locale(), event['start-date'], root.khalDateFormat)
startDate = Date.fromLocaleString(I18n.locale(), event['start-date'], root.khalDateFormat);
} else {
startDate = new Date()
startDate = new Date();
}
if (event['end-date']) {
endDate = Date.fromLocaleString(I18n.locale(), event['end-date'], root.khalDateFormat)
endDate = Date.fromLocaleString(I18n.locale(), event['end-date'], root.khalDateFormat);
} else {
endDate = new Date(startDate)
endDate = new Date(startDate);
}
// Create start/end times
let startTime = new Date(startDate)
let endTime = new Date(endDate)
if (event['start-time']
&& event['all-day'] !== "True") {
let startTime = new Date(startDate);
let endTime = new Date(endDate);
if (event['start-time'] && event['all-day'] !== "True") {
// Parse time if available and not all-day
let timeStr = event['start-time']
let timeStr = event['start-time'];
if (timeStr) {
// Match time with optional seconds and AM/PM
let timeParts = timeStr.match(/(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i)
let timeParts = timeStr.match(/(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i);
if (timeParts) {
let hours = parseInt(timeParts[1])
let minutes = parseInt(timeParts[2])
let hours = parseInt(timeParts[1]);
let minutes = parseInt(timeParts[2]);
// Handle AM/PM conversion if present
if (timeParts[3]) {
let period = timeParts[3].toUpperCase()
let period = timeParts[3].toUpperCase();
if (period === 'PM' && hours !== 12) {
hours += 12
hours += 12;
} else if (period === 'AM' && hours === 12) {
hours = 0
hours = 0;
}
}
startTime.setHours(hours, minutes)
startTime.setHours(hours, minutes);
if (event['end-time']) {
let endTimeParts = event['end-time'].match(
/(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i)
let endTimeParts = event['end-time'].match(/(\d+):(\d+)(?::\d+)?\s*(AM|PM)?/i);
if (endTimeParts) {
let endHours = parseInt(endTimeParts[1])
let endMinutes = parseInt(endTimeParts[2])
let endHours = parseInt(endTimeParts[1]);
let endMinutes = parseInt(endTimeParts[2]);
// Handle AM/PM conversion if present
if (endTimeParts[3]) {
let endPeriod = endTimeParts[3].toUpperCase()
let endPeriod = endTimeParts[3].toUpperCase();
if (endPeriod === 'PM' && endHours !== 12) {
endHours += 12
endHours += 12;
} else if (endPeriod === 'AM' && endHours === 12) {
endHours = 0
endHours = 0;
}
}
endTime.setHours(endHours, endMinutes)
endTime.setHours(endHours, endMinutes);
}
} else {
// Default to 1 hour duration on same day
endTime = new Date(startTime)
endTime.setHours(
startTime.getHours() + 1)
endTime = new Date(startTime);
endTime.setHours(startTime.getHours() + 1);
}
}
}
}
// Create unique ID for this event (to track multi-day events)
let eventId = event.title + "_" + event['start-date']
+ "_" + (event['start-time'] || 'allday')
let eventId = event.title + "_" + event['start-date'] + "_" + (event['start-time'] || 'allday');
// Create event object template
let extractedUrl = ""
let extractedUrl = "";
if (!event.url && event.description) {
let urlMatch = event.description.match(/https?:\/\/[^\s]+/)
let urlMatch = event.description.match(/https?:\/\/[^\s]+/);
if (urlMatch) {
extractedUrl = urlMatch[0]
extractedUrl = urlMatch[0];
}
}
let eventTemplate = {
@@ -242,75 +239,71 @@ Singleton {
"calendar": "",
"color": "",
"allDay": event['all-day'] === "True",
"isMultiDay": startDate.toDateString(
) !== endDate.toDateString()
}
"isMultiDay": startDate.toDateString() !== endDate.toDateString()
};
// Add event to each day it spans
let currentDate = new Date(startDate)
let currentDate = new Date(startDate);
while (currentDate <= endDate) {
let dateKey = Qt.formatDate(currentDate,
"yyyy-MM-dd")
let dateKey = Qt.formatDate(currentDate, "yyyy-MM-dd");
if (!newEventsByDate[dateKey])
newEventsByDate[dateKey] = []
newEventsByDate[dateKey] = [];
// Check if this exact event is already added to this date (prevent duplicates)
let existingEvent = newEventsByDate[dateKey].find(
e => {
return e.id === eventId
})
let existingEvent = newEventsByDate[dateKey].find(e => {
return e.id === eventId;
});
if (existingEvent) {
// Move to next day without adding duplicate
currentDate.setDate(currentDate.getDate() + 1)
continue
currentDate.setDate(currentDate.getDate() + 1);
continue;
}
// Create a copy of the event for this date
let dayEvent = Object.assign({}, eventTemplate)
let dayEvent = Object.assign({}, eventTemplate);
// For multi-day events, adjust the display time for this specific day
if (currentDate.getTime() === startDate.getTime()) {
// First day - use original start time
dayEvent.start = new Date(startTime)
dayEvent.start = new Date(startTime);
} else {
// Subsequent days - start at beginning of day for all-day events
dayEvent.start = new Date(currentDate)
dayEvent.start = new Date(currentDate);
if (!dayEvent.allDay)
dayEvent.start.setHours(0, 0, 0, 0)
dayEvent.start.setHours(0, 0, 0, 0);
}
if (currentDate.getTime() === endDate.getTime()) {
// Last day - use original end time
dayEvent.end = new Date(endTime)
dayEvent.end = new Date(endTime);
} else {
// Earlier days - end at end of day for all-day events
dayEvent.end = new Date(currentDate)
dayEvent.end = new Date(currentDate);
if (!dayEvent.allDay)
dayEvent.end.setHours(23, 59, 59, 999)
dayEvent.end.setHours(23, 59, 59, 999);
}
newEventsByDate[dateKey].push(dayEvent)
newEventsByDate[dateKey].push(dayEvent);
// Move to next day
currentDate.setDate(currentDate.getDate() + 1)
currentDate.setDate(currentDate.getDate() + 1);
}
}
}
// Sort events by start time within each date
for (let dateKey in newEventsByDate) {
newEventsByDate[dateKey].sort((a, b) => {
return a.start.getTime(
) - b.start.getTime()
})
return a.start.getTime() - b.start.getTime();
});
}
root.eventsByDate = newEventsByDate
root.lastError = ""
root.eventsByDate = newEventsByDate;
root.lastError = "";
} catch (error) {
root.lastError = "Failed to parse events JSON: " + error.toString()
root.eventsByDate = {}
root.lastError = "Failed to parse events JSON: " + error.toString();
root.eventsByDate = {};
}
// Reset for next run
eventsProcess.rawOutput = ""
eventsProcess.rawOutput = "";
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
eventsProcess.rawOutput += data + "\n"
eventsProcess.rawOutput += data + "\n";
}
}
}

View File

@@ -6,9 +6,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("ChangelogService")
readonly property string currentVersion: "1.4"
readonly property bool changelogEnabled: false
@@ -101,7 +103,7 @@ Singleton {
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("ChangelogService: Failed to create changelog marker");
log.warn("Failed to create changelog marker");
}
}
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("ClipboardService")
readonly property int longTextThreshold: 200
@@ -78,7 +80,7 @@ Singleton {
}
DMSService.sendRequest("clipboard.getHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardService: Failed to get history:", response.error);
log.warn("Failed to get history:", response.error);
return;
}
internalEntries = response.result || [];
@@ -144,7 +146,7 @@ Singleton {
"id": entry.id
}, function (response) {
if (response.error) {
console.warn("ClipboardService: Failed to delete entry:", response.error);
log.warn("Failed to delete entry:", response.error);
return;
}
internalEntries = internalEntries.filter(e => e.id !== entry.id);
@@ -169,7 +171,7 @@ Singleton {
"id": entry.id
}, function (response) {
if (response.error) {
console.warn("ClipboardService: Failed to delete entry:", response.error);
log.warn("Failed to delete entry:", response.error);
return;
}
internalEntries = internalEntries.filter(e => e.id !== entry.id);
@@ -223,7 +225,7 @@ Singleton {
const savedCount = pinnedCount;
DMSService.sendRequest("clipboard.clearHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardService: Failed to clear history:", response.error);
log.warn("Failed to clear history:", response.error);
return;
}
refresh();

View File

@@ -7,9 +7,11 @@ import Quickshell.I3
import Quickshell.Wayland
import Quickshell.Hyprland
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("CompositorService")
property bool isHyprland: false
property bool isNiri: false
@@ -52,7 +54,7 @@ Singleton {
randrScales = scales;
}
} catch (e) {
console.warn("CompositorService: failed to parse randr data:", e);
log.warn("failed to parse randr data:", e);
}
}
randrReady = true;
@@ -379,9 +381,7 @@ Singleton {
const focusedWin = NiriService.windows.find(nw => nw.is_focused);
if (!focusedWin)
return [];
const screenWsIds = new Set(
NiriService.allWorkspaces.filter(ws => ws.output === screenName).map(ws => ws.id)
);
const screenWsIds = new Set(NiriService.allWorkspaces.filter(ws => ws.output === screenName).map(ws => ws.id));
return screenWsIds.has(focusedWin.workspace_id) ? toplevels : [];
}
return NiriService.filterCurrentDisplay(toplevels, screenName);
@@ -454,7 +454,7 @@ Singleton {
}
}
} catch (e) {
console.warn("CompositorService: workspace snapshot failed:", e);
log.warn("workspace snapshot failed:", e);
}
if (currentWorkspaceId === null)
@@ -498,7 +498,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "hyprland";
console.info("CompositorService: Detected Hyprland");
log.info("Detected Hyprland");
return;
}
@@ -513,7 +513,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "niri";
console.info("CompositorService: Detected Niri with socket:", niriSocket);
log.info("Detected Niri with socket:", niriSocket);
NiriService.generateNiriBlurrule();
}
}, 0);
@@ -531,7 +531,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "sway";
console.info("CompositorService: Detected Sway with socket:", swaySocket);
log.info("Detected Sway with socket:", swaySocket);
}
}, 0);
return;
@@ -548,7 +548,7 @@ Singleton {
isMiracle = true;
isLabwc = false;
compositor = "miracle";
console.info("CompositorService: Detected Miracle WM with socket:", miracleSocket);
log.info("Detected Miracle WM with socket:", miracleSocket);
}
}, 0);
return;
@@ -565,7 +565,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "scroll";
console.info("CompositorService: Detected Scroll with socket:", scrollSocket);
log.info("Detected Scroll with socket:", scrollSocket);
}
}, 0);
return;
@@ -580,7 +580,7 @@ Singleton {
isMiracle = false;
isLabwc = true;
compositor = "labwc";
console.info("CompositorService: Detected LabWC with PID:", labwcPid);
log.info("Detected LabWC with PID:", labwcPid);
return;
}
@@ -595,7 +595,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "unknown";
console.warn("CompositorService: No compositor detected");
log.warn("No compositor detected");
}
}
@@ -618,7 +618,7 @@ Singleton {
isMiracle = false;
isLabwc = false;
compositor = "dwl";
console.info("CompositorService: Detected DWL via DMS capability");
log.info("Detected DWL via DMS capability");
}
}
@@ -638,7 +638,7 @@ Singleton {
if (isLabwc) {
Quickshell.execDetached(["dms", "dpms", "off"]);
}
console.warn("CompositorService: Cannot power off monitors, unknown compositor");
log.warn("Cannot power off monitors, unknown compositor");
}
function powerOnMonitors() {
@@ -657,12 +657,12 @@ Singleton {
if (isLabwc) {
Quickshell.execDetached(["dms", "dpms", "on"]);
}
console.warn("CompositorService: Cannot power on monitors, unknown compositor");
log.warn("Cannot power on monitors, unknown compositor");
}
function _dwlPowerOffMonitors() {
if (!Quickshell.screens || Quickshell.screens.length === 0) {
console.warn("CompositorService: No screens available for DWL power off");
log.warn("No screens available for DWL power off");
return;
}
@@ -676,7 +676,7 @@ Singleton {
function _dwlPowerOnMonitors() {
if (!Quickshell.screens || Quickshell.screens.length === 0) {
console.warn("CompositorService: No screens available for DWL power on");
log.warn("No screens available for DWL power on");
return;
}

View File

@@ -4,9 +4,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("CupsService")
property int refCount: 0
@@ -205,7 +207,7 @@ Singleton {
enabled: DMSService.isConnected
function onCupsStateUpdate(data) {
console.log("CupsService: Subscription update received");
log.debug("Subscription update received");
getState();
}

View File

@@ -4,9 +4,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DMSNetworkService")
property bool networkAvailable: false
property string backend: ""
@@ -141,7 +143,7 @@ Singleton {
function onNetworkStateUpdate(data) {
const networksCount = data.wifiNetworks?.length ?? "null";
console.log("DMSNetworkService: Subscription update received, networks:", networksCount);
log.debug("Subscription update received, networks:", networksCount);
updateState(data);
}
}
@@ -301,7 +303,7 @@ Singleton {
const timeout = 30000;
if (busyDuration > timeout) {
console.warn("DMSNetworkService: VPN operation timed out after", timeout, "ms");
log.warn("VPN operation timed out after", timeout, "ms");
vpnIsBusy = false;
pendingVpnUuid = "";
vpnBusyStartTime = 0;
@@ -331,7 +333,7 @@ Singleton {
if (pendingConnectionSSID) {
if (wifiConnected && currentWifiSSID === pendingConnectionSSID && wifiIP) {
const elapsed = Date.now() - pendingConnectionStartTime;
console.info("DMSNetworkService: Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms");
log.info("Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms");
ToastService.showInfo(`Connected to ${pendingConnectionSSID}`);
if (userPreference === "wifi" || userPreference === "auto") {
@@ -402,7 +404,7 @@ Singleton {
DMSService.sendRequest("network.wifi.scan", params, response => {
isScanning = false;
if (response.error) {
console.warn("DMSNetworkService: WiFi scan failed:", response.error);
log.warn("WiFi scan failed:", response.error);
} else {
Qt.callLater(() => getState());
}
@@ -485,10 +487,10 @@ Singleton {
}
function submitCredentials(token, secrets, save) {
console.log("submitCredentials: networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion);
log.debug("submitCredentials: networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion);
if (!networkAvailable || DMSService.apiVersion < 7) {
console.warn("submitCredentials: Aborting - networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion);
log.warn("submitCredentials: Aborting - networkAvailable=" + networkAvailable + " apiVersion=" + DMSService.apiVersion);
return;
}
@@ -502,7 +504,7 @@ Singleton {
DMSService.sendRequest("network.credentials.submit", params, response => {
if (response.error) {
console.warn("DMSNetworkService: Failed to submit credentials:", response.error);
log.warn("Failed to submit credentials:", response.error);
}
});
}
@@ -520,7 +522,7 @@ Singleton {
DMSService.sendRequest("network.credentials.cancel", params, response => {
if (response.error) {
console.warn("DMSNetworkService: Failed to cancel credentials:", response.error);
log.warn("Failed to cancel credentials:", response.error);
}
});
}
@@ -533,7 +535,7 @@ Singleton {
ssid: ssid
}, response => {
if (response.error) {
console.warn("Failed to forget network:", response.error);
log.warn("Failed to forget network:", response.error);
} else {
ToastService.showInfo(I18n.tr("Forgot network %1").arg(ssid));
@@ -565,7 +567,7 @@ Singleton {
wifiToggling = false;
if (response.error) {
console.warn("Failed to toggle WiFi:", response.error);
log.warn("Failed to toggle WiFi:", response.error);
} else if (response.result) {
wifiEnabled = response.result.enabled;
ToastService.showInfo(wifiEnabled ? I18n.tr("WiFi enabled") : I18n.tr("WiFi disabled"));
@@ -600,7 +602,7 @@ Singleton {
targetPreference = "";
if (response.error) {
console.warn("Failed to set network preference:", response.error);
log.warn("Failed to set network preference:", response.error);
}
});
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DMSService")
property bool dmsAvailable: false
property var capabilities: []
@@ -198,14 +200,14 @@ Singleton {
try {
response = JSON.parse(line);
} catch (e) {
console.warn("DMSService: Failed to parse request response:", line.substring(0, 100));
log.warn("Failed to parse request response:", line.substring(0, 100));
return;
}
const isClipboard = clipboardRequestIds[response.id];
if (isClipboard)
delete clipboardRequestIds[response.id];
else
console.log("DMSService: Request socket <<", line);
log.debug("Request socket <<", line);
handleResponse(response);
}
}
@@ -232,11 +234,11 @@ Singleton {
try {
response = JSON.parse(line);
} catch (e) {
console.warn("DMSService: Failed to parse subscription event:", line.substring(0, 100));
log.warn("Failed to parse subscription event:", line.substring(0, 100));
return;
}
if (!line.includes("clipboard"))
console.log("DMSService: Subscribe socket <<", line);
log.debug("Subscribe socket <<", line);
handleSubscriptionEvent(response);
}
}
@@ -251,9 +253,9 @@ Singleton {
request.params = {
"services": activeSubscriptions
};
console.log("DMSService: Subscribing to services:", JSON.stringify(activeSubscriptions));
log.debug("Subscribing to services:", JSON.stringify(activeSubscriptions));
} else {
console.log("DMSService: Subscribing to all services");
log.debug("Subscribing to all services");
}
subscribeSocket.send(request);
@@ -291,7 +293,7 @@ Singleton {
} else {
const filtered = activeSubscriptions.filter(s => s !== service);
if (filtered.length === 0) {
console.warn("DMSService: Cannot remove last subscription");
log.warn("Cannot remove last subscription");
return;
}
subscribe(filtered);
@@ -316,7 +318,7 @@ Singleton {
if (response.error) {
if (response.error.includes("unknown method") && response.error.includes("subscribe")) {
if (!shownOutdatedError) {
console.error("DMSService: Server does not support subscribe method");
log.error("Server does not support subscribe method");
ToastService.showError(I18n.tr("DMS out of date"), I18n.tr("To update, run the following command:"), updateCommand);
shownOutdatedError = true;
}
@@ -336,7 +338,7 @@ Singleton {
cliVersion = data.cliVersion || "";
capabilities = data.capabilities || [];
console.info("DMSService: Connected (API v" + apiVersion + ", CLI " + cliVersion + ") -", JSON.stringify(capabilities));
log.info("Connected (API v" + apiVersion + ", CLI " + cliVersion + ") -", JSON.stringify(capabilities));
if (apiVersion < expectedApiVersion) {
ToastService.showError("DMS server is outdated (API v" + apiVersion + ", expected v" + expectedApiVersion + ")");
@@ -401,7 +403,7 @@ Singleton {
function sendRequest(method, params, callback) {
if (!isConnected) {
console.warn("DMSService.sendRequest: Not connected, method:", method);
log.warn("DMSService.sendRequest: Not connected, method:", method);
if (callback) {
callback({
"error": "not connected to DMS socket"
@@ -427,7 +429,7 @@ Singleton {
if (method.startsWith("clipboard")) {
clipboardRequestIds[id] = true;
} else {
console.log("DMSService.sendRequest: Sending request id=" + id + " method=" + method);
log.debug("DMSService.sendRequest: Sending request id=" + id + " method=" + method);
}
requestSocket.send(request);
}

View File

@@ -1,10 +1,8 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
@@ -18,75 +16,75 @@ Singleton {
if (_cache[moddedAppId] !== undefined)
return _cache[moddedAppId];
const result = (function() {
// 1. Try heuristic lookup (standard)
const entry = DesktopEntries.heuristicLookup(moddedAppId);
let icon = Quickshell.iconPath(entry?.icon, true);
if (icon && icon !== "")
return icon;
// 2. Try the appId itself as an icon name
icon = Quickshell.iconPath(moddedAppId, true);
if (icon && icon !== "")
return icon;
// 3. Try variations of the appId (lowercase, last part)
const appIds = [moddedAppId.toLowerCase()];
const lastPart = moddedAppId.split('.').pop();
if (lastPart && lastPart !== moddedAppId) {
appIds.push(lastPart);
appIds.push(lastPart.toLowerCase());
}
for (const id of appIds) {
icon = Quickshell.iconPath(id, true);
if (icon && icon !== "")
return icon;
}
// 4. Deep search in all desktop entries (if the above fail)
// This is slow-ish but only happens once for failed icons
const strippedId = moddedAppId.replace(/-bin$/, "").toLowerCase();
const allEntries = DesktopEntries.applications.values;
for (let i = 0; i < allEntries.length; i++) {
const e = allEntries[i];
const eId = (e.id || "").toLowerCase();
const eName = (e.name || "").toLowerCase();
const eExec = (e.execString || "").toLowerCase();
if (eId.includes(strippedId) || eName.includes(strippedId) || eExec.includes(strippedId)) {
icon = Quickshell.iconPath(e.icon, true);
if (icon && icon !== "")
return icon;
}
}
// 5. Nix/Guix specific store check (as a last resort)
for (const appId of appIds) {
let execPath = entry?.execString?.replace(/\/bin.*/, "");
if (!execPath)
continue;
if (execPath.startsWith("/nix/store/") || execPath.startsWith("/gnu/store/")) {
const basePath = execPath;
const sizes = ["256x256", "128x128", "64x64", "48x48", "32x32", "24x24", "16x16"];
let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg`;
icon = Quickshell.iconPath(iconPath, true);
const result = (function () {
// 1. Try heuristic lookup (standard)
const entry = DesktopEntries.heuristicLookup(moddedAppId);
let icon = Quickshell.iconPath(entry?.icon, true);
if (icon && icon !== "")
return icon;
for (const size of sizes) {
iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png`;
icon = Quickshell.iconPath(iconPath, true);
// 2. Try the appId itself as an icon name
icon = Quickshell.iconPath(moddedAppId, true);
if (icon && icon !== "")
return icon;
// 3. Try variations of the appId (lowercase, last part)
const appIds = [moddedAppId.toLowerCase()];
const lastPart = moddedAppId.split('.').pop();
if (lastPart && lastPart !== moddedAppId) {
appIds.push(lastPart);
appIds.push(lastPart.toLowerCase());
}
for (const id of appIds) {
icon = Quickshell.iconPath(id, true);
if (icon && icon !== "")
return icon;
}
}
}
return "";
})();
// 4. Deep search in all desktop entries (if the above fail)
// This is slow-ish but only happens once for failed icons
const strippedId = moddedAppId.replace(/-bin$/, "").toLowerCase();
const allEntries = DesktopEntries.applications.values;
for (let i = 0; i < allEntries.length; i++) {
const e = allEntries[i];
const eId = (e.id || "").toLowerCase();
const eName = (e.name || "").toLowerCase();
const eExec = (e.execString || "").toLowerCase();
if (eId.includes(strippedId) || eName.includes(strippedId) || eExec.includes(strippedId)) {
icon = Quickshell.iconPath(e.icon, true);
if (icon && icon !== "")
return icon;
}
}
// 5. Nix/Guix specific store check (as a last resort)
for (const appId of appIds) {
let execPath = entry?.execString?.replace(/\/bin.*/, "");
if (!execPath)
continue;
if (execPath.startsWith("/nix/store/") || execPath.startsWith("/gnu/store/")) {
const basePath = execPath;
const sizes = ["256x256", "128x128", "64x64", "48x48", "32x32", "24x24", "16x16"];
let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg`;
icon = Quickshell.iconPath(iconPath, true);
if (icon && icon !== "")
return icon;
for (const size of sizes) {
iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png`;
icon = Quickshell.iconPath(iconPath, true);
if (icon && icon !== "")
return icon;
}
}
}
return "";
})();
_cache[moddedAppId] = result;
return result;

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DgopService")
property int refCount: 0
property int updateInterval: refCount > 0 ? 3000 : 30000
@@ -643,7 +645,7 @@ Singleton {
onStarted: dgopProcessPid = processId ?? 0
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("Dgop process failed with exit code:", exitCode);
log.warn("Dgop process failed with exit code:", exitCode);
isUpdating = false;
}
}
@@ -654,8 +656,8 @@ Singleton {
const data = JSON.parse(text.trim());
parseData(data);
} catch (e) {
console.warn("Failed to parse dgop JSON:", e);
console.warn("Raw text was:", text.substring(0, 200));
log.warn("Failed to parse dgop JSON:", e);
log.warn("Raw text was:", text.substring(0, 200));
isUpdating = false;
}
}
@@ -669,7 +671,7 @@ Singleton {
running: false
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("GPU init process failed with exit code:", exitCode);
log.warn("GPU init process failed with exit code:", exitCode);
}
}
stdout: StdioCollector {
@@ -679,7 +681,7 @@ Singleton {
const data = JSON.parse(text.trim());
parseData(data);
} catch (e) {
console.warn("Failed to parse GPU init JSON:", e);
log.warn("Failed to parse GPU init JSON:", e);
}
}
}
@@ -692,7 +694,7 @@ Singleton {
running: false
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("System init process failed with exit code:", exitCode);
log.warn("System init process failed with exit code:", exitCode);
}
}
stdout: StdioCollector {
@@ -702,7 +704,7 @@ Singleton {
const data = JSON.parse(text.trim());
parseData(data);
} catch (e) {
console.warn("Failed to parse system init JSON:", e);
log.warn("Failed to parse system init JSON:", e);
}
}
}
@@ -727,7 +729,7 @@ Singleton {
}
}
} else {
console.warn("dgop is not installed or not in PATH");
log.warn("dgop is not installed or not in PATH");
}
}
}
@@ -738,7 +740,7 @@ Singleton {
running: false
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("Failed to read /etc/os-release");
log.warn("Failed to read /etc/os-release");
}
}
stdout: StdioCollector {
@@ -761,9 +763,9 @@ Singleton {
// Prefer PRETTY_NAME, fallback to NAME
const distroName = prettyName || name || "Linux";
distribution = distroName;
console.info("Detected distribution:", distroName);
log.info("Detected distribution:", distroName);
} catch (e) {
console.warn("Failed to parse /etc/os-release:", e);
log.warn("Failed to parse /etc/os-release:", e);
distribution = "Linux";
}
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DisplayService")
property bool brightnessAvailable: devices.length > 0
property var devices: []
@@ -247,7 +249,7 @@ Singleton {
function setBrightness(percentage, device, suppressOsd) {
const actualDevice = device === "" ? getDefaultDevice() : (device || currentDevice || getDefaultDevice());
if (!actualDevice) {
console.warn("DisplayService: No device selected for brightness change");
log.warn("No device selected for brightness change");
return;
}
@@ -273,14 +275,14 @@ Singleton {
}
if (maxValue <= 0) {
console.warn("DisplayService: Invalid max value for device", actualDevice, "- skipping brightness change");
log.warn("Invalid max value for device", actualDevice, "- skipping brightness change");
return;
}
const clampedValue = Math.max(minValue, Math.min(maxValue, percentage));
if (!DMSService.isConnected) {
console.warn("DisplayService: Not connected to DMS");
log.warn("Not connected to DMS");
return;
}
@@ -319,7 +321,7 @@ Singleton {
DMSService.sendRequest("brightness.setBrightness", params, response => {
if (response.error) {
console.error("DisplayService: Failed to set brightness:", response.error);
log.error("Failed to set brightness:", response.error);
ToastService.showError(I18n.tr("Failed to set brightness"), response.error, "", "brightness");
} else {
ToastService.dismissCategory("brightness");
@@ -453,7 +455,7 @@ Singleton {
"enabled": true
}, response => {
if (response.error) {
console.error("DisplayService: Failed to enable gamma control:", response.error);
log.error("Failed to enable gamma control:", response.error);
ToastService.showError(I18n.tr("Failed to enable night mode"), response.error, "", "night-mode");
nightModeEnabled = false;
SessionData.setNightModeEnabled(false);
@@ -481,7 +483,7 @@ Singleton {
"enabled": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable gamma control:", response.error);
log.error("Failed to disable gamma control:", response.error);
ToastService.showError(I18n.tr("Failed to disable night mode"), response.error, "", "night-mode");
} else {
ToastService.dismissCategory("night-mode");
@@ -505,7 +507,7 @@ Singleton {
"sunset": null
}, response => {
if (response.error) {
console.error("DisplayService: Failed to clear manual times:", response.error);
log.error("Failed to clear manual times:", response.error);
return;
}
@@ -513,7 +515,7 @@ Singleton {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error);
log.error("Failed to disable IP location:", response.error);
return;
}
@@ -522,7 +524,7 @@ Singleton {
"high": temperature
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error);
log.error("Failed to set temperature:", response.error);
ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
} else {
ToastService.dismissCategory("night-mode");
@@ -564,7 +566,7 @@ Singleton {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error);
log.error("Failed to disable IP location:", response.error);
return;
}
@@ -573,7 +575,7 @@ Singleton {
"high": highTemp
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error);
log.error("Failed to set temperature:", response.error);
ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
return;
}
@@ -583,7 +585,7 @@ Singleton {
"sunset": sunset
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set manual times:", response.error);
log.error("Failed to set manual times:", response.error);
ToastService.showError(I18n.tr("Failed to set night mode schedule"), response.error, "", "night-mode");
} else {
ToastService.dismissCategory("night-mode");
@@ -602,7 +604,7 @@ Singleton {
"sunset": null
}, response => {
if (response.error) {
console.error("DisplayService: Failed to clear manual times:", response.error);
log.error("Failed to clear manual times:", response.error);
return;
}
@@ -611,7 +613,7 @@ Singleton {
"high": highTemp
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error);
log.error("Failed to set temperature:", response.error);
ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
return;
}
@@ -621,7 +623,7 @@ Singleton {
"use": true
}, response => {
if (response.error) {
console.error("DisplayService: Failed to enable IP location:", response.error);
log.error("Failed to enable IP location:", response.error);
ToastService.showError(I18n.tr("Failed to enable IP location"), response.error, "", "night-mode");
} else {
ToastService.dismissCategory("night-mode");
@@ -632,7 +634,7 @@ Singleton {
"use": false
}, response => {
if (response.error) {
console.error("DisplayService: Failed to disable IP location:", response.error);
log.error("Failed to disable IP location:", response.error);
return;
}
@@ -641,7 +643,7 @@ Singleton {
"longitude": SessionData.longitude
}, response => {
if (response.error) {
console.error("DisplayService: Failed to set location:", response.error);
log.error("Failed to set location:", response.error);
ToastService.showError(I18n.tr("Failed to set night mode location"), response.error, "", "night-mode");
} else {
ToastService.dismissCategory("night-mode");
@@ -649,7 +651,7 @@ Singleton {
});
});
} else {
console.warn("DisplayService: Location mode selected but no coordinates set and IP location disabled");
log.warn("Location mode selected but no coordinates set and IP location disabled");
}
});
});
@@ -703,7 +705,7 @@ Singleton {
if (response.error) {
gammaControlAvailable = false;
automationAvailable = false;
console.error("DisplayService: Gamma control not available:", response.error);
log.error("Gamma control not available:", response.error);
} else {
gammaControlAvailable = true;
automationAvailable = true;
@@ -713,7 +715,7 @@ Singleton {
"enabled": true
}, enableResponse => {
if (enableResponse.error) {
console.error("DisplayService: Failed to enable gamma control on startup:", enableResponse.error);
log.error("Failed to enable gamma control on startup:", enableResponse.error);
return;
}
@@ -772,7 +774,7 @@ Singleton {
DMSService.sendRequest("brightness.rescan", null, response => {
if (response.error) {
console.error("DisplayService: Failed to rescan brightness devices:", response.error);
log.error("Failed to rescan brightness devices:", response.error);
}
});
}

View File

@@ -6,9 +6,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DwlService")
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
readonly property string mangoDmsDir: configDir + "/mango/dms"
@@ -91,7 +93,7 @@ Singleton {
const hasDwl = DMSService.capabilities.includes("dwl");
if (hasDwl && !dwlAvailable) {
dwlAvailable = true;
console.info("DwlService: DWL capability detected");
log.info("DWL capability detected");
requestState();
refreshOutputScales();
} else if (!hasDwl) {
@@ -130,7 +132,7 @@ Singleton {
"toggleTagset": toggleTagset
}, response => {
if (response.error) {
console.warn("DwlService: setTags error:", response.error);
log.warn("setTags error:", response.error);
}
});
}
@@ -146,7 +148,7 @@ Singleton {
"xorTags": xorTags
}, response => {
if (response.error) {
console.warn("DwlService: setClientTags error:", response.error);
log.warn("setClientTags error:", response.error);
}
});
}
@@ -161,7 +163,7 @@ Singleton {
"index": index
}, response => {
if (response.error) {
console.warn("DwlService: setLayout error:", response.error);
log.warn("setLayout error:", response.error);
}
});
}
@@ -205,7 +207,7 @@ Singleton {
function toggleTag(outputName, tagIndex) {
const output = getOutputState(outputName);
if (!output || !output.tags) {
console.log("toggleTag: no output or tags for", outputName);
log.debug("toggleTag: no output or tags for", outputName);
return;
}
@@ -219,13 +221,13 @@ Singleton {
const clickedMask = 1 << tagIndex;
const newMask = currentMask ^ clickedMask;
console.log("toggleTag:", outputName, "tag:", tagIndex, "currentMask:", currentMask.toString(2), "clickedMask:", clickedMask.toString(2), "newMask:", newMask.toString(2));
log.debug("toggleTag:", outputName, "tag:", tagIndex, "currentMask:", currentMask.toString(2), "clickedMask:", clickedMask.toString(2), "newMask:", newMask.toString(2));
if (newMask === 0) {
console.log("toggleTag: newMask is 0, switching to tag", tagIndex);
log.debug("toggleTag: newMask is 0, switching to tag", tagIndex);
setTags(outputName, 1 << tagIndex, 0);
} else {
console.log("toggleTag: setting combined mask", newMask);
log.debug("toggleTag: setting combined mask", newMask);
setTags(outputName, newMask, 0);
}
}
@@ -256,14 +258,14 @@ Singleton {
}
outputScales = newScales;
} catch (e) {
console.warn("DwlService: Failed to parse mmsg output:", e);
log.warn("Failed to parse mmsg output:", e);
}
}
}
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("DwlService: mmsg failed with exit code:", exitCode);
log.warn("mmsg failed with exit code:", exitCode);
}
}
}
@@ -333,10 +335,10 @@ Singleton {
Proc.runCommand("mango-write-outputs", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("DwlService: Failed to write outputs config:", output);
log.warn("Failed to write outputs config:", output);
return;
}
console.info("DwlService: Generated outputs config at", outputsPath);
log.info("Generated outputs config at", outputsPath);
if (CompositorService.isDwl)
reloadConfig();
});
@@ -345,7 +347,7 @@ Singleton {
function reloadConfig() {
Proc.runCommand("mango-reload", ["mmsg", "-d", "reload_config"], (output, exitCode) => {
if (exitCode !== 0)
console.warn("DwlService: mmsg reload_config failed:", output);
log.warn("mmsg reload_config failed:", output);
});
}
@@ -372,10 +374,10 @@ borderpx=${borderSize}
Proc.runCommand("mango-write-layout", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("DwlService: Failed to write layout config:", output);
log.warn("Failed to write layout config:", output);
return;
}
console.info("DwlService: Generated layout config at", layoutPath);
log.info("Generated layout config at", layoutPath);
reloadConfig();
});
}
@@ -407,13 +409,13 @@ borderpx=${borderSize}
if (!CompositorService.isDwl)
return;
console.log("DwlService: Generating cursor config...");
log.debug("Generating cursor config...");
const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null;
if (!settings) {
Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("DwlService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
});
return;
}
@@ -426,7 +428,7 @@ borderpx=${borderSize}
if (isDefaultConfig) {
Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("DwlService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
});
return;
}
@@ -444,10 +446,10 @@ cursor_size=${size}`;
Proc.runCommand("mango-write-cursor", ["sh", "-c", `mkdir -p "${mangoDmsDir}" && cat > "${cursorPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("DwlService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
return;
}
console.info("DwlService: Generated cursor config at", cursorPath);
log.info("Generated cursor config at", cursorPath);
reloadConfig();
});
}

View File

@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("ExtWorkspaceService")
property bool extWorkspaceAvailable: false
property var groups: []
@@ -49,13 +51,13 @@ Singleton {
if (typeof CompositorService !== "undefined") {
const useExtWorkspace = DMSService.forceExtWorkspace || (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isDwl && !CompositorService.isSway && !CompositorService.isScroll && !CompositorService.isMiracle);
if (!useExtWorkspace) {
console.info("ExtWorkspaceService: ext-workspace available but compositor has native support");
log.info("ext-workspace available but compositor has native support");
extWorkspaceAvailable = false;
return;
}
}
extWorkspaceAvailable = true;
console.info("ExtWorkspaceService: ext-workspace capability detected");
log.info("ext-workspace capability detected");
DMSService.addSubscription("extworkspace");
requestState();
} else if (!hasExtWorkspace) {
@@ -78,9 +80,9 @@ Singleton {
function handleStateUpdate(state) {
groups = state.groups || [];
if (groups.length === 0) {
console.warn("ExtWorkspaceService: Received empty workspace groups from backend");
log.warn("Received empty workspace groups from backend");
} else {
console.log("ExtWorkspaceService: Updated with", groups.length, "workspace groups");
log.debug("Updated with", groups.length, "workspace groups");
}
stateChanged();
}
@@ -95,7 +97,7 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: activateWorkspace error:", response.error);
log.warn("activateWorkspace error:", response.error);
}
});
}
@@ -110,7 +112,7 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: deactivateWorkspace error:", response.error);
log.warn("deactivateWorkspace error:", response.error);
}
});
}
@@ -125,7 +127,7 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: removeWorkspace error:", response.error);
log.warn("removeWorkspace error:", response.error);
}
});
}
@@ -140,7 +142,7 @@ Singleton {
"name": name
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: createWorkspace error:", response.error);
log.warn("createWorkspace error:", response.error);
}
});
}
@@ -272,6 +274,6 @@ Singleton {
return;
}
}
console.warn("ExtWorkspaceService: workspace not found:", workspaceName);
log.warn("workspace not found:", workspaceName);
}
}

View File

@@ -6,9 +6,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("FirstLaunchService")
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell"
readonly property string settingsPath: configDir + "/settings.json"
@@ -77,10 +79,10 @@ Singleton {
if (result === "first") {
root.isFirstLaunch = true;
console.info("FirstLaunchService: First launch detected, greeter will be shown");
log.info("First launch detected, greeter will be shown");
} else if (result === "existing_user") {
root.isFirstLaunch = false;
console.info("FirstLaunchService: Existing user detected, silently creating marker");
log.info("Existing user detected, silently creating marker");
touchMarkerProcess.running = true;
} else {
root.isFirstLaunch = false;
@@ -102,9 +104,9 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
console.info("FirstLaunchService: First launch marker created");
log.info("First launch marker created");
} else {
console.warn("FirstLaunchService: Failed to create first launch marker");
log.warn("Failed to create first launch marker");
}
}
}

View File

@@ -6,9 +6,11 @@ import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("HyprlandService")
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
readonly property string hyprDmsDir: configDir + "/hypr/dms"
@@ -29,7 +31,7 @@ Singleton {
function ensureWindowrulesConfig() {
Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("HyprlandService: Failed to ensure windowrules.conf:", output);
log.warn("Failed to ensure windowrules.conf:", output);
});
}
@@ -159,10 +161,10 @@ Singleton {
Proc.runCommand("hypr-write-outputs", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("HyprlandService: Failed to write outputs config:", output);
log.warn("Failed to write outputs config:", output);
return;
}
console.info("HyprlandService: Generated outputs config at", outputsPath);
log.info("Generated outputs config at", outputsPath);
if (CompositorService.isHyprland)
reloadConfig();
});
@@ -171,7 +173,7 @@ Singleton {
function reloadConfig() {
Proc.runCommand("hyprctl-reload", ["hyprctl", "reload"], (output, exitCode) => {
if (exitCode !== 0)
console.warn("HyprlandService: hyprctl reload failed:", output);
log.warn("hyprctl reload failed:", output);
});
}
@@ -202,10 +204,10 @@ decoration {
Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("HyprlandService: Failed to write layout config:", output);
log.warn("Failed to write layout config:", output);
return;
}
console.info("HyprlandService: Generated layout config at", layoutPath);
log.info("Generated layout config at", layoutPath);
reloadConfig();
});
}
@@ -264,7 +266,7 @@ decoration {
if (!settings) {
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("HyprlandService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
});
return;
}
@@ -282,7 +284,7 @@ decoration {
if (!hasTheme && !hasNonDefaultSize && !hasCursorSettings) {
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("HyprlandService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
});
return;
}
@@ -313,7 +315,7 @@ decoration {
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${cursorPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("HyprlandService: Failed to write cursor config:", output);
log.warn("Failed to write cursor config:", output);
return;
}
if (hasTheme)
@@ -331,7 +333,7 @@ decoration {
const fullName = wsId + " " + newName;
Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("HyprlandService: Failed to rename workspace:", output);
log.warn("Failed to rename workspace:", output);
}
});
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("IdleService")
readonly property bool idleMonitorAvailable: {
try {
@@ -82,7 +84,7 @@ Singleton {
function createIdleMonitors() {
if (!idleMonitorAvailable) {
console.info("IdleService: IdleMonitor not available, skipping creation");
log.info("IdleMonitor not available, skipping creation");
return;
}
@@ -157,7 +159,7 @@ Singleton {
}
});
} catch (e) {
console.warn("IdleService: Error creating IdleMonitors:", e);
log.warn("Error creating IdleMonitors:", e);
}
}
@@ -181,11 +183,11 @@ Singleton {
onExternalInhibitActiveChanged: {
if (externalInhibitActive) {
const apps = DMSService.screensaverInhibitors.map(i => i.appName).join(", ");
console.info("IdleService: External idle inhibit active from:", apps || "unknown");
log.info("External idle inhibit active from:", apps || "unknown");
SessionService.idleInhibited = true;
SessionService.inhibitReason = "External app: " + (apps || "unknown");
} else {
console.info("IdleService: External idle inhibit released");
log.info("External idle inhibit released");
SessionService.idleInhibited = false;
SessionService.inhibitReason = "Keep system awake";
}
@@ -193,9 +195,9 @@ Singleton {
Component.onCompleted: {
if (!idleMonitorAvailable) {
console.warn("IdleService: IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.");
log.warn("IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.");
} else {
console.info("IdleService: Initialized with idle monitoring support");
log.info("Initialized with idle monitoring support");
createIdleMonitors();
}

View File

@@ -8,14 +8,16 @@ import Quickshell.Io
import Quickshell.Wayland
// ! Even though qmlls says this is unused, it is wrong
import qs.Common
import qs.Services
import "../Common/KeybindActions.js" as Actions
Singleton {
id: root
readonly property var log: Log.scoped("KeybindsService")
Component.onCompleted: {
if (!shortcutInhibitorAvailable) {
console.warn("[KeybindsService] ShortcutInhibitor is not available in this environment, keybinds editor disabled.");
log.warn("ShortcutInhibitor is not available in this environment, keybinds editor disabled.");
}
}
@@ -150,7 +152,7 @@ Singleton {
try {
root.cheatsheet = JSON.parse(text);
} catch (e) {
console.error("[KeybindsService] Failed to parse cheatsheet:", e);
log.error("Failed to parse cheatsheet:", e);
root.cheatsheet = {};
}
root.cheatsheetLoading = false;
@@ -161,7 +163,7 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0)
return;
console.warn("[KeybindsService] Cheatsheet load failed with code:", exitCode);
log.warn("Cheatsheet load failed with code:", exitCode);
root.cheatsheetLoading = false;
}
}
@@ -176,7 +178,7 @@ Singleton {
root._rawData = JSON.parse(text);
root._processData();
} catch (e) {
console.error("[KeybindsService] Failed to parse binds:", e);
log.error("Failed to parse binds:", e);
}
root.loading = false;
}
@@ -184,7 +186,7 @@ Singleton {
onExited: exitCode => {
if (exitCode !== 0) {
console.warn("[KeybindsService] Load process failed with code:", exitCode);
log.warn("Load process failed with code:", exitCode);
root.loading = false;
}
}
@@ -206,7 +208,7 @@ Singleton {
onExited: exitCode => {
root.saving = false;
if (exitCode !== 0) {
console.error("[KeybindsService] Save failed with code:", exitCode);
log.error("Save failed with code:", exitCode);
root.bindSaveCompleted(false);
return;
}
@@ -231,7 +233,7 @@ Singleton {
onExited: exitCode => {
if (exitCode !== 0) {
console.error("[KeybindsService] Remove failed with code:", exitCode);
log.error("Remove failed with code:", exitCode);
return;
}
root.lastError = "";
@@ -255,7 +257,7 @@ Singleton {
onExited: exitCode => {
root.fixing = false;
if (exitCode !== 0) {
console.error("[KeybindsService] Fix failed with code:", exitCode);
log.error("Fix failed with code:", exitCode);
return;
}
root.lastError = "";

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
Singleton {
id: root

226
quickshell/Services/Log.qml Normal file
View File

@@ -0,0 +1,226 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
enum Level {
Debug,
Info,
Warn,
Error,
Fatal
}
readonly property int level: _parseLevel(Quickshell.env("DMS_LOG_LEVEL"))
readonly property string levelName: _levelName(level)
readonly property string _logFilePath: Quickshell.env("DMS_LOG_FILE") || ""
readonly property bool _useColor: !Quickshell.env("NO_COLOR") && Quickshell.env("DMS_LOG_NO_COLOR") !== "1"
function scoped(module) {
return {
debug: function () {
root._emit(Log.Level.Debug, module, arguments);
},
info: function () {
root._emit(Log.Level.Info, module, arguments);
},
warn: function () {
root._emit(Log.Level.Warn, module, arguments);
},
error: function () {
root._emit(Log.Level.Error, module, arguments);
},
fatal: function () {
root._emit(Log.Level.Fatal, module, arguments);
}
};
}
function debug() {
_emit(Log.Level.Debug, "", arguments);
}
function info() {
_emit(Log.Level.Info, "", arguments);
}
function warn() {
_emit(Log.Level.Warn, "", arguments);
}
function error() {
_emit(Log.Level.Error, "", arguments);
}
function fatal() {
_emit(Log.Level.Fatal, "", arguments);
}
function callStack() {
const trace = _captureStack(0).split("\n").map(l => l.trim()).filter(l => l.length > 0);
_emit(Log.Level.Info, "Debug", ["--------------------------"]);
_emit(Log.Level.Info, "Debug", ["Current call stack"]);
for (const line of trace)
_emit(Log.Level.Info, "Debug", ["- " + line]);
_emit(Log.Level.Info, "Debug", ["--------------------------"]);
}
function _parseLevel(name) {
switch ((name || "").toLowerCase()) {
case "debug":
return Log.Level.Debug;
case "warn":
case "warning":
return Log.Level.Warn;
case "error":
return Log.Level.Error;
case "fatal":
return Log.Level.Fatal;
default:
return Log.Level.Info;
}
}
function _levelName(lvl) {
switch (lvl) {
case Log.Level.Debug:
return "debug";
case Log.Level.Info:
return "info";
case Log.Level.Warn:
return "warn";
case Log.Level.Error:
return "error";
case Log.Level.Fatal:
return "fatal";
}
return "info";
}
function _levelTag(lvl, color) {
let tag, ansi;
switch (lvl) {
case Log.Level.Fatal:
tag = " FATAL";
ansi = "\x1b[31m";
break;
case Log.Level.Error:
tag = " ERROR";
ansi = "\x1b[91m";
break;
case Log.Level.Warn:
tag = " WARN";
ansi = "\x1b[33m";
break;
case Log.Level.Info:
tag = " INFO";
ansi = "\x1b[32m";
break;
case Log.Level.Debug:
tag = " DEBUG";
ansi = "\x1b[34m";
break;
default:
return " INFO";
}
if (!color)
return tag;
return ansi + tag + "\x1b[0m";
}
function _stringify(v) {
if (v === null)
return "null";
if (v === undefined)
return "undefined";
if (typeof v === "string")
return v;
if (v instanceof Error)
return v.toString();
try {
return JSON.stringify(v);
} catch (e) {
return String(v);
}
}
function _captureStack(skip) {
try {
throw new Error();
} catch (e) {
const lines = (e.stack || "").split("\n");
return lines.slice(1 + (skip || 0)).join("\n");
}
}
function _callerLocation() {
const stack = _captureStack(2);
const lines = stack.split("\n");
for (const line of lines) {
const m = line.match(/([^/@\s]+\.qml):(\d+)/);
if (!m)
continue;
if (m[1] === "Log.qml")
continue;
return {
file: m[1],
line: m[2]
};
}
return null;
}
function _emit(lvl, module, args) {
if (lvl < root.level)
return;
const argList = Array.from(args);
const loc = _callerLocation();
const msg = argList.map(_stringify).join(" ");
let tag;
if (module && loc && loc.file === module + ".qml")
tag = "[" + module + ":" + loc.line + "] ";
else if (module && loc)
tag = "[" + module + "] (" + loc.file + ":" + loc.line + ") ";
else if (module)
tag = "[" + module + "] ";
else if (loc)
tag = "(" + loc.file + ":" + loc.line + ") ";
else
tag = "";
const body = tag + msg;
switch (lvl) {
case Log.Level.Debug:
console.debug(body);
break;
case Log.Level.Info:
console.info(body);
break;
case Log.Level.Warn:
console.warn(body);
break;
case Log.Level.Error:
case Log.Level.Fatal:
console.error(body);
break;
}
if (root._logFilePath && fileTee.running)
fileTee.write(_levelTag(lvl, false) + " qml: " + body + "\n");
if (lvl === Log.Level.Fatal)
Qt.callLater(() => Qt.exit(1));
}
Process {
id: fileTee
command: ["sh", "-c", "exec tee -a \"$0\" >/dev/null", root._logFilePath]
stdinEnabled: true
running: root._logFilePath.length > 0
}
}

View File

@@ -6,6 +6,7 @@ import Quickshell
Singleton {
id: root
readonly property var log: Log.scoped("MultimediaService")
property bool available: false
@@ -14,6 +15,7 @@ Singleton {
const testObj = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
import qs.Services
Item {}
`, root, "MultimediaService.TestComponent");
if (testObj) {
@@ -29,7 +31,7 @@ Singleton {
Component.onCompleted: {
if (!detectAvailability()) {
console.warn("MultimediaService: QtMultimedia not available");
log.warn("QtMultimedia not available");
}
}
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("MuxService")
property var sessions: []
property bool loading: false
@@ -20,46 +22,50 @@ Singleton {
readonly property string displayName: muxType === "zellij" ? "Zellij" : "Tmux"
readonly property var terminalFlags: ({
"ghostty": ["-e"],
"kitty": ["-e"],
"alacritty": ["-e"],
"foot": [],
"wezterm": ["start", "--"],
"gnome-terminal": ["--"],
"xterm": ["-e"],
"konsole": ["-e"],
"st": ["-e"],
"terminator": ["-e"],
"xfce4-terminal": ["-e"]
})
"ghostty": ["-e"],
"kitty": ["-e"],
"alacritty": ["-e"],
"foot": [],
"wezterm": ["start", "--"],
"gnome-terminal": ["--"],
"xterm": ["-e"],
"konsole": ["-e"],
"st": ["-e"],
"terminator": ["-e"],
"xfce4-terminal": ["-e"]
})
function getTerminalFlag(terminal) {
return terminalFlags[terminal] ?? ["-e"]
return terminalFlags[terminal] ?? ["-e"];
}
readonly property string terminal: SessionData.resolveTerminal() || "ghostty"
function _terminalPrefix() {
return [terminal].concat(getTerminalFlag(terminal))
return [terminal].concat(getTerminalFlag(terminal));
}
Process {
id: tmuxCheckProcess
command: ["which", "tmux"]
running: false
onExited: (code) => { root.tmuxAvailable = (code === 0) }
onExited: code => {
root.tmuxAvailable = (code === 0);
}
}
Process {
id: zellijCheckProcess
command: ["which", "zellij"]
running: false
onExited: (code) => { root.zellijAvailable = (code === 0) }
onExited: code => {
root.zellijAvailable = (code === 0);
}
}
function checkAvailability() {
tmuxCheckProcess.running = true
zellijCheckProcess.running = true
tmuxCheckProcess.running = true;
zellijCheckProcess.running = true;
}
Component.onCompleted: checkAvailability()
@@ -72,141 +78,139 @@ Singleton {
onStreamFinished: {
try {
if (root.muxType === "zellij")
root._parseZellijSessions(text)
root._parseZellijSessions(text);
else
root._parseTmuxSessions(text)
root._parseTmuxSessions(text);
} catch (e) {
console.error("[MuxService] Error parsing sessions:", e)
root.sessions = []
log.error("Error parsing sessions:", e);
root.sessions = [];
}
root.loading = false
root.loading = false;
}
}
stderr: SplitParser {
onRead: (line) => {
onRead: line => {
if (line.trim())
console.error("[MuxService] stderr:", line)
log.error("stderr:", line);
}
}
onExited: (code) => {
onExited: code => {
if (code !== 0 && code !== 1) {
console.warn("[MuxService] Process exited with code:", code)
root.sessions = []
log.warn("Process exited with code:", code);
root.sessions = [];
}
root.loading = false
root.loading = false;
}
}
function refreshSessions() {
if (!root.currentMuxAvailable) {
root.sessions = []
return
root.sessions = [];
return;
}
root.loading = true
root.loading = true;
if (listProcess.running)
listProcess.running = false
listProcess.running = false;
if (root.muxType === "zellij")
listProcess.command = ["zellij", "list-sessions", "--no-formatting"]
listProcess.command = ["zellij", "list-sessions", "--no-formatting"];
else
listProcess.command = ["tmux", "list-sessions", "-F", "#{session_name}|#{session_windows}|#{session_attached}"]
listProcess.command = ["tmux", "list-sessions", "-F", "#{session_name}|#{session_windows}|#{session_attached}"];
Qt.callLater(function () {
listProcess.running = true
})
listProcess.running = true;
});
}
function _isSessionExcluded(name) {
var filter = SettingsData.muxSessionFilter.trim()
var filter = SettingsData.muxSessionFilter.trim();
if (filter.length === 0)
return false
var parts = filter.split(",")
return false;
var parts = filter.split(",");
for (var i = 0; i < parts.length; i++) {
var pattern = parts[i].trim()
var pattern = parts[i].trim();
if (pattern.length === 0)
continue
continue;
if (pattern.startsWith("/") && pattern.endsWith("/") && pattern.length > 2) {
try {
var re = new RegExp(pattern.slice(1, -1))
var re = new RegExp(pattern.slice(1, -1));
if (re.test(name))
return true
return true;
} catch (e) {}
} else {
if (name.toLowerCase() === pattern.toLowerCase())
return true
return true;
}
}
return false
return false;
}
function _parseTmuxSessions(output) {
var sessionList = []
var lines = output.trim().split('\n')
var sessionList = [];
var lines = output.trim().split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim()
var line = lines[i].trim();
if (line.length === 0)
continue
var parts = line.split('|')
continue;
var parts = line.split('|');
if (parts.length >= 3 && !_isSessionExcluded(parts[0])) {
sessionList.push({
name: parts[0],
windows: parts[1],
attached: parts[2] === "1"
})
});
}
}
root.sessions = sessionList
root.sessions = sessionList;
}
function _parseZellijSessions(output) {
var sessionList = []
var lines = output.trim().split('\n')
var sessionList = [];
var lines = output.trim().split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim()
var line = lines[i].trim();
if (line.length === 0)
continue
var exited = line.includes("(EXITED")
var bracketIdx = line.indexOf(" [")
var name = (bracketIdx > 0 ? line.substring(0, bracketIdx) : line).trim()
continue;
var exited = line.includes("(EXITED");
var bracketIdx = line.indexOf(" [");
var name = (bracketIdx > 0 ? line.substring(0, bracketIdx) : line).trim();
if (!_isSessionExcluded(name)) {
sessionList.push({
name: name,
windows: "N/A",
attached: !exited
})
});
}
}
root.sessions = sessionList
root.sessions = sessionList;
}
function attachToSession(name) {
if (SettingsData.muxUseCustomCommand && SettingsData.muxCustomCommand) {
Quickshell.execDetached([Paths.expandTilde(SettingsData.muxCustomCommand), name])
Quickshell.execDetached([Paths.expandTilde(SettingsData.muxCustomCommand), name]);
} else if (root.muxType === "zellij") {
Quickshell.execDetached(_terminalPrefix().concat(["zellij", "attach", name]))
Quickshell.execDetached(_terminalPrefix().concat(["zellij", "attach", name]));
} else {
Quickshell.execDetached(_terminalPrefix().concat(["tmux", "attach", "-t", name]))
Quickshell.execDetached(_terminalPrefix().concat(["tmux", "attach", "-t", name]));
}
}
function createSession(name) {
if (SettingsData.muxUseCustomCommand && SettingsData.muxCustomCommand) {
Quickshell.execDetached([Paths.expandTilde(SettingsData.muxCustomCommand), name])
Quickshell.execDetached([Paths.expandTilde(SettingsData.muxCustomCommand), name]);
} else if (root.muxType === "zellij") {
Quickshell.execDetached(_terminalPrefix().concat(["zellij", "-s", name]))
Quickshell.execDetached(_terminalPrefix().concat(["zellij", "-s", name]));
} else {
Quickshell.execDetached(_terminalPrefix().concat(["tmux", "new-session", "-s", name]))
Quickshell.execDetached(_terminalPrefix().concat(["tmux", "new-session", "-s", name]));
}
}
@@ -214,17 +218,17 @@ Singleton {
function renameSession(oldName, newName) {
if (root.muxType === "zellij")
return
Quickshell.execDetached(["tmux", "rename-session", "-t", oldName, newName])
Qt.callLater(refreshSessions)
return;
Quickshell.execDetached(["tmux", "rename-session", "-t", oldName, newName]);
Qt.callLater(refreshSessions);
}
function killSession(name) {
if (root.muxType === "zellij") {
Quickshell.execDetached(["zellij", "kill-session", name])
Quickshell.execDetached(["zellij", "kill-session", name]);
} else {
Quickshell.execDetached(["tmux", "kill-session", "-t", name])
Quickshell.execDetached(["tmux", "kill-session", "-t", name]);
}
Qt.callLater(refreshSessions)
Qt.callLater(refreshSessions);
}
}

View File

@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("NetworkService")
property bool networkAvailable: activeService !== null
property string backend: activeService?.backend ?? ""
@@ -97,12 +99,12 @@ Singleton {
readonly property string socketPath: Quickshell.env("DMS_SOCKET")
Component.onCompleted: {
console.info("NetworkService: Initializing...");
log.info("Initializing...");
if (!socketPath || socketPath.length === 0) {
console.info("NetworkService: DMS_SOCKET not set, using LegacyNetworkService");
log.info("DMS_SOCKET not set, using LegacyNetworkService");
useLegacyService();
} else {
console.log("NetworkService: DMS_SOCKET found, waiting for capabilities...");
log.debug("DMS_SOCKET found, waiting for capabilities...");
}
}
@@ -111,13 +113,13 @@ Singleton {
function onNetworkAvailableChanged() {
if (!activeService && DMSNetworkService.networkAvailable) {
console.info("NetworkService: Network capability detected, using DMSNetworkService");
log.info("Network capability detected, using DMSNetworkService");
activeService = DMSNetworkService;
usingLegacy = false;
console.info("NetworkService: Switched to DMSNetworkService, networkAvailable:", networkAvailable);
log.info("Switched to DMSNetworkService, networkAvailable:", networkAvailable);
connectSignals();
} else if (!activeService && !DMSNetworkService.networkAvailable && socketPath && socketPath.length > 0) {
console.info("NetworkService: Network capability not available in DMS, using LegacyNetworkService");
log.info("Network capability not available in DMS, using LegacyNetworkService");
useLegacyService();
}
}
@@ -126,7 +128,7 @@ Singleton {
function useLegacyService() {
activeService = LegacyNetworkService;
usingLegacy = true;
console.info("NetworkService: Switched to LegacyNetworkService, networkAvailable:", networkAvailable);
log.info("Switched to LegacyNetworkService, networkAvailable:", networkAvailable);
if (LegacyNetworkService.activate) {
LegacyNetworkService.activate();
}

View File

@@ -6,9 +6,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("NiriService")
readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
@@ -118,10 +120,10 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated layout config at", configPath);
log.info("Generated layout config at", configPath);
return;
}
console.warn("NiriService: Failed to write layout config, exit code:", exitCode);
log.warn("Failed to write layout config, exit code:", exitCode);
}
}
@@ -132,10 +134,10 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated alttab config at", alttabPath);
log.info("Generated alttab config at", alttabPath);
return;
}
console.warn("NiriService: Failed to write alttab config, exit code:", exitCode);
log.warn("Failed to write alttab config, exit code:", exitCode);
}
}
@@ -145,10 +147,10 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated wpblur config at", blurrulePath);
log.info("Generated wpblur config at", blurrulePath);
return;
}
console.warn("NiriService: Failed to write wpblur config, exit code:", exitCode);
log.warn("Failed to write wpblur config, exit code:", exitCode);
}
}
@@ -159,10 +161,10 @@ Singleton {
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated cursor config at", cursorPath);
log.info("Generated cursor config at", cursorPath);
return;
}
console.warn("NiriService: Failed to write cursor config, exit code:", exitCode);
log.warn("Failed to write cursor config, exit code:", exitCode);
}
}
@@ -184,7 +186,7 @@ Singleton {
const event = JSON.parse(line);
handleNiriEvent(event);
} catch (e) {
console.warn("NiriService: Failed to parse event:", line, e);
log.warn("Failed to parse event:", line, e);
}
}
}
@@ -201,19 +203,19 @@ Singleton {
return;
Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode);
log.warn("Failed to fetch outputs, exit code:", exitCode);
return;
}
try {
const outputsData = JSON.parse(output);
outputs = outputsData;
console.info("NiriService: Loaded", Object.keys(outputsData).length, "outputs");
log.info("Loaded", Object.keys(outputsData).length, "outputs");
updateDisplayScales();
if (windows.length > 0) {
windows = sortWindowsByLayout(windows);
}
} catch (e) {
console.warn("NiriService: Failed to parse outputs:", e);
log.warn("Failed to parse outputs:", e);
}
});
}
@@ -1076,7 +1078,7 @@ Singleton {
}
function doGenerateNiriLayoutConfig() {
console.log("NiriService: Generating layout config...");
log.debug("Generating layout config...");
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
@@ -1136,7 +1138,7 @@ Singleton {
const path = niriDmsDir + "/" + name + ".kdl";
Proc.runCommand("niri-ensure-" + name, ["sh", "-c", `mkdir -p "${niriDmsDir}" && [ ! -f "${path}" ] && touch "${path}" || true`], (output, exitCode) => {
if (exitCode !== 0)
console.warn("NiriService: Failed to ensure " + name + ".kdl, exit code:", exitCode);
log.warn("Failed to ensure " + name + ".kdl, exit code:", exitCode);
});
}
@@ -1144,7 +1146,7 @@ Singleton {
}
function generateNiriBlurrule() {
console.log("NiriService: Generating wpblur config...");
log.debug("Generating wpblur config...");
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
const niriDmsDir = configDir + "/niri/dms";
@@ -1160,7 +1162,7 @@ Singleton {
if (!CompositorService.isNiri)
return;
console.log("NiriService: Generating cursor config...");
log.debug("Generating cursor config...");
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
const niriDmsDir = configDir + "/niri/dms";
@@ -1275,12 +1277,12 @@ Singleton {
const fullCommand = commands.join(" && ");
Proc.runCommand("niri-output-config", ["sh", "-c", fullCommand], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("NiriService: Failed to apply output config:", output);
log.warn("Failed to apply output config:", output);
if (callback)
callback(false, output);
return;
}
console.info("NiriService: Applied output config for", outputName);
log.info("Applied output config for", outputName);
fetchOutputs();
if (callback)
callback(true, "Success");
@@ -1369,10 +1371,10 @@ Singleton {
Proc.runCommand("niri-write-outputs", ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${kdlContent}EOF`], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("NiriService: Failed to write outputs config:", output);
log.warn("Failed to write outputs config:", output);
return;
}
console.info("NiriService: Generated outputs config at", outputsPath);
log.info("Generated outputs config at", outputsPath);
});
}

View File

@@ -6,9 +6,11 @@ import QtCore
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("NotepadStorageService")
property int refCount: 0
@@ -39,7 +41,7 @@ Singleton {
root.metadataLoaded = true
root.validateTabs()
} catch(e) {
console.warn("Failed to parse notepad metadata:", e)
log.warn("Failed to parse notepad metadata:", e)
root.createDefaultTab()
}
}
@@ -148,7 +150,7 @@ Singleton {
callback: callback
})
} else {
console.warn("Tab file does not exist:", fullPath)
log.warn("Tab file does not exist:", fullPath)
callback("")
}
}
@@ -389,7 +391,7 @@ Singleton {
}
onSaveFailed: {
console.error("Failed to save tab content")
log.error("Failed to save tab content")
if (creationCallback) {
creationCallback()
}

View File

@@ -6,10 +6,12 @@ import Quickshell
import Quickshell.Io
import Quickshell.Services.Notifications
import qs.Common
import qs.Services
import "../Common/markdown2html.js" as Markdown2Html
Singleton {
id: root
readonly property var log: Log.scoped("NotificationService")
readonly property list<NotifWrapper> notifications: []
readonly property list<NotifWrapper> allWrappers: []
@@ -153,7 +155,7 @@ Singleton {
historyAdapter.notifications = historyList;
historyFileView.writeAdapter();
} catch (e) {
console.warn("NotificationService: save history failed:", e);
log.warn("save history failed:", e);
}
}
@@ -203,7 +205,7 @@ Singleton {
if ((maxAgeMs > 0 && loaded.length !== (historyAdapter.notifications || []).length) || needsRewrite)
saveHistory();
} catch (e) {
console.warn("NotificationService: load history failed:", e);
log.warn("load history failed:", e);
historyLoaded = true;
}
}
@@ -403,7 +405,7 @@ Singleton {
try {
return new RegExp(pattern, "i").test(value);
} catch (e) {
console.warn("NotificationService: invalid notification rule regex:", pattern);
log.warn("invalid notification rule regex:", pattern);
return false;
}
}

View File

@@ -10,6 +10,7 @@ import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("PluginService")
property var availablePlugins: ({})
property var loadedPlugins: ({})
@@ -167,13 +168,13 @@ Singleton {
const manifest = JSON.parse(raw)
root._onManifestParsed(absPath, manifest, "${sourceTag}", ${mtimeEpochMs})
} catch (e) {
console.error("PluginService: bad manifest", absPath, e.message)
log.error("bad manifest", absPath, e.message)
knownManifests[absPath] = { mtime: ${mtimeEpochMs}, source: "${sourceTag}", bad: true }
}
fv.destroy()
}
onLoadFailed: (err) => {
console.warn("PluginService: manifest load failed", absPath, err)
log.warn("manifest load failed", absPath, err)
fv.destroy()
}
}
@@ -186,7 +187,7 @@ Singleton {
function _onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs) {
if (!manifest || !manifest.id || !manifest.name || !manifest.component) {
console.error("PluginService: invalid manifest fields:", absPath);
log.error("invalid manifest fields:", absPath);
knownManifests[absPath] = {
mtime: mtimeEpochMs,
source: sourceTag,
@@ -269,7 +270,7 @@ Singleton {
function loadPlugin(pluginId, bustCache) {
const plugin = availablePlugins[pluginId];
if (!plugin) {
console.error("PluginService: Plugin not found:", pluginId);
log.error("Plugin not found:", pluginId);
pluginLoadFailed(pluginId, "Plugin not found");
return false;
}
@@ -296,7 +297,7 @@ Singleton {
url += "?t=" + Date.now();
const comp = Qt.createComponent(url, Component.PreferSynchronous);
if (comp.status === Component.Error) {
console.error("PluginService: component error", pluginId, comp.errorString());
log.error("component error", pluginId, comp.errorString());
pluginLoadFailed(pluginId, comp.errorString());
return false;
}
@@ -310,7 +311,7 @@ Singleton {
"pluginService": root
});
if (!instance) {
console.error("PluginService: failed to instantiate plugin:", pluginId, comp.errorString());
log.error("failed to instantiate plugin:", pluginId, comp.errorString());
pluginLoadFailed(pluginId, comp.errorString());
return false;
}
@@ -339,7 +340,7 @@ Singleton {
pluginLoaded(pluginId);
return true;
} catch (e) {
console.error("PluginService: Error loading plugin:", pluginId, e.message);
log.error("Error loading plugin:", pluginId, e.message);
pluginLoadFailed(pluginId, e.message);
return false;
}
@@ -348,7 +349,7 @@ Singleton {
function unloadPlugin(pluginId) {
const plugin = loadedPlugins[pluginId];
if (!plugin) {
console.warn("PluginService: Plugin not loaded:", pluginId);
log.warn("Plugin not loaded:", pluginId);
return false;
}
@@ -392,7 +393,7 @@ Singleton {
pluginUnloaded(pluginId);
return true;
} catch (error) {
console.error("PluginService: Error unloading plugin:", pluginId, "Error:", error.message);
log.error("Error unloading plugin:", pluginId, "Error:", error.message);
return false;
}
}
@@ -705,7 +706,7 @@ Singleton {
fv.setText(content);
});
} catch (e) {
console.warn("PluginService: Failed to write state for", pluginId, e.message);
log.warn("Failed to write state for", pluginId, e.message);
}
}
@@ -753,14 +754,14 @@ Singleton {
process.command = ["mkdir", "-p", pluginDirectory];
process.exited.connect(function (exitCode) {
if (exitCode !== 0) {
console.error("PluginService: Failed to create plugin directory, exit code:", exitCode);
log.error("Failed to create plugin directory, exit code:", exitCode);
}
process.destroy();
});
process.running = true;
return true;
} else {
console.error("PluginService: Failed to create mkdir process");
log.error("Failed to create mkdir process");
return false;
}
}

View File

@@ -6,6 +6,7 @@ import Quickshell
Singleton {
id: root
readonly property var log: Log.scoped("PolkitService")
readonly property bool disablePolkitIntegration: Quickshell.env("DMS_DISABLE_POLKIT") === "1"
@@ -17,6 +18,7 @@ Singleton {
const qmlString = `
import QtQuick
import Quickshell.Services.Polkit
import qs.Services
PolkitAgent {
}
@@ -24,10 +26,10 @@ Singleton {
agent = Qt.createQmlObject(qmlString, root, "PolkitService.Agent")
polkitAvailable = true
console.info("PolkitService: Initialized successfully")
log.info("Initialized successfully")
} catch (e) {
polkitAvailable = false
console.warn("PolkitService: Polkit not available - authentication prompts disabled. This requires a newer version of Quickshell.")
log.warn("Polkit not available - authentication prompts disabled. This requires a newer version of Quickshell.")
}
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("PortalService")
property bool accountsServiceAvailable: false
property string systemProfileImage: ""
@@ -127,7 +129,7 @@ Singleton {
"iconTheme": themeName
}, response => {
if (response.error) {
console.warn("PortalService: Failed to set icon theme:", response.error);
log.warn("Failed to set icon theme:", response.error);
}
});
}
@@ -139,7 +141,7 @@ Singleton {
"path": imagePath || ""
}, response => {
if (response.error) {
console.warn("PortalService: Failed to set icon file:", response.error);
log.warn("Failed to set icon file:", response.error);
const errorMsg = response.error.toString();
let userMessage = I18n.tr("Failed to set profile image");
@@ -169,7 +171,7 @@ Singleton {
if (socketPath && socketPath.length > 0) {
checkDMSCapabilities();
} else {
console.info("PortalService: DMS_SOCKET not set");
log.info("DMS_SOCKET not set");
}
colorSchemeDetector.running = true;
}
@@ -207,7 +209,7 @@ Singleton {
checkAccountsService();
checkSettingsPortal();
} else {
console.info("PortalService: freedesktop capability not available in DMS");
log.info("freedesktop capability not available in DMS");
}
}

View File

@@ -8,9 +8,11 @@ import Quickshell.Hyprland
import Quickshell.I3
import Quickshell.Wayland
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("SessionService")
property bool hasUwsm: false
property bool isElogind: false
@@ -64,15 +66,15 @@ Singleton {
detectHibernateProcess.running = true;
detectPrimeRunProcess.running = true;
detectWtypeProcess.running = true;
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable);
log.info("Native inhibitor available:", nativeInhibitorAvailable);
if (!SettingsData.loginctlLockIntegration) {
console.log("SessionService: loginctl lock integration disabled by user");
log.debug("loginctl lock integration disabled by user");
return;
}
if (socketPath && socketPath.length > 0) {
checkDMSCapabilities();
} else {
console.log("SessionService: DMS_SOCKET not set");
log.debug("DMS_SOCKET not set");
}
}
}
@@ -93,7 +95,7 @@ Singleton {
command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"]
onExited: function (exitCode) {
console.log("SessionService: Elogind detection exited with code", exitCode);
log.debug("Elogind detection exited with code", exitCode);
isElogind = (exitCode === 0);
}
}
@@ -396,7 +398,7 @@ Singleton {
if (idleInhibited) {
return;
}
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
log.debug("Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = true;
inhibitorChanged();
}
@@ -405,7 +407,7 @@ Singleton {
if (!idleInhibited) {
return;
}
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
log.debug("Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = false;
inhibitorChanged();
}
@@ -441,19 +443,19 @@ Singleton {
return ["true"];
}
console.log("SessionService: Starting systemd/elogind inhibit process");
log.debug("Starting systemd/elogind inhibit process");
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"];
}
running: idleInhibited && !nativeInhibitorAvailable
onRunningChanged: {
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
log.debug("Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
}
onExited: function (exitCode) {
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode);
log.warn("Inhibitor process crashed with exit code:", exitCode);
idleInhibited = false;
ToastService.showWarning("Idle inhibitor failed");
}
@@ -545,7 +547,7 @@ Singleton {
}
} else {
loginctlAvailable = false;
console.log("SessionService: loginctl capability not available in DMS");
log.debug("loginctl capability not available in DMS");
}
if (DMSService.capabilities.includes("dbus")) {
@@ -574,7 +576,7 @@ Singleton {
prepareForSleepSubscriptionPending = false;
if (response.error) {
console.warn("SessionService: Failed to subscribe to PrepareForSleep:", response.error);
log.warn("Failed to subscribe to PrepareForSleep:", response.error);
return;
}
@@ -621,9 +623,9 @@ Singleton {
enabled: SettingsData.lockBeforeSuspend
}, response => {
if (response.error) {
console.warn("SessionService: Failed to sync lock before suspend:", response.error);
log.warn("Failed to sync lock before suspend:", response.error);
} else {
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend);
log.debug("Synced lock before suspend:", SettingsData.lockBeforeSuspend);
}
});
}
@@ -637,9 +639,9 @@ Singleton {
enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend
}, response => {
if (response.error) {
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error);
log.warn("Failed to sync sleep inhibitor:", response.error);
} else {
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration);
log.debug("Synced sleep inhibitor:", SettingsData.loginctlLockIntegration);
}
});
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("SettingsSearchService")
property string query: ""
property var results: []
@@ -41,12 +43,12 @@ Singleton {
root.indexLoaded = true;
root._rebuildTranslationCache();
} catch (e) {
console.warn("SettingsSearchService: Failed to parse index:", e);
log.warn("Failed to parse index:", e);
root.settingsIndex = [];
root._translatedCache = [];
}
}
onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error)
onLoadFailed: error => log.warn("Failed to load index:", error)
}
function registerCard(settingKey, item, flickable) {

View File

@@ -4,9 +4,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("VPNService")
readonly property bool available: DMSNetworkService.vpnAvailable
@@ -48,7 +50,7 @@ Singleton {
DMSService.sendRequest("network.vpn.plugins", null, response => {
pluginsLoading = false;
if (response.error) {
console.warn("VPNService: Failed to fetch plugins:", response.error);
log.warn("Failed to fetch plugins:", response.error);
return;
}
if (!response.result)

View File

@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("WlrOutputService")
property bool wlrOutputAvailable: false
property var outputs: []
@@ -53,7 +55,7 @@ Singleton {
const hasWlrOutput = DMSService.capabilities.includes("wlroutput");
if (hasWlrOutput && !wlrOutputAvailable) {
wlrOutputAvailable = true;
console.info("WlrOutputService: wlr-output-management capability detected");
log.info("wlr-output-management capability detected");
requestState();
return;
}
@@ -81,11 +83,11 @@ Singleton {
serial = state.serial || 0;
if (outputs.length === 0) {
console.warn("WlrOutputService: Received empty outputs list");
log.warn("Received empty outputs list");
} else {
console.log("WlrOutputService: Updated with", outputs.length, "outputs, serial:", serial);
log.debug("Updated with", outputs.length, "outputs, serial:", serial);
outputs.forEach((output, index) => {
console.log("WlrOutputService: Output", index, "-", output.name, "enabled:", output.enabled, "mode:", output.currentMode ? output.currentMode.width + "x" + output.currentMode.height + "@" + (output.currentMode.refresh / 1000) + "Hz" : "none");
log.debug("Output", index, "-", output.name, "enabled:", output.enabled, "mode:", output.currentMode ? output.currentMode.width + "x" + output.currentMode.height + "@" + (output.currentMode.refresh / 1000) + "Hz" : "none");
});
}
stateChanged();
@@ -112,9 +114,9 @@ Singleton {
return;
}
console.log("WlrOutputService: Applying configuration for", heads.length, "outputs");
log.debug("Applying configuration for", heads.length, "outputs");
heads.forEach((head, index) => {
console.log("WlrOutputService: Head", index, "- name:", head.name, "enabled:", head.enabled, "modeId:", head.modeId, "customMode:", JSON.stringify(head.customMode), "position:", JSON.stringify(head.position), "scale:", head.scale, "transform:", head.transform, "adaptiveSync:", head.adaptiveSync);
log.debug("Head", index, "- name:", head.name, "enabled:", head.enabled, "modeId:", head.modeId, "customMode:", JSON.stringify(head.customMode), "position:", JSON.stringify(head.position), "scale:", head.scale, "transform:", head.transform, "adaptiveSync:", head.adaptiveSync);
});
DMSService.sendRequest("wlroutput.applyConfiguration", {
@@ -124,9 +126,9 @@ Singleton {
const message = response.error || response.result?.message || "";
if (response.error) {
console.warn("WlrOutputService: applyConfiguration error:", response.error);
log.warn("applyConfiguration error:", response.error);
} else {
console.log("WlrOutputService: Configuration applied successfully");
log.debug("Configuration applied successfully");
}
configurationApplied(success, message);
@@ -144,7 +146,7 @@ Singleton {
return;
}
console.log("WlrOutputService: Testing configuration for", heads.length, "outputs");
log.debug("Testing configuration for", heads.length, "outputs");
DMSService.sendRequest("wlroutput.testConfiguration", {
"heads": heads
@@ -153,9 +155,9 @@ Singleton {
const message = response.error || response.result?.message || "";
if (response.error) {
console.warn("WlrOutputService: testConfiguration error:", response.error);
log.warn("testConfiguration error:", response.error);
} else {
console.log("WlrOutputService: Configuration test passed");
log.debug("Configuration test passed");
}
if (callback) {
@@ -167,7 +169,7 @@ Singleton {
function setOutputEnabled(outputName, enabled, callback) {
const output = getOutput(outputName);
if (!output) {
console.warn("WlrOutputService: Output not found:", outputName);
log.warn("Output not found:", outputName);
if (callback) {
callback(false, "Output not found");
}