1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-30 09:32:05 -04:00

logger: add a dedicated QML logging Singleton

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

View File

@@ -26,6 +26,17 @@ var runCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
daemon, _ := cmd.Flags().GetBool("daemon")
session, _ := cmd.Flags().GetBool("session")
if v, _ := cmd.Flags().GetString("log-level"); v != "" {
if err := os.Setenv("DMS_LOG_LEVEL", v); err != nil {
log.Fatalf("Failed to set DMS_LOG_LEVEL: %v", err)
}
}
if v, _ := cmd.Flags().GetString("log-file"); v != "" {
if err := os.Setenv("DMS_LOG_FILE", v); err != nil {
log.Fatalf("Failed to set DMS_LOG_FILE: %v", err)
}
}
log.ApplyEnvOverrides()
if daemon {
runShellDaemon(session)
} else {

View File

@@ -15,6 +15,8 @@ func init() {
runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode")
runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process")
runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)")
runCmd.Flags().String("log-level", "", "Log level: debug, info, warn, error, fatal (overrides DMS_LOG_LEVEL)")
runCmd.Flags().String("log-file", "", "Append logs to this file in addition to stderr (overrides DMS_LOG_FILE)")
runCmd.Flags().MarkHidden("daemon-child")
greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd)

View File

@@ -15,6 +15,8 @@ func init() {
runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode")
runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process")
runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)")
runCmd.Flags().String("log-level", "", "Log level: debug, info, warn, error, fatal (overrides DMS_LOG_LEVEL)")
runCmd.Flags().String("log-file", "", "Append logs to this file in addition to stderr (overrides DMS_LOG_FILE)")
runCmd.Flags().MarkHidden("daemon-child")
greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd)

View File

@@ -80,6 +80,16 @@ func getRuntimeDir() string {
return os.TempDir()
}
func appendLogEnv(env []string) []string {
if v := os.Getenv("DMS_LOG_LEVEL"); v != "" {
env = append(env, "DMS_LOG_LEVEL="+v)
}
if v := os.Getenv("DMS_LOG_FILE"); v != "" {
env = append(env, "DMS_LOG_FILE="+v)
}
return env
}
func hasSystemdRun() bool {
_, err := exec.LookPath("systemd-run")
return err == nil
@@ -216,6 +226,8 @@ func runShellInteractive(session bool) {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb")
}
cmd.Env = appendLogEnv(cmd.Env)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -459,6 +471,8 @@ func runShellDaemon(session bool) {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb")
}
cmd.Env = appendLogEnv(cmd.Env)
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
log.Fatalf("Error opening /dev/null: %v", err)

View File

@@ -66,12 +66,12 @@ require (
github.com/go-git/go-git/v6 v6.0.0-alpha.2
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/mattn/go-isatty v0.0.22
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/muesli/termenv v0.16.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/afero v1.15.0

View File

@@ -1,12 +1,16 @@
package log
import (
"io"
"os"
"regexp"
"strings"
"sync"
"github.com/charmbracelet/lipgloss"
cblog "github.com/charmbracelet/log"
"github.com/mattn/go-isatty"
"github.com/muesli/termenv"
)
// Logger embeds the Charm Logger and adds Printf/Fatalf
@@ -21,8 +25,26 @@ func (l *Logger) Fatalf(format string, v ...any) { l.Logger.Fatalf(format, v...)
var (
logger *Logger
initLogger sync.Once
logMu sync.Mutex
logFile *os.File
logStderr io.Writer = os.Stderr
ansiRe = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
)
// ansiStripWriter strips ANSI escape sequences before forwarding to w. Used
// for the file sink so colored stderr stays colored while the file stays plain.
type ansiStripWriter struct{ w io.Writer }
func (a *ansiStripWriter) Write(p []byte) (int, error) {
stripped := ansiRe.ReplaceAll(p, nil)
if _, err := a.w.Write(stripped); err != nil {
return 0, err
}
return len(p), nil
}
func parseLogLevel(level string) cblog.Level {
switch strings.ToLower(level) {
case "debug":
@@ -86,7 +108,7 @@ func GetLogger() *Logger {
SetString(" DEBUG").
Foreground(lipgloss.Color("4"))
base := cblog.New(os.Stderr)
base := cblog.New(logStderr)
base.SetStyles(styles)
base.SetReportTimestamp(false)
@@ -98,10 +120,85 @@ func GetLogger() *Logger {
base.SetPrefix(" go")
logger = &Logger{base}
if path := os.Getenv("DMS_LOG_FILE"); path != "" {
_ = SetLogFile(path)
}
})
return logger
}
// SetLevel updates the active log level. Accepts the same strings as
// DMS_LOG_LEVEL. Unknown values default to info.
func SetLevel(level string) {
GetLogger().SetLevel(parseLogLevel(level))
}
// SetLogFile makes the logger append to path in addition to stderr. Passing an
// empty string detaches the file sink. Atomic per-line writes (≤PIPE_BUF) on
// O_APPEND keep concurrent Go and QML writers from corrupting each other.
//
// Color handling: charmbracelet/log auto-detects color support from its
// io.Writer, and io.MultiWriter doesn't pass that through, so we force the ANSI
// profile when stderr is a TTY and route the file through ansiStripWriter so
// the file stays plain while stderr keeps its colors.
func SetLogFile(path string) error {
logMu.Lock()
defer logMu.Unlock()
if logFile != nil {
logFile.Close()
logFile = nil
}
l := GetLogger()
if path == "" {
l.SetOutput(logStderr)
applyColorProfile(l, logStderr)
return nil
}
f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
if err != nil {
return err
}
logFile = f
out := io.MultiWriter(logStderr, &ansiStripWriter{w: f})
l.SetOutput(out)
applyColorProfile(l, logStderr)
return nil
}
// applyColorProfile forces the renderer's color profile to match what stderr
// would produce on its own, undoing the auto-downgrade triggered by wrapping
// stderr in a non-TTY writer (e.g. io.MultiWriter).
func applyColorProfile(l *Logger, stderr io.Writer) {
f, ok := stderr.(*os.File)
if !ok {
l.SetColorProfile(termenv.Ascii)
return
}
if isatty.IsTerminal(f.Fd()) {
l.SetColorProfile(termenv.ANSI)
return
}
l.SetColorProfile(termenv.Ascii)
}
// ApplyEnvOverrides re-reads DMS_LOG_LEVEL and DMS_LOG_FILE and reconfigures
// the singleton. Safe to call after CLI flags have rewritten the environment.
func ApplyEnvOverrides() {
GetLogger()
if level := os.Getenv("DMS_LOG_LEVEL"); level != "" {
SetLevel(level)
}
if path := os.Getenv("DMS_LOG_FILE"); path != "" {
if err := SetLogFile(path); err != nil {
Warnf("Failed to open log file %q: %v", path, err)
}
}
}
// * Convenience wrappers
func Debug(msg any, keyvals ...any) { GetLogger().Debug(msg, keyvals...) }

View File

@@ -5,9 +5,11 @@ import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("CacheData")
readonly property int cacheConfigVersion: 1
@@ -131,7 +133,7 @@ Singleton {
}
}
} catch (e) {
console.warn("CacheData: Failed to parse cache:", e.message);
log.warn("Failed to parse cache:", e.message);
} finally {
_loading = false;
}
@@ -149,7 +151,7 @@ Singleton {
}
function migrateFromUndefinedToV1(cache) {
console.info("CacheData: Migrating configuration from undefined to version 1");
log.info("Migrating configuration from undefined to version 1");
}
function cleanupUnusedKeys() {
@@ -164,7 +166,7 @@ Singleton {
for (const key in cache) {
if (!validKeys.includes(key)) {
console.log("CacheData: Removing unused key:", key);
log.debug("Removing unused key:", key);
delete cache[key];
needsSave = true;
}
@@ -174,7 +176,7 @@ Singleton {
cacheFile.setText(JSON.stringify(cache, null, 2));
}
} catch (e) {
console.warn("CacheData: Failed to cleanup unused keys:", e.message);
log.warn("Failed to cleanup unused keys:", e.message);
}
}
@@ -184,7 +186,7 @@ Singleton {
if (content && content.trim())
return JSON.parse(content);
} catch (e) {
console.warn("CacheData: Failed to parse launcher cache:", e.message);
log.warn("Failed to parse launcher cache:", e.message);
}
return null;
}
@@ -220,7 +222,7 @@ Singleton {
}
onLoadFailed: error => {
if (!isGreeterMode) {
console.info("CacheData: No cache file found, starting fresh");
log.info("No cache file found, starting fresh");
}
}
}

View File

@@ -5,9 +5,11 @@ import QtQuick
import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("I18n")
property string _resolvedLocale: "en"
@@ -54,15 +56,15 @@ Singleton {
try {
root.translations = JSON.parse(text());
root.translationsLoaded = true;
console.info(`I18n: Loaded translations for '${root._resolvedLocale}' (${Object.keys(root.translations).length} contexts)`);
log.info(`I18n: Loaded translations for '${root._resolvedLocale}' (${Object.keys(root.translations).length} contexts)`);
} catch (e) {
console.warn(`I18n: Error parsing '${root._resolvedLocale}':`, e, "- falling back to English");
log.warn(`I18n: Error parsing '${root._resolvedLocale}':`, e, "- falling back to English");
root._fallbackToEnglish();
}
}
onLoadFailed: error => {
console.warn(`I18n: Failed to load '${root._resolvedLocale}' (${error}), ` + "falling back to English");
log.warn(`I18n: Failed to load '${root._resolvedLocale}' (${error}), ` + "falling back to English");
root._fallbackToEnglish();
}
}
@@ -105,14 +107,14 @@ Singleton {
_selectedPath = fileUrl;
translationsLoaded = false;
translations = ({});
console.info(`I18n: Using locale '${localeTag}' from ${fileUrl}`);
log.info(`I18n: Using locale '${localeTag}' from ${fileUrl}`);
}
function _fallbackToEnglish() {
_selectedPath = "";
translationsLoaded = false;
translations = ({});
console.warn("I18n: Falling back to built-in English strings");
log.warn("Falling back to built-in English strings");
}
function tr(term, context) {

View File

@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("Proc")
readonly property int noTimeout: -1
property int defaultDebounceMs: 50
@@ -112,7 +114,7 @@ Singleton {
const safeExitCode = exitCodeValue !== null && exitCodeValue !== undefined ? exitCodeValue : -1;
entry.callback(safeOutput, safeExitCode);
} catch (e) {
console.warn("runCommand callback error for command:", entry.command, "Error:", e);
log.warn("runCommand callback error for command:", entry.command, "Error:", e);
}
}
try {

View File

@@ -12,6 +12,7 @@ import "settings/SessionStore.js" as Store
Singleton {
id: root
readonly property var log: Log.scoped("SessionData")
readonly property int sessionConfigVersion: 3
@@ -257,7 +258,7 @@ Singleton {
} catch (e) {
_parseError = true;
const msg = e.message;
console.error("SessionData: Failed to parse session.json - file will not be overwritten.");
log.error("Failed to parse session.json - file will not be overwritten.");
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse session.json"), msg));
}
}
@@ -337,7 +338,7 @@ Singleton {
} catch (e) {
_parseError = true;
const msg = e.message;
console.error("SessionData: Failed to parse session.json - file will not be overwritten.");
log.error("Failed to parse session.json - file will not be overwritten.");
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse session.json"), msg));
}
}
@@ -552,7 +553,7 @@ Singleton {
}
if (!screen) {
console.warn("SessionData: Screen not found");
log.warn("Screen not found");
return;
}
@@ -649,7 +650,7 @@ Singleton {
}
if (!screen) {
console.warn("SessionData: Screen not found");
log.warn("Screen not found");
return;
}
@@ -680,7 +681,7 @@ Singleton {
}
if (!screen) {
console.warn("SessionData: Screen not found");
log.warn("Screen not found");
return;
}
@@ -711,7 +712,7 @@ Singleton {
}
if (!screen) {
console.warn("SessionData: Screen not found");
log.warn("Screen not found");
return;
}
@@ -742,7 +743,7 @@ Singleton {
}
if (!screen) {
console.warn("SessionData: Screen not found");
log.warn("Screen not found");
return;
}

