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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
226
quickshell/Services/Log.qml
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user