View File

@@ -13,6 +13,7 @@ import "settings/SettingsStore.js" as Store
Singleton {
id: root
readonly property var log: Log.scoped("SettingsData")
readonly property int settingsConfigVersion: 5
@@ -1294,7 +1295,7 @@ Singleton {
} catch (e) {
_parseError = true;
const msg = e.message;
console.error("SettingsData: Failed to parse settings.json - file will not be overwritten. Error:", msg);
log.error("Failed to parse settings.json - file will not be overwritten. Error:", msg);
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse settings.json"), msg));
applyStoredTheme();
} finally {
@@ -1315,12 +1316,12 @@ Singleton {
if (_isReadOnly) {
_hasUnsavedChanges = _checkForUnsavedChanges();
if (!wasReadOnly)
console.info("SettingsData: settings.json is now read-only");
log.info("settings.json is now read-only");
} else {
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
_hasUnsavedChanges = false;
if (wasReadOnly)
console.info("SettingsData: settings.json is now writable");
log.info("settings.json is now writable");
if (_pendingMigration)
settingsFile.setText(JSON.stringify(_pendingMigration, null, 2));
}
@@ -1374,7 +1375,7 @@ Singleton {
} catch (e) {
const msg = e.message || String(e);
if (!_isMissingPluginSettingsError(e))
console.warn("SettingsData: Failed to load plugin_settings.json. Error:", msg);
log.warn("Failed to load plugin_settings.json. Error:", msg);
_resetPluginSettings();
}
}
@@ -1391,7 +1392,7 @@ Singleton {
} catch (e) {
_pluginParseError = true;
const msg = e.message;
console.error("SettingsData: Failed to parse plugin_settings.json - file will not be overwritten. Error:", msg);
log.error("Failed to parse plugin_settings.json - file will not be overwritten. Error:", msg);
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse plugin_settings.json"), msg));
pluginSettings = {};
} finally {
@@ -2794,7 +2795,7 @@ Singleton {
} catch (e) {
_parseError = true;
const msg = e.message;
console.error("SettingsData: Failed to reload settings.json - file will not be overwritten. Error:", msg);
log.error("Failed to reload settings.json - file will not be overwritten. Error:", msg);
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse settings.json"), msg));
} finally {
_loading = false;
@@ -2829,7 +2830,7 @@ Singleton {
if (!isGreeterMode) {
const msg = String(error || "");
if (!_isMissingPluginSettingsError(error))
console.warn("SettingsData: Failed to load plugin_settings.json. Error:", msg);
log.warn("Failed to load plugin_settings.json. Error:", msg);
_resetPluginSettings();
}
}

View File

@@ -12,6 +12,7 @@ import "StockThemes.js" as StockThemes
Singleton {
id: root
readonly property var log: Log.scoped("Theme")
readonly property string stateDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericCacheLocation).toString()) + "/DankMaterialShell"
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
@@ -148,7 +149,7 @@ Singleton {
}
if (colorsFileLoadFailed && currentTheme === dynamic && rawWallpaperPath) {
console.info("Theme: Matugen now available, regenerating colors for dynamic theme");
log.info("Matugen now available, regenerating colors for dynamic theme");
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode);
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default";
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot";
@@ -376,7 +377,7 @@ Singleton {
"use": true
}, response => {
if (!response.error) {
console.info("Theme automation: IP location enabled after connection");
log.info("Theme automation: IP location enabled after connection");
}
});
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
@@ -389,13 +390,13 @@ Singleton {
"longitude": SessionData.longitude
}, locationResponse => {
if (locationResponse?.error) {
console.warn("Theme automation: Failed to set location", locationResponse.error);
log.warn("Theme automation: Failed to set location", locationResponse.error);
}
});
}
});
} else {
console.warn("Theme automation: No location configured");
log.warn("Theme automation: No location configured");
}
}
}
@@ -1525,12 +1526,12 @@ Singleton {
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType, stockColors) {
if (!matugenAvailable) {
console.warn("Theme: matugen not available or disabled - cannot set system theme");
log.warn("matugen not available or disabled - cannot set system theme");
return;
}
if (workerRunning) {
console.info("Theme: Worker already running, queueing request");
log.info("Worker already running, queueing request");
pendingThemeRequest = {
kind,
value,
@@ -1542,7 +1543,7 @@ Singleton {
return;
}
console.info("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", stockColors ? "(stock colors)" : "(dynamic)");
log.info("Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", stockColors ? "(stock colors)" : "(dynamic)");
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
NiriService.suppressNextToast();
@@ -1557,7 +1558,7 @@ Singleton {
"runUserTemplates": (typeof SettingsData !== "undefined") ? SettingsData.runUserMatugenTemplates : true
};
console.log("Theme: Starting matugen worker");
log.debug("Starting matugen worker");
workerRunning = true;
const args = ["dms", "matugen", "queue", "--state-dir", stateDir, "--shell-dir", shellDir, "--config-dir", configDir, "--kind", desired.kind, "--value", desired.value, "--mode", desired.mode, "--icon-theme", desired.iconTheme, "--matugen-type", desired.matugenType,];
@@ -1715,7 +1716,7 @@ Singleton {
}
if (!darkTheme || !darkTheme.primary) {
console.warn("Theme data not available for:", currentTheme);
log.warn("Theme data not available for:", currentTheme);
return;
}
@@ -1953,10 +1954,10 @@ Singleton {
id: systemThemeGenerator
running: false
stdout: SplitParser {
onRead: data => console.info("Theme worker:", data)
onRead: data => log.info("Theme worker:", data)
}
stderr: SplitParser {
onRead: data => console.warn("Theme worker:", data)
onRead: data => log.warn("Theme worker:", data)
}
onExited: exitCode => {
@@ -1965,18 +1966,18 @@ Singleton {
switch (exitCode) {
case 0:
console.info("Theme: Matugen worker completed successfully");
log.info("Matugen worker completed successfully");
root.matugenCompleted(currentMode, "success");
break;
case 2:
console.log("Theme: Matugen worker completed with code 2 (no changes needed)");
log.debug("Matugen worker completed with code 2 (no changes needed)");
root.matugenCompleted(currentMode, "no-changes");
break;
default:
if (typeof ToastService !== "undefined") {
ToastService.showError("Theme worker failed (" + exitCode + ")");
}
console.warn("Theme: Matugen worker failed with exit code:", exitCode);
log.warn("Matugen worker failed with exit code:", exitCode);
root.matugenCompleted(currentMode, "error");
}
@@ -1985,7 +1986,7 @@ Singleton {
const req = pendingThemeRequest;
pendingThemeRequest = null;
console.info("Theme: Processing queued theme request");
log.info("Processing queued theme request");
setDesiredTheme(req.kind, req.value, req.isLight, req.iconTheme, req.matugenType, req.stockColors);
}
}
@@ -2039,7 +2040,7 @@ Singleton {
}
}
} catch (e) {
console.error("Theme: Failed to parse dynamic colors:", e);
log.error("Failed to parse dynamic colors:", e);
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error";
ToastService.showError("Dynamic colors parse error: " + e.message);
@@ -2059,11 +2060,11 @@ Singleton {
onLoadFailed: function (error) {
if (currentTheme === dynamic) {
console.warn("Theme: Dynamic colors file load failed, marking for regeneration");
log.warn("Dynamic colors file load failed, marking for regeneration");
colorsFileLoadFailed = true;
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
if (!isGreeterMode && matugenAvailable && rawWallpaperPath) {
console.log("Theme: Matugen available, triggering immediate regeneration");
log.debug("Matugen available, triggering immediate regeneration");
generateSystemThemesFromCurrentTheme();
}
}
@@ -2187,7 +2188,7 @@ Singleton {
"endMinute": endMinute
}, response => {
if (response && response.error) {
console.error("Theme automation: Failed to sync time schedule:", response.error);
log.error("Theme automation: Failed to sync time schedule:", response.error);
}
});
@@ -2280,9 +2281,9 @@ Singleton {
if (root.themeModeAutomationActive) {
if (SessionData.nightModeUseIPLocation) {
console.warn("Theme automation: Waiting for IP location from backend");
log.warn("Theme automation: Waiting for IP location from backend");
} else {
console.warn("Theme automation: Location mode requires coordinates");
log.warn("Theme automation: Location mode requires coordinates");
}
}
}
@@ -2364,7 +2365,7 @@ Singleton {
"use": true
}, response => {
if (response?.error) {
console.warn("Theme automation: Failed to enable IP location", response.error);
log.warn("Theme automation: Failed to enable IP location", response.error);
}
});
return true;
@@ -2378,7 +2379,7 @@ Singleton {
"longitude": SessionData.longitude
}, locResp => {
if (locResp?.error) {
console.warn("Theme automation: Failed to set location", locResp.error);
log.warn("Theme automation: Failed to set location", locResp.error);
}
});
}

View File

@@ -27,6 +27,7 @@ import qs.Services
Item {
id: root
readonly property var log: Log.scoped("DMSShell")
property bool osdSurfacesLoaded: true
property int pendingOsdResumeReloads: 0
@@ -54,7 +55,7 @@ Item {
item.popoutService = PopoutService;
}
item.pluginId = pluginId;
console.info("Daemon plugin loaded:", pluginId);
log.info("Daemon plugin loaded:", pluginId);
}
}
}
@@ -93,7 +94,7 @@ Item {
}
onFadeCancelled: {
console.log("Fade to lock cancelled by user on screen:", fadeWindowLoader.modelData.name);
log.debug("Fade to lock cancelled by user on screen:", fadeWindowLoader.modelData.name);
}
}
@@ -133,7 +134,7 @@ Item {
}
onFadeCancelled: {
console.log("Fade to DPMS cancelled by user on screen:", fadeDpmsWindowLoader.modelData.name);
log.debug("Fade to DPMS cancelled by user on screen:", fadeDpmsWindowLoader.modelData.name);
}
}
@@ -773,7 +774,7 @@ Item {
cmd += " " + escapedPath;
}
console.log("FilePicker: Launching", cmd);
log.debug("FilePicker: Launching", cmd);
Quickshell.execDetached({
command: ["sh", "-c", cmd]
@@ -805,10 +806,10 @@ Item {
}
function onAppPickerRequested(data) {
console.log("DMSShell: App picker requested with data:", JSON.stringify(data));
log.debug("App picker requested with data:", JSON.stringify(data));
if (!data || !data.target) {
console.warn("DMSShell: Invalid app picker request data");
log.warn("Invalid app picker request data");
return;
}

View File

@@ -9,6 +9,7 @@ import qs.Modules.Settings.DisplayConfig
Item {
id: root
readonly property var log: Log.scoped("DMSShellIPC")
required property var powerMenuModalLoader
required property var processListModalLoader
@@ -861,7 +862,7 @@ Item {
function set(key: string, value: string): string {
if (!(key in SettingsData)) {
console.warn("Cannot set property, not found:", key);
log.warn("Cannot set property, not found:", key);
return "SETTINGS_INVALID_KEY";
}
@@ -894,12 +895,12 @@ Item {
throw "Unsupported type";
}
console.warn("Setting:", key, value);
log.warn("Setting:", key, value);
SettingsData[key] = value;
SettingsData.saveSettings();
return "SETTINGS_SET_SUCCESS";
} catch (e) {
console.warn("Failed to set property:", key, "error:", e);
log.warn("Failed to set property:", key, "error:", e);
return "SETTINGS_SET_FAILURE";
}
}

View File

@@ -1,5 +1,4 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Modals.Common
import qs.Widgets
@@ -7,6 +6,7 @@ import qs.Services
DankModal {
id: root
readonly property var log: Log.scoped("AppPickerModal")
property string title: I18n.tr("Select Application")
property string targetData: ""
@@ -30,52 +30,52 @@ DankModal {
onBackgroundClicked: close()
onDialogClosed: {
searchQuery = ""
selectedIndex = 0
keyboardNavigationActive = false
searchQuery = "";
selectedIndex = 0;
keyboardNavigationActive = false;
}
onOpened: {
searchQuery = ""
updateApplicationList()
selectedIndex = 0
searchQuery = "";
updateApplicationList();
selectedIndex = 0;
Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.searchField) {
contentLoader.item.searchField.text = ""
contentLoader.item.searchField.forceActiveFocus()
contentLoader.item.searchField.text = "";
contentLoader.item.searchField.forceActiveFocus();
}
})
});
}
function updateApplicationList() {
applicationsModel.clear()
const apps = AppSearchService.applications
const usageHistory = usageHistoryKey && SettingsData[usageHistoryKey] ? SettingsData[usageHistoryKey] : {}
let filteredApps = []
applicationsModel.clear();
const apps = AppSearchService.applications;
const usageHistory = usageHistoryKey && SettingsData[usageHistoryKey] ? SettingsData[usageHistoryKey] : {};
let filteredApps = [];
for (const app of apps) {
if (!app || !app.categories) continue
let matchesCategory = categoryFilter.length === 0
if (!app || !app.categories)
continue;
let matchesCategory = categoryFilter.length === 0;
if (categoryFilter.length > 0) {
try {
for (const cat of app.categories) {
if (categoryFilter.includes(cat)) {
matchesCategory = true
break
matchesCategory = true;
break;
}
}
} catch (e) {
console.warn("AppPicker: Error iterating categories for", app.name, ":", e)
continue
log.warn("AppPicker: Error iterating categories for", app.name, ":", e);
continue;
}
}
if (matchesCategory) {
const name = app.name || ""
const lowerName = name.toLowerCase()
const lowerQuery = searchQuery.toLowerCase()
const name = app.name || "";
const lowerName = name.toLowerCase();
const lowerQuery = searchQuery.toLowerCase();
if (searchQuery === "" || lowerName.includes(lowerQuery)) {
filteredApps.push({
@@ -84,21 +84,21 @@ DankModal {
exec: app.exec || app.execString || "",
startupClass: app.startupWMClass || "",
appData: app
})
});
}
}
}
filteredApps.sort((a, b) => {
const aId = a.appData.id || a.appData.execString || a.appData.exec || ""
const bId = b.appData.id || b.appData.execString || b.appData.exec || ""
const aUsage = usageHistory[aId] ? usageHistory[aId].count : 0
const bUsage = usageHistory[bId] ? usageHistory[bId].count : 0
const aId = a.appData.id || a.appData.execString || a.appData.exec || "";
const bId = b.appData.id || b.appData.execString || b.appData.exec || "";
const aUsage = usageHistory[aId] ? usageHistory[aId].count : 0;
const bUsage = usageHistory[bId] ? usageHistory[bId].count : 0;
if (aUsage !== bUsage) {
return bUsage - aUsage
return bUsage - aUsage;
}
return (a.name || "").localeCompare(b.name || "")
})
return (a.name || "").localeCompare(b.name || "");
});
filteredApps.forEach(app => {
applicationsModel.append({
@@ -107,10 +107,10 @@ DankModal {
exec: app.exec,
startupClass: app.startupClass,
appId: app.appData.id || app.appData.execString || app.appData.exec || ""
})
})
});
});
console.log("AppPicker: Found " + filteredApps.length + " applications")
log.debug("AppPicker: Found " + filteredApps.length + " applications");
}
onSearchQueryChanged: updateApplicationList()
@@ -129,56 +129,57 @@ DankModal {
focus: true
Keys.onEscapePressed: event => {
root.close()
event.accepted = true
root.close();
event.accepted = true;
}
Keys.onPressed: event => {
if (applicationsModel.count === 0) return
if (applicationsModel.count === 0)
return;
// Toggle view mode with Tab key
if (event.key === Qt.Key_Tab) {
root.viewMode = root.viewMode === "grid" ? "list" : "grid"
event.accepted = true
return
root.viewMode = root.viewMode === "grid" ? "list" : "grid";
event.accepted = true;
return;
}
if (root.viewMode === "grid") {
if (event.key === Qt.Key_Left) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.max(0, root.selectedIndex - 1)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.max(0, root.selectedIndex - 1);
event.accepted = true;
} else if (event.key === Qt.Key_Right) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + 1)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + 1);
event.accepted = true;
} else if (event.key === Qt.Key_Up) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.max(0, root.selectedIndex - root.gridColumns)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.max(0, root.selectedIndex - root.gridColumns);
event.accepted = true;
} else if (event.key === Qt.Key_Down) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + root.gridColumns)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + root.gridColumns);
event.accepted = true;
}
} else {
if (event.key === Qt.Key_Up) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.max(0, root.selectedIndex - 1)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.max(0, root.selectedIndex - 1);
event.accepted = true;
} else if (event.key === Qt.Key_Down) {
root.keyboardNavigationActive = true
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + 1)
event.accepted = true
root.keyboardNavigationActive = true;
root.selectedIndex = Math.min(applicationsModel.count - 1, root.selectedIndex + 1);
event.accepted = true;
}
}
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (root.selectedIndex >= 0 && root.selectedIndex < applicationsModel.count) {
const app = applicationsModel.get(root.selectedIndex)
launchApplication(app)
const app = applicationsModel.get(root.selectedIndex);
launchApplication(app);
}
event.accepted = true
event.accepted = true;
}
}
@@ -217,7 +218,7 @@ DankModal {
iconColor: root.viewMode === "list" ? Theme.primary : Theme.surfaceText
backgroundColor: root.viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
onClicked: {
root.viewMode = "list"
root.viewMode = "list";
}
}
@@ -229,7 +230,7 @@ DankModal {
iconColor: root.viewMode === "grid" ? Theme.primary : Theme.surfaceText
backgroundColor: root.viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
onClicked: {
root.viewMode = "grid"
root.viewMode = "grid";
}
}
}
@@ -257,42 +258,42 @@ DankModal {
keyForwardTargets: [appContent]
onTextEdited: {
root.searchQuery = text
root.searchQuery = text;
}
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
root.close()
event.accepted = true
return
root.close();
event.accepted = true;
return;
}
const isEnterKey = [Qt.Key_Return, Qt.Key_Enter].includes(event.key)
const hasText = text.length > 0
const isEnterKey = [Qt.Key_Return, Qt.Key_Enter].includes(event.key);
const hasText = text.length > 0;
if (isEnterKey && hasText) {
if (root.keyboardNavigationActive && applicationsModel.count > 0) {
const app = applicationsModel.get(root.selectedIndex)
launchApplication(app)
const app = applicationsModel.get(root.selectedIndex);
launchApplication(app);
} else if (applicationsModel.count > 0) {
const app = applicationsModel.get(0)
launchApplication(app)
const app = applicationsModel.get(0);
launchApplication(app);
}
event.accepted = true
return
event.accepted = true;
return;
}
const navigationKeys = [Qt.Key_Down, Qt.Key_Up, Qt.Key_Left, Qt.Key_Right, Qt.Key_Tab, Qt.Key_Backtab]
const isNavigationKey = navigationKeys.includes(event.key)
const isEmptyEnter = isEnterKey && !hasText
const navigationKeys = [Qt.Key_Down, Qt.Key_Up, Qt.Key_Left, Qt.Key_Right, Qt.Key_Tab, Qt.Key_Backtab];
const isNavigationKey = navigationKeys.includes(event.key);
const isEmptyEnter = isEnterKey && !hasText;
event.accepted = !(isNavigationKey || isEmptyEnter)
event.accepted = !(isNavigationKey || isEmptyEnter);
}
Connections {
function onShouldBeVisibleChanged() {
if (!root.shouldBeVisible) {
searchField.focus = false
searchField.focus = false;
}
}
@@ -303,12 +304,12 @@ DankModal {
Rectangle {
width: parent.width
height: {
let usedHeight = 40 + Theme.spacingS
usedHeight += 52 + Theme.spacingS
let usedHeight = 40 + Theme.spacingS;
usedHeight += 52 + Theme.spacingS;
if (root.showTargetData) {
usedHeight += 36 + Theme.spacingS
usedHeight += 36 + Theme.spacingS;
}
return parent.height - usedHeight
return parent.height - usedHeight;
}
radius: Theme.cornerRadius
color: "transparent"
@@ -320,14 +321,14 @@ DankModal {
property int itemSpacing: Theme.spacingS
function ensureVisible(index) {
if (index < 0 || index >= count) return
const itemY = index * (itemHeight + itemSpacing)
const itemBottom = itemY + itemHeight
if (index < 0 || index >= count)
return;
const itemY = index * (itemHeight + itemSpacing);
const itemBottom = itemY + itemHeight;
if (itemY < contentY) {
contentY = itemY
contentY = itemY;
} else if (itemBottom > contentY + height) {
contentY = itemBottom - height
contentY = itemBottom - height;
}
}
@@ -343,9 +344,9 @@ DankModal {
spacing: itemSpacing
onCurrentIndexChanged: {
root.selectedIndex = currentIndex
root.selectedIndex = currentIndex;
if (root.keyboardNavigationActive) {
ensureVisible(currentIndex)
ensureVisible(currentIndex);
}
}
@@ -360,11 +361,11 @@ DankModal {
hoverUpdatesSelection: true
onItemClicked: (idx, modelData) => {
launchApplication(modelData)
launchApplication(modelData);
}
onKeyboardNavigationReset: {
root.keyboardNavigationActive = false
root.keyboardNavigationActive = false;
}
}
}
@@ -373,14 +374,14 @@ DankModal {
id: appGrid
function ensureVisible(index) {
if (index < 0 || index >= count) return
const itemY = Math.floor(index / root.gridColumns) * cellHeight
const itemBottom = itemY + cellHeight
if (index < 0 || index >= count)
return;
const itemY = Math.floor(index / root.gridColumns) * cellHeight;
const itemBottom = itemY + cellHeight;
if (itemY < contentY) {
contentY = itemY
contentY = itemY;
} else if (itemBottom > contentY + height) {
contentY = itemBottom - height
contentY = itemBottom - height;
}
}
@@ -397,9 +398,9 @@ DankModal {
currentIndex: root.selectedIndex
onCurrentIndexChanged: {
root.selectedIndex = currentIndex
root.selectedIndex = currentIndex;
if (root.keyboardNavigationActive) {
ensureVisible(currentIndex)
ensureVisible(currentIndex);
}
}
@@ -413,11 +414,11 @@ DankModal {
hoverUpdatesSelection: true
onItemClicked: (idx, modelData) => {
launchApplication(modelData)
launchApplication(modelData);
}
onKeyboardNavigationReset: {
root.keyboardNavigationActive = false
root.keyboardNavigationActive = false;
}
}
}
@@ -449,22 +450,22 @@ DankModal {
}
function launchApplication(app) {
if (!app) return
root.applicationSelected(app, root.targetData)
if (!app)
return;
root.applicationSelected(app, root.targetData);
if (usageHistoryKey && app.appId) {
const usageHistory = SettingsData[usageHistoryKey] || {}
const currentCount = usageHistory[app.appId] ? usageHistory[app.appId].count : 0
const usageHistory = SettingsData[usageHistoryKey] || {};
const currentCount = usageHistory[app.appId] ? usageHistory[app.appId].count : 0;
usageHistory[app.appId] = {
count: currentCount + 1,
lastUsed: Date.now(),
name: app.name
}
SettingsData.set(usageHistoryKey, usageHistory)
};
SettingsData.set(usageHistoryKey, usageHistory);
}
root.close()
root.close();
}
}
}

View File

@@ -7,6 +7,7 @@ import qs.Widgets
DankModal {
id: root
readonly property var log: Log.scoped("BluetoothPairingModal")
layerNamespace: "dms:bluetooth-pairing"
@@ -24,7 +25,7 @@ DankModal {
property string passkeyInput: ""
function show(pairingData) {
console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData));
log.debug("BluetoothPairingModal.show() called:", JSON.stringify(pairingData));
token = pairingData.token || "";
deviceName = pairingData.deviceName || "";
deviceAddress = pairingData.deviceAddr || "";
@@ -33,7 +34,7 @@ DankModal {
pinInput = "";
passkeyInput = "";
console.log("BluetoothPairingModal: Calling open()");
log.debug("Calling open()");
open();
Qt.callLater(() => {
if (contentLoader.item) {

View File

@@ -2,9 +2,11 @@ import QtQuick
import Quickshell
import qs.Common
import qs.Modals
import qs.Services
AppPickerModal {
id: root
readonly property var log: Log.scoped("BrowserPickerModal")
property string url: ""
@@ -17,35 +19,44 @@ AppPickerModal {
showTargetData: true
function shellEscape(str) {
return "'" + str.replace(/'/g, "'\\''") + "'"
return "'" + str.replace(/'/g, "'\\''") + "'";
}
onApplicationSelected: (app, url) => {
if (!app) return
if (!app)
return;
let cmd = app.exec || "";
const escapedUrl = shellEscape(url);
let cmd = app.exec || ""
const escapedUrl = shellEscape(url)
let hasField = false
if (cmd.includes("%u")) { cmd = cmd.replace("%u", escapedUrl); hasField = true }
else if (cmd.includes("%U")) { cmd = cmd.replace("%U", escapedUrl); hasField = true }
else if (cmd.includes("%f")) { cmd = cmd.replace("%f", escapedUrl); hasField = true }
else if (cmd.includes("%F")) { cmd = cmd.replace("%F", escapedUrl); hasField = true }
cmd = cmd.replace(/%[ikc]/g, "")
if (!hasField) {
cmd += " " + escapedUrl
let hasField = false;
if (cmd.includes("%u")) {
cmd = cmd.replace("%u", escapedUrl);
hasField = true;
} else if (cmd.includes("%U")) {
cmd = cmd.replace("%U", escapedUrl);
hasField = true;
} else if (cmd.includes("%f")) {
cmd = cmd.replace("%f", escapedUrl);
hasField = true;
} else if (cmd.includes("%F")) {
cmd = cmd.replace("%F", escapedUrl);
hasField = true;
}
console.log("BrowserPicker: Launching", cmd)
cmd = cmd.replace(/%[ikc]/g, "");
if (!hasField) {
cmd += " " + escapedUrl;
}
log.debug("BrowserPicker: Launching", cmd);
Quickshell.execDetached({
command: ["sh", "-c", cmd]
})
});
}
onViewModeChanged: {
SettingsData.set("browserPickerViewMode", viewMode)
SettingsData.set("browserPickerViewMode", viewMode);
}
}

View File

@@ -6,6 +6,7 @@ import qs.Widgets
Item {
id: thumbnail
readonly property var log: Log.scoped("ClipboardThumbnail")
required property var entry
required property string entryType
@@ -52,7 +53,7 @@ Item {
modal.activeImageLoads--;
}
if (response.error) {
console.warn("ClipboardThumbnail: Failed to load image:", entry.id);
log.warn("Failed to load image:", entry.id);
return;
}
const data = response.result?.data;

View File

@@ -7,6 +7,7 @@ import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("DankModal")
property string layerNamespace: "dms:modal"
property alias content: contentLoader.sourceComponent
@@ -246,10 +247,10 @@ Item {
return WlrLayershell.Overlay;
switch (Quickshell.env("DMS_MODAL_LAYER")) {
case "bottom":
console.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
log.error("'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "background":
console.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
log.error("'background' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "overlay":
return WlrLayershell.Overlay;

View File

@@ -9,6 +9,7 @@ import qs.Widgets
DankModal {
id: root
readonly property var log: Log.scoped("DankColorPickerModal")
layerNamespace: "dms:color-picker"
@@ -111,7 +112,7 @@ DankModal {
hideInstant();
Proc.runCommand("dms-color-pick", ["dms", "color", "pick", "--json"], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("dms color pick exited with code:", exitCode);
log.warn("dms color pick exited with code:", exitCode);
root.show();
return;
}
@@ -120,11 +121,11 @@ DankModal {
if (result.hex) {
applyPickedColor(result.hex);
} else {
console.warn("Failed to parse dms color pick output: missing hex");
log.warn("Failed to parse dms color pick output: missing hex");
root.show();
}
} catch (e) {
console.warn("Failed to parse dms color pick JSON:", e);
log.warn("Failed to parse dms color pick JSON:", e);
root.show();
}
}, 0, Proc.noTimeout);

View File

@@ -8,6 +8,7 @@ import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("DankLauncherV2Modal")
visible: false
@@ -323,10 +324,10 @@ Item {
WlrLayershell.layer: {
switch (Quickshell.env("DMS_MODAL_LAYER")) {
case "bottom":
console.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
log.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "background":
console.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
log.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "overlay":
return WlrLayershell.Overlay;

View File

@@ -1,10 +1,12 @@
import QtQuick
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("GreeterDoctorPage")
property bool isRunning: false
property bool hasRun: false
@@ -228,9 +230,7 @@ Item {
text: {
if (root.errorCount === 0)
return I18n.tr("All checks passed", "greeter doctor page success");
return root.errorCount === 1
? I18n.tr("%1 issue found", "greeter doctor page error count").arg(root.errorCount)
: I18n.tr("%1 issues found", "greeter doctor page error count").arg(root.errorCount);
return root.errorCount === 1 ? I18n.tr("%1 issue found", "greeter doctor page error count").arg(root.errorCount) : I18n.tr("%1 issues found", "greeter doctor page error count").arg(root.errorCount);
}
font.pixelSize: Theme.fontSizeMedium
color: root.errorCount > 0 ? Theme.error : Theme.surfaceVariantText
@@ -412,7 +412,7 @@ Item {
else
root.selectedFilter = "ok";
} catch (e) {
console.error("GreeterDoctorPage: Failed to parse doctor output:", e);
log.error("Failed to parse doctor output:", e);
}
}
}

View File

@@ -7,6 +7,7 @@ import qs.Widgets
FloatingWindow {
id: root
readonly property var log: Log.scoped("GreeterModal")
property bool disablePopupTransparency: true
property int currentPage: 0
@@ -105,7 +106,7 @@ FloatingWindow {
root.cheatsheetData = JSON.parse(trimmed);
root.cheatsheetLoaded = true;
} catch (e) {
console.warn("Greeter: Failed to parse cheatsheet:", e);
log.warn("Greeter: Failed to parse cheatsheet:", e);
}
}
}

View File

@@ -9,6 +9,7 @@ import qs.Widgets
FloatingWindow {
id: processListModal
readonly property var log: Log.scoped("ProcessListModal")
property bool disablePopupTransparency: true
property int currentTab: 0
@@ -22,7 +23,7 @@ FloatingWindow {
function show() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available");
log.warn("dgop is not available");
return;
}
visible = true;
@@ -36,7 +37,7 @@ FloatingWindow {
function toggle() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available");
log.warn("dgop is not available");
return;
}
visible = !visible;
@@ -44,7 +45,7 @@ FloatingWindow {
function focusOrToggle() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available");
log.warn("dgop is not available");
return;
}
if (visible) {

View File

@@ -7,6 +7,7 @@ import qs.Widgets
FloatingWindow {
id: root
readonly property var log: Log.scoped("WorkspaceRenameModal")
property bool disablePopupTransparency: true
readonly property int inputFieldHeight: Theme.fontSizeMedium + Theme.spacingL * 2
@@ -39,7 +40,7 @@ FloatingWindow {
} else if (CompositorService.isHyprland) {
HyprlandService.renameWorkspace(name);
} else {
console.warn("WorkspaceRenameModal: rename not supported for this compositor");
log.warn("rename not supported for this compositor");
}
}

View File

@@ -7,6 +7,7 @@ import "../utils/layout.js" as LayoutUtils
Column {
id: root
readonly property var log: Log.scoped("DragDropGrid")
property bool editMode: false
property string expandedSection: ""
@@ -988,7 +989,7 @@ Column {
return true;
}
} catch (e) {
console.warn("DragDropGrid: stale plugin component for", pluginId, "- reloading");
log.warn("stale plugin component for", pluginId, "- reloading");
PluginService.reloadPlugin(pluginId);
}
return false;

View File

@@ -6,6 +6,7 @@ import "../utils/widgets.js" as WidgetUtils
QtObject {
id: root
readonly property var log: Log.scoped("WidgetModel")
property var vpnBuiltinInstance: null
property var cupsBuiltinInstance: null
@@ -26,7 +27,7 @@ QtObject {
const widgets = SettingsData.controlCenterWidgets || [];
const hasVpnWidget = widgets.some(w => w.id === "builtin_vpn");
if (!hasVpnWidget && vpnLoader.active) {
console.log("VpnWidget: No VPN widget in control center, deactivating loader");
log.debug("VpnWidget: No VPN widget in control center, deactivating loader");
vpnLoader.active = false;
}
}
@@ -55,7 +56,7 @@ QtObject {
const widgets = SettingsData.controlCenterWidgets || [];
const hasCupsWidget = widgets.some(w => w.id === "builtin_cups");
if (!hasCupsWidget && cupsLoader.active) {
console.log("CupsWidget: No CUPS widget in control center, deactivating loader");
log.debug("CupsWidget: No CUPS widget in control center, deactivating loader");
cupsLoader.active = false;
}
}

View File

@@ -1,9 +1,11 @@
import QtQuick
import qs.Common
import qs.Modules.ControlCenter.Widgets
import qs.Services
CompoundPill {
id: root
readonly property var log: Log.scoped("ColorPickerPill")
property var colorPickerModal: null
@@ -14,14 +16,14 @@ CompoundPill {
secondaryText: I18n.tr("Choose a color")
onToggled: {
console.log("ColorPickerPill toggled, modal:", colorPickerModal);
log.debug("ColorPickerPill toggled, modal:", colorPickerModal);
if (colorPickerModal) {
colorPickerModal.show();
}
}
onExpandClicked: {
console.log("ColorPickerPill expandClicked, modal:", colorPickerModal);
log.debug("ColorPickerPill expandClicked, modal:", colorPickerModal);
if (colorPickerModal) {
colorPickerModal.show();
}

View File

@@ -7,6 +7,7 @@ import qs.Services
PanelWindow {
id: barWindow
readonly property var log: Log.scoped("DankBarWindow")
required property var rootWindow
required property var barConfig
@@ -164,7 +165,7 @@ PanelWindow {
barWindow.BackgroundEffect.blurRegion = region;
barWindow.blurRegion = region;
} catch (e) {
console.warn("BarBlur: Failed to create blur region:", e);
log.warn("BarBlur: Failed to create blur region:", e);
}
}
@@ -534,11 +535,11 @@ PanelWindow {
Connections {
target: PluginService
function onPluginLoaded(pluginId) {
console.info("DankBar: Plugin loaded:", pluginId);
log.info("DankBar: Plugin loaded:", pluginId);
SettingsData.widgetDataChanged();
}
function onPluginUnloaded(pluginId) {
console.info("DankBar: Plugin unloaded:", pluginId);
log.info("DankBar: Plugin unloaded:", pluginId);
SettingsData.widgetDataChanged();
}
}

View File

@@ -9,6 +9,7 @@ import qs.Widgets
BasePill {
id: root
readonly property var log: Log.scoped("AppsDock")
enableBackgroundHover: false
enableCursor: false
@@ -550,9 +551,9 @@ BasePill {
showBadge: root.showOverflowBadge
z: 10
onClicked: {
console.log("Overflow button clicked! Current state:", root.overflowExpanded);
log.debug("Overflow button clicked! Current state:", root.overflowExpanded);
root.overflowExpanded = !root.overflowExpanded;
console.log("New state:", root.overflowExpanded);
log.debug("New state:", root.overflowExpanded);
}
}

View File

@@ -7,6 +7,7 @@ import qs.Widgets
BasePill {
id: battery
readonly property var log: Log.scoped("Battery")
property bool batteryPopupVisible: false
property var popoutTarget: null
@@ -130,13 +131,13 @@ BasePill {
// Check if this is a touchpad
if (delta !== 120 && delta !== -120) {
touchpadAccumulator += delta;
console.info("Acc: "+touchpadAccumulator);
log.info("Acc: " + touchpadAccumulator);
if (Math.abs(touchpadAccumulator) < 500)
return;
delta = touchpadAccumulator;
touchpadAccumulator = 0;
}
console.info("Trigger! Delta: "+delta)
log.info("Trigger! Delta: " + delta);
// This is after the other delta checks so it only shows on valid Y scroll
if (typeof PowerProfiles === "undefined") {
@@ -149,11 +150,14 @@ BasePill {
var index = profiles.findIndex(profile => PowerProfiles.profile === profile);
// Step once based on mouse wheel direction
if (delta > 0) index += 1;
else index -= 1;
if (delta > 0)
index += 1;
else
index -= 1;
// Already at end of list, can't go further
if (index < 0 || index >= profiles.length) return;
if (index < 0 || index >= profiles.length)
return;
// Set new profile
PowerProfiles.profile = profiles[index];

View File

@@ -6,6 +6,7 @@ import qs.Widgets
Rectangle {
id: root
readonly property var log: Log.scoped("CalendarOverviewCard")
implicitWidth: SettingsData.showWeekNumber ? 736 : 700
@@ -521,7 +522,7 @@ Rectangle {
onClicked: {
if (modelData.url && modelData.url !== "") {
if (Qt.openUrlExternally(modelData.url) === false) {
console.warn("Failed to open URL: " + modelData.url);
log.warn("Failed to open URL: " + modelData.url);
} else {
root.closeDash();
}

View File

@@ -7,6 +7,7 @@ import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("WeatherTab")
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
@@ -45,7 +46,7 @@ Item {
hourlyList.currentIndex = Math.max(0, Math.min((WeatherService.weather.hourlyForecast?.length ?? 1) - 1, WeatherService.calendarHourDifference((new Date()), date) + (new Date()).getHours()));
}
} catch (e) {
console.warn("Weather Date Sync Error:", e);
log.warn("Weather Date Sync Error:", e);
}
syncing = false;

View File

@@ -5,9 +5,11 @@ import QtQuick
import Quickshell
import Quickshell.Io
import "GreetdEnv.js" as GreetdEnv
import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("GreetdMemory")
readonly property string greetCfgDir: Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter"
readonly property string sessionConfigPath: greetCfgDir + "/session.json"
@@ -42,7 +44,7 @@ Singleton {
nightModeEnabled = config.nightModeEnabled !== undefined ? config.nightModeEnabled : false;
}
} catch (e) {
console.warn("Failed to parse greeter session config:", e);
log.warn("Failed to parse greeter session config:", e);
}
}
@@ -56,7 +58,7 @@ Singleton {
if (!rememberLastSession || !rememberLastUser)
saveMemory();
} catch (e) {
console.warn("Failed to parse greetd memory:", e);
log.warn("Failed to parse greetd memory:", e);
}
}
@@ -122,7 +124,7 @@ Singleton {
parseSessionConfig(sessionConfigFileView.text());
}
onLoadFailed: error => {
console.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
log.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
}
}
}

View File

@@ -5,10 +5,12 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import "GreetdEnv.js" as GreetdEnv
Singleton {
id: root
readonly property var log: Log.scoped("GreetdSettings")
readonly property string configPath: {
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter";
@@ -81,8 +83,7 @@ Singleton {
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "purple";
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "";
registryThemeVariants = settings.registryThemeVariants !== undefined ?
settings.registryThemeVariants : ({});
registryThemeVariants = settings.registryThemeVariants !== undefined ? settings.registryThemeVariants : ({});
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot";
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false;
@@ -142,7 +143,7 @@ Singleton {
Theme.applyGreeterTheme(currentThemeName);
}
} catch (e) {
console.warn("Failed to parse greetd settings:", e);
log.warn("Failed to parse greetd settings:", e);
} finally {
settingsLoaded = true;
}
@@ -192,7 +193,7 @@ Singleton {
parseSettings(settingsFile.text());
}
onLoadFailed: error => {
console.warn("Failed to load greetd settings:", error);
log.warn("Failed to load greetd settings:", error);
root.parseSettings("");
}
}

View File

@@ -1,9 +1,11 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Services
Item {
id: keyboard_controller
readonly property var log: Log.scoped("KeyboardController")
// reference on the TextInput
property Item target
@@ -14,21 +16,20 @@ Item {
function show() {
if (!isKeyboardActive && keyboard === null) {
keyboard = keyboardComponent.createObject(
keyboard_controller.rootObject)
keyboard.target = keyboard_controller.target
keyboard.dismissed.connect(hide)
isKeyboardActive = true
keyboard = keyboardComponent.createObject(keyboard_controller.rootObject);
keyboard.target = keyboard_controller.target;
keyboard.dismissed.connect(hide);
isKeyboardActive = true;
} else
console.log("The keyboard is already shown")
log.debug("The keyboard is already shown");
}
function hide() {
if (isKeyboardActive && keyboard !== null) {
keyboard.destroy()
isKeyboardActive = false
keyboard.destroy();
isKeyboardActive = false;
} else
console.log("The keyboard is already hidden")
log.debug("The keyboard is already hidden");
}
// private

View File

@@ -14,6 +14,7 @@ import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("LockScreenContent")
function encodeFileUrl(path) {
if (!path)
@@ -95,9 +96,9 @@ Item {
if (SessionService.loginctlAvailable && DMSService.apiVersion >= 2) {
DMSService.sendRequest("loginctl.lockerReady", null, resp => {
if (resp?.error)
console.warn("lockerReady failed:", resp.error);
log.warn("lockerReady failed:", resp.error);
else
console.log("lockerReady sent (afterAnimating/afterRendering)");
log.debug("lockerReady sent (afterAnimating/afterRendering)");
});
}
}
@@ -803,7 +804,7 @@ Item {
}
if (pam.passwd.active) {
console.log("PAM is active, ignoring input");
log.debug("PAM is active, ignoring input");
event.accepted = true;
return;
}
@@ -1622,7 +1623,7 @@ Item {
buttonSize: 40
onClicked: {
if (demoMode) {
console.log("Demo: Power Menu");
log.debug("Demo: Power Menu");
} else {
powerMenu.show();
}

View File

@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Services
PanelWindow {
id: root
readonly property var log: Log.scoped("LockScreenDemo")
property bool demoActive: false
@@ -25,12 +27,12 @@ PanelWindow {
color: "transparent"
function showDemo(): void {
console.log("Showing lock screen demo");
log.debug("Showing lock screen demo");
demoActive = true;
}
function hideDemo(): void {
console.log("Hiding lock screen demo");
log.debug("Hiding lock screen demo");
demoActive = false;
}

View File

@@ -7,6 +7,7 @@ import qs.Services
Item {
id: root
readonly property var log: Log.scoped("VideoScreensaver")
required property string screenName
property bool active: false
@@ -53,7 +54,7 @@ Item {
onExited: exitCode => {
if (exitCode !== 0 || !videoPicker.result) {
console.warn("VideoScreensaver: no video found in folder");
log.warn("no video found in folder");
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("No video found in folder"));
root.dismiss();
}
@@ -98,14 +99,14 @@ Item {
`, background, "VideoScreensaver.VideoPlayer");
videoPlayer.errorOccurred.connect((error, errorString) => {
console.warn("VideoScreensaver: playback error:", errorString);
log.warn("playback error:", errorString);
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("Playback error: ") + errorString);
root.dismiss();
});
return true;
} catch (e) {
console.warn("VideoScreensaver: Failed to create video player:", e);
log.warn("Failed to create video player:", e);
return false;
}
}

View File

@@ -1,10 +1,12 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("PluginSettings")
required property string pluginId
property var pluginService: null
@@ -131,7 +133,7 @@ Item {
return;
}
if (!hasPermission) {
console.warn("PluginSettings: Plugin", pluginId, "does not have settings_write permission");
log.warn("Plugin", pluginId, "does not have settings_write permission");
return;
}
if (pluginService.savePluginData) {

View File

@@ -9,6 +9,7 @@ import qs.Services
Singleton {
id: root
readonly property var log: Log.scoped("DisplayConfigState")
readonly property bool hasOutputBackend: WlrOutputService.wlrOutputAvailable
readonly property var wlrOutputs: WlrOutputService.outputs
@@ -106,7 +107,7 @@ Singleton {
function findMatchingProfile() {
const profiles = validatedProfiles;
console.log("[Profile Match] Current outputs:", JSON.stringify(currentOutputSet));
log.debug("[Profile Match] Current outputs:", JSON.stringify(currentOutputSet));
let bestMatch = "";
let bestScore = -1;
@@ -116,25 +117,25 @@ Singleton {
const profile = profiles[profileId];
const profileSet = new Set(profile.outputSet);
console.log("[Profile Match] Checking", profile.name, "outputSet:", JSON.stringify(profile.outputSet));
log.debug("[Profile Match] Checking", profile.name, "outputSet:", JSON.stringify(profile.outputSet));
let allCurrentPresent = true;
for (const output of currentOutputSet) {
if (!profileSet.has(output)) {
console.log("[Profile Match] - Missing output:", output);
log.debug("[Profile Match] - Missing output:", output);
allCurrentPresent = false;
break;
}
}
if (!allCurrentPresent) {
console.log("[Profile Match] - SKIP: not all current outputs present");
log.debug("[Profile Match] - SKIP: not all current outputs present");
continue;
}
const disconnectedCount = profile.outputSet.length - currentOutputSet.length;
const score = currentOutputSet.length * 100 - disconnectedCount;
const updatedAt = profile.updatedAt || profile.createdAt || 0;
console.log("[Profile Match] - MATCH score:", score, "(disconnected:", disconnectedCount, "updatedAt:", updatedAt + ")");
log.debug("[Profile Match] - MATCH score:", score, "(disconnected:", disconnectedCount, "updatedAt:", updatedAt + ")");
if (score > bestScore || (score === bestScore && updatedAt > bestUpdatedAt)) {
bestScore = score;
@@ -142,7 +143,7 @@ Singleton {
bestUpdatedAt = updatedAt;
}
}
console.log("[Profile Match] Best match:", bestMatch, "score:", bestScore);
log.debug("[Profile Match] Best match:", bestMatch, "score:", bestScore);
return bestMatch;
}

View File

@@ -6,6 +6,7 @@ import qs.Services
Column {
id: root
readonly property var log: Log.scoped("WidgetsTabSection")
property var items: []
property var allWidgets: []
@@ -1726,11 +1727,11 @@ Column {
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
onOpened: {
console.log("Privacy context menu opened");
log.debug("Privacy context menu opened");
}
onClosed: {
console.log("Privacy Center context menu closed");
log.debug("Privacy Center context menu closed");
}
background: Rectangle {

View File

@@ -7,6 +7,7 @@ import qs.Widgets
import qs.Services
Variants {
readonly property var log: Log.scoped("WallpaperBackground")
model: {
if (SessionData.isGreeterMode) {
return Quickshell.screens;
@@ -103,7 +104,7 @@ Variants {
function _recheckScreenScale() {
const newScale = CompositorService.getScreenScale(modelData);
if (newScale !== root.screenScale) {
console.info("WallpaperBackground: screen scale corrected for", modelData.name + ":", root.screenScale, "->", newScale);
log.info("screen scale corrected for", modelData.name + ":", root.screenScale, "->", newScale);
root.screenScale = newScale;
}
}

View File

@@ -8,6 +8,7 @@ import qs.Widgets
Item {
id: root
readonly property var log: Log.scoped("OverviewWidget")
required property var panelWindow
required property bool overviewOpen
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
@@ -276,7 +277,7 @@ Item {
}
return result;
} catch (e) {
console.error("OverviewWidget filter error:", e);
log.error("OverviewWidget filter error:", e);
return [];
}
}

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
@@ -19,206 +18,217 @@ Singleton {
readonly property bool enhancedPairingAvailable: DMSService.dmsAvailable && DMSService.apiVersion >= 9 && DMSService.capabilities.includes("bluetooth")
readonly property bool connected: {
if (!adapter || !adapter.devices) {
return false
return false;
}
let isConnected = false
adapter.devices.values.forEach(dev => { if (dev.connected) isConnected = true })
return isConnected
let isConnected = false;
adapter.devices.values.forEach(dev => {
if (dev.connected)
isConnected = true;
});
return isConnected;
}
readonly property var pairedDevices: {
if (!adapter || !adapter.devices) {
return []
return [];
}
return adapter.devices.values.filter(dev => {
return dev && (dev.paired || dev.trusted)
})
return dev && (dev.paired || dev.trusted);
});
}
readonly property var allDevicesWithBattery: {
if (!adapter || !adapter.devices) {
return []
return [];
}
return adapter.devices.values.filter(dev => {
return dev && dev.batteryAvailable && dev.battery > 0
})
return dev && dev.batteryAvailable && dev.battery > 0;
});
}
function sortDevices(devices) {
return devices.sort((a, b) => {
const aName = a.name || a.deviceName || ""
const bName = b.name || b.deviceName || ""
const aAddr = a.address || ""
const bAddr = b.address || ""
const aName = a.name || a.deviceName || "";
const bName = b.name || b.deviceName || "";
const aAddr = a.address || "";
const bAddr = b.address || "";
const aHasRealName = aName.includes(" ") && aName.length > 3
const bHasRealName = bName.includes(" ") && bName.length > 3
const aHasRealName = aName.includes(" ") && aName.length > 3;
const bHasRealName = bName.includes(" ") && bName.length > 3;
if (aHasRealName && !bHasRealName) return -1
if (!aHasRealName && bHasRealName) return 1
if (aHasRealName && !bHasRealName)
return -1;
if (!aHasRealName && bHasRealName)
return 1;
if (aHasRealName && bHasRealName) {
return aName.localeCompare(bName)
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
return c.profile === profile;
})) {
let newCodecs = codecQueryProcess.availableCodecs.slice()
let newCodecs = codecQueryProcess.availableCodecs.slice();
newCodecs.push({
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
})
codecQueryProcess.availableCodecs = newCodecs
});
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
return c.profile === profile;
})) {
let newCodecs = codecFullQueryProcess.availableCodecs.slice()
let newCodecs = codecFullQueryProcess.availableCodecs.slice();
newCodecs.push({
"name": codecInfo.name,
"profile": profile,
"description": codecInfo.description,
"qualityColor": codecInfo.qualityColor
})
codecFullQueryProcess.availableCodecs = newCodecs
});
codecFullQueryProcess.availableCodecs = newCodecs;
}
}
}
@@ -503,7 +513,7 @@ 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
@@ -511,13 +521,13 @@ Singleton {
if (root.adapter && root.adapter.devices) {
root.adapter.devices.values.forEach(device => {
if (device && root.getCardName(device) === cardName) {
Qt.callLater(() => root.refreshDeviceCodec(device))
Qt.callLater(() => root.refreshDeviceCodec(device));
}
})
});
}
}
callback = null
callback = null;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,8 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,7 @@ import qs.Services
PanelWindow {
id: root
readonly property var log: Log.scoped("DankOSD")
property string blurNamespace: "dms:osd"
WlrLayershell.namespace: blurNamespace
@@ -94,10 +95,10 @@ PanelWindow {
WlrLayershell.layer: {
switch (Quickshell.env("DMS_OSD_LAYER")) {
case "bottom":
console.warn("DankOSD: 'bottom' layer is not valid for OSDs. Defaulting to 'overlay' layer.");
log.warn("'bottom' layer is not valid for OSDs. Defaulting to 'overlay' layer.");
return WlrLayershell.Overlay;
case "background":
console.warn("DankOSD: 'background' layer is not valid for OSDs. Defaulting to 'overlay' layer.");
log.warn("'background' layer is not valid for OSDs. Defaulting to 'overlay' layer.");
return WlrLayershell.Overlay;
case "top":
return WlrLayershell.Top;

View File

@@ -6,6 +6,7 @@ import qs.Services
Item {
id: root
readonly property var log: Log.scoped("DankPopout")
property string layerNamespace: "dms:popout"
property alias content: contentLoader.sourceComponent
@@ -414,10 +415,10 @@ Item {
WlrLayershell.layer: {
switch (Quickshell.env("DMS_POPOUT_LAYER")) {
case "bottom":
console.warn("DankPopout: 'bottom' layer is not valid for popouts. Defaulting to 'top' layer.");
log.warn("'bottom' layer is not valid for popouts. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "background":
console.warn("DankPopout: 'background' layer is not valid for popouts. Defaulting to 'top' layer.");
log.warn("'background' layer is not valid for popouts. Defaulting to 'top' layer.");
return WlrLayershell.Top;
case "overlay":
return WlrLayershell.Overlay;

View File

@@ -11,6 +11,7 @@ import "../Common/KeybindActions.js" as Actions
Item {
id: root
readonly property var log: Log.scoped("KeybindItem")
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
@@ -716,7 +717,7 @@ Item {
const key = KeyUtils.xkbKeyFromQtKey(qtKey);
if (!key) {
console.warn("[KeybindItem] Unknown key:", event.key, "mods:", event.modifiers);
log.warn("Unknown key:", event.key, "mods:", event.modifiers);
return;
}

View File

@@ -3,24 +3,25 @@ import qs.Services
Item {
id: root
readonly property var log: Log.scoped("PluginGlobalVar")
required property string varName
property var defaultValue: undefined
readonly property var value: {
const pid = parent?.pluginId ?? ""
const pid = parent?.pluginId ?? "";
if (!pid || !PluginService.globalVars[pid]) {
return defaultValue
return defaultValue;
}
return PluginService.globalVars[pid][varName] ?? defaultValue
return PluginService.globalVars[pid][varName] ?? defaultValue;
}
function set(newValue) {
const pid = parent?.pluginId ?? ""
const pid = parent?.pluginId ?? "";
if (pid) {
PluginService.setGlobalVar(pid, varName, newValue)
PluginService.setGlobalVar(pid, varName, newValue);
} else {
console.warn("PluginGlobalVar: Cannot set", varName, "- no pluginId from parent")
log.warn("Cannot set", varName, "- no pluginId from parent");
}
}