mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-18 09:14:01 -04:00
perf: gate idle work behind feature and capability state
This commit is contained in:
@@ -125,7 +125,7 @@ Singleton {
|
||||
path: StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/appusage.json"
|
||||
blockLoading: true
|
||||
blockWrites: true
|
||||
watchChanges: true
|
||||
watchChanges: SettingsData.launcherHistoryEnabled
|
||||
onLoaded: {
|
||||
if (root._saving)
|
||||
return;
|
||||
|
||||
@@ -12,15 +12,16 @@ QtObject {
|
||||
if (wanted === _held)
|
||||
return;
|
||||
_held = wanted;
|
||||
if (modules === null) {
|
||||
if (typeof service.addRef !== "function") {
|
||||
service.refCount += wanted ? 1 : -1;
|
||||
return;
|
||||
}
|
||||
const held = modules ?? [];
|
||||
if (wanted) {
|
||||
service.addRef(modules);
|
||||
service.addRef(held);
|
||||
return;
|
||||
}
|
||||
service.removeRef(modules);
|
||||
service.removeRef(held);
|
||||
}
|
||||
|
||||
onActiveChanged: sync(active)
|
||||
|
||||
@@ -64,9 +64,15 @@ Singleton {
|
||||
return "";
|
||||
}
|
||||
|
||||
function probeTerminals() {
|
||||
if (terminalProbe.running || installedTerminals.length > 0)
|
||||
return;
|
||||
terminalProbe.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: terminalProbe
|
||||
running: true
|
||||
running: false
|
||||
command: ["sh", "-c", "for t in ghostty kitty foot alacritty wezterm konsole gnome-terminal xterm; do command -v \"$t\" >/dev/null 2>&1 && echo \"$t\"; done"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
@@ -1856,7 +1862,7 @@ Singleton {
|
||||
FileView {
|
||||
id: greeterSessionFile
|
||||
|
||||
path: root.greeterSessionBaseDir ? (root.greeterSessionBaseDir + "/session.json") : ""
|
||||
path: isGreeterMode && root.greeterSessionBaseDir ? (root.greeterSessionBaseDir + "/session.json") : ""
|
||||
preload: isGreeterMode
|
||||
blockLoading: false
|
||||
blockWrites: true
|
||||
|
||||
@@ -785,6 +785,10 @@ Singleton {
|
||||
property bool lockAtStartup: Spec.SPEC.lockAtStartup.def
|
||||
|
||||
property bool enableFprint: Spec.SPEC.enableFprint.def
|
||||
onEnableFprintChanged: {
|
||||
if (enableFprint)
|
||||
refreshAuthAvailability();
|
||||
}
|
||||
property int maxFprintTries: Spec.SPEC.maxFprintTries.def
|
||||
readonly property bool fprintdAvailable: Processes.fprintdAvailable
|
||||
readonly property bool lockFingerprintCanEnable: Processes.lockFingerprintCanEnable
|
||||
@@ -795,6 +799,10 @@ Singleton {
|
||||
readonly property string greeterFingerprintReason: Processes.greeterFingerprintReason
|
||||
readonly property string greeterFingerprintSource: Processes.greeterFingerprintSource
|
||||
property bool enableU2f: Spec.SPEC.enableU2f.def
|
||||
onEnableU2fChanged: {
|
||||
if (enableU2f)
|
||||
refreshAuthAvailability();
|
||||
}
|
||||
property string u2fMode: Spec.SPEC.u2fMode.def
|
||||
readonly property bool u2fAvailable: Processes.u2fAvailable
|
||||
readonly property bool lockU2fCanEnable: Processes.lockU2fCanEnable
|
||||
@@ -1404,8 +1412,6 @@ Singleton {
|
||||
Processes.settingsRoot = root;
|
||||
loadSettings();
|
||||
initializeListModels();
|
||||
refreshAuthAvailability();
|
||||
Processes.checkPluginSettings();
|
||||
}
|
||||
|
||||
function applyStoredTheme() {
|
||||
@@ -3795,7 +3801,7 @@ Singleton {
|
||||
FileView {
|
||||
id: greeterSettingsFile
|
||||
|
||||
path: root.greeterSettingsBaseDir ? (root.greeterSettingsBaseDir + "/settings.json") : ""
|
||||
path: isGreeterMode && root.greeterSettingsBaseDir ? (root.greeterSettingsBaseDir + "/settings.json") : ""
|
||||
preload: isGreeterMode
|
||||
blockLoading: false
|
||||
blockWrites: true
|
||||
@@ -3823,13 +3829,16 @@ Singleton {
|
||||
onLoaded: {
|
||||
if (isGreeterMode)
|
||||
return;
|
||||
pluginSettingsFileExists = true;
|
||||
parsePluginSettings(pluginSettingsFile.text());
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
if (isGreeterMode)
|
||||
return;
|
||||
const msg = String(error || "");
|
||||
if (!_isMissingPluginSettingsError(error))
|
||||
const missing = _isMissingPluginSettingsError(error);
|
||||
pluginSettingsFileExists = !missing;
|
||||
if (!missing)
|
||||
log.warn("Failed to load plugin_settings.json. Error:", msg);
|
||||
_resetPluginSettings();
|
||||
}
|
||||
|
||||
@@ -773,8 +773,12 @@ Singleton {
|
||||
return false;
|
||||
if (entry.clipboardOnly && (!DMSService.isConnected || DMSService.apiVersion < 23))
|
||||
return false;
|
||||
if (entry.greeterOnly)
|
||||
GreeterService.ensureDetected();
|
||||
if (entry.greeterOnly && !GreeterService.available)
|
||||
return false;
|
||||
if (entry.autostartOnly)
|
||||
DesktopService.ensureProbed();
|
||||
if (entry.autostartOnly && !DesktopService.autostartAvailable)
|
||||
return false;
|
||||
if (entry.cellularOnly && (NetworkService.cellularDevices?.length ?? 0) === 0)
|
||||
|
||||
@@ -120,7 +120,6 @@ Singleton {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", stateDir]);
|
||||
Proc.runCommand("matugenCheck", ["sh", "-c", "command -v matugen"], (output, code) => {
|
||||
matugenAvailable = (code === 0) && !envDisableMatugen;
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
|
||||
@@ -64,9 +64,10 @@ Singleton {
|
||||
|
||||
readonly property string homeDir: Quickshell.env("HOME") || ""
|
||||
readonly property string u2fKeysPath: homeDir ? homeDir + "/.config/Yubico/u2f_keys" : ""
|
||||
readonly property bool homeU2fKeysDetected: u2fKeysPath !== "" && u2fKeysWatcher.loaded && u2fKeysText.trim() !== ""
|
||||
property bool authProbeRequested: false
|
||||
readonly property bool homeU2fKeysDetected: u2fKeysPath !== "" && (authWatchers.item?.u2fKeys.loaded ?? false) && u2fKeysText.trim() !== ""
|
||||
readonly property bool lockU2fCustomConfigDetected: PamStack.moduleEnabled(dankshellU2fPamText, "pam_u2f")
|
||||
readonly property bool lockU2fCustomSourceDetected: (settingsRoot?.lockU2fPamPath || "") !== "" && customU2fPamWatcher.loaded
|
||||
readonly property bool lockU2fCustomSourceDetected: (settingsRoot?.lockU2fPamPath || "") !== "" && (authWatchers.item?.customU2fPam.loaded ?? false)
|
||||
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd")
|
||||
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
||||
|
||||
@@ -250,9 +251,10 @@ Singleton {
|
||||
readonly property var _pamProbeCommand: ["sh", "-c", "for module in pam_fprintd.so pam_u2f.so; do found=false; for dir in /usr/lib64/security /usr/lib/security /lib/security /lib/x86_64-linux-gnu/security /usr/lib/x86_64-linux-gnu/security /usr/lib/aarch64-linux-gnu/security /run/current-system/sw/lib/security; do if [ -f \"$dir/$module\" ]; then found=true; break; fi; done; printf '%s:%s\\n' \"$module\" \"$found\"; done"]
|
||||
|
||||
function detectAuthCapabilities() {
|
||||
authProbeRequested = true;
|
||||
// FileView cannot watch paths that do not exist yet, so reload the U2F PAM
|
||||
dankshellU2fPamWatcher.reload();
|
||||
u2fKeysWatcher.reload();
|
||||
authWatchers.item.dankshellU2fPam.reload();
|
||||
authWatchers.item.u2fKeys.reload();
|
||||
|
||||
if (forcedFprintAvailable === null) {
|
||||
fingerprintProbeFinalized = false;
|
||||
@@ -397,10 +399,6 @@ Singleton {
|
||||
return PamStack.stackHasModule(greetdPamText, includedPamStacks, moduleName);
|
||||
}
|
||||
|
||||
function checkPluginSettings() {
|
||||
pluginSettingsCheckProcess.running = true;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: authApplyDebounce
|
||||
interval: 300
|
||||
@@ -541,102 +539,101 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: greetdPamWatcher
|
||||
path: "/etc/pam.d/greetd"
|
||||
printErrors: false
|
||||
onLoaded: root.greetdPamText = text()
|
||||
onLoadFailed: root.greetdPamText = ""
|
||||
}
|
||||
Loader {
|
||||
id: authWatchers
|
||||
active: root.authProbeRequested
|
||||
sourceComponent: Item {
|
||||
property alias dankshellU2fPam: dankshellU2fPamWatcher
|
||||
property alias customU2fPam: customU2fPamWatcher
|
||||
property alias u2fKeys: u2fKeysWatcher
|
||||
|
||||
FileView {
|
||||
id: systemAuthPamWatcher
|
||||
path: "/etc/pam.d/system-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.systemAuthPamText = text()
|
||||
onLoadFailed: root.systemAuthPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: greetdPamWatcher
|
||||
path: "/etc/pam.d/greetd"
|
||||
printErrors: false
|
||||
onLoaded: root.greetdPamText = text()
|
||||
onLoadFailed: root.greetdPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: commonAuthPamWatcher
|
||||
path: "/etc/pam.d/common-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.commonAuthPamText = text()
|
||||
onLoadFailed: root.commonAuthPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: systemAuthPamWatcher
|
||||
path: "/etc/pam.d/system-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.systemAuthPamText = text()
|
||||
onLoadFailed: root.systemAuthPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: passwordAuthPamWatcher
|
||||
path: "/etc/pam.d/password-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.passwordAuthPamText = text()
|
||||
onLoadFailed: root.passwordAuthPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: commonAuthPamWatcher
|
||||
path: "/etc/pam.d/common-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.commonAuthPamText = text()
|
||||
onLoadFailed: root.commonAuthPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: systemLoginPamWatcher
|
||||
path: "/etc/pam.d/system-login"
|
||||
printErrors: false
|
||||
onLoaded: root.systemLoginPamText = text()
|
||||
onLoadFailed: root.systemLoginPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: passwordAuthPamWatcher
|
||||
path: "/etc/pam.d/password-auth"
|
||||
printErrors: false
|
||||
onLoaded: root.passwordAuthPamText = text()
|
||||
onLoadFailed: root.passwordAuthPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: systemLocalLoginPamWatcher
|
||||
path: "/etc/pam.d/system-local-login"
|
||||
printErrors: false
|
||||
onLoaded: root.systemLocalLoginPamText = text()
|
||||
onLoadFailed: root.systemLocalLoginPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: systemLoginPamWatcher
|
||||
path: "/etc/pam.d/system-login"
|
||||
printErrors: false
|
||||
onLoaded: root.systemLoginPamText = text()
|
||||
onLoadFailed: root.systemLoginPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: commonAuthPcPamWatcher
|
||||
path: "/etc/pam.d/common-auth-pc"
|
||||
printErrors: false
|
||||
onLoaded: root.commonAuthPcPamText = text()
|
||||
onLoadFailed: root.commonAuthPcPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: systemLocalLoginPamWatcher
|
||||
path: "/etc/pam.d/system-local-login"
|
||||
printErrors: false
|
||||
onLoaded: root.systemLocalLoginPamText = text()
|
||||
onLoadFailed: root.systemLocalLoginPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: loginPamWatcher
|
||||
path: "/etc/pam.d/login"
|
||||
printErrors: false
|
||||
onLoaded: root.loginPamText = text()
|
||||
onLoadFailed: root.loginPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: commonAuthPcPamWatcher
|
||||
path: "/etc/pam.d/common-auth-pc"
|
||||
printErrors: false
|
||||
onLoaded: root.commonAuthPcPamText = text()
|
||||
onLoadFailed: root.commonAuthPcPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: dankshellU2fPamWatcher
|
||||
path: "/etc/pam.d/dankshell-u2f"
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onLoaded: root.dankshellU2fPamText = text()
|
||||
onLoadFailed: root.dankshellU2fPamText = ""
|
||||
}
|
||||
FileView {
|
||||
id: loginPamWatcher
|
||||
path: "/etc/pam.d/login"
|
||||
printErrors: false
|
||||
onLoaded: root.loginPamText = text()
|
||||
onLoadFailed: root.loginPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: customU2fPamWatcher
|
||||
path: root.settingsRoot?.lockU2fPamPath || ""
|
||||
printErrors: false
|
||||
}
|
||||
FileView {
|
||||
id: dankshellU2fPamWatcher
|
||||
path: "/etc/pam.d/dankshell-u2f"
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onLoaded: root.dankshellU2fPamText = text()
|
||||
onLoadFailed: root.dankshellU2fPamText = ""
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: u2fKeysWatcher
|
||||
path: root.u2fKeysPath
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onLoaded: root.u2fKeysText = text()
|
||||
onLoadFailed: root.u2fKeysText = ""
|
||||
}
|
||||
FileView {
|
||||
id: customU2fPamWatcher
|
||||
path: root.settingsRoot?.lockU2fPamPath || ""
|
||||
printErrors: false
|
||||
}
|
||||
|
||||
property var pluginSettingsCheckProcess: Process {
|
||||
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
||||
running: false
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (!settingsRoot)
|
||||
return;
|
||||
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
||||
FileView {
|
||||
id: u2fKeysWatcher
|
||||
path: root.u2fKeysPath
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onLoaded: root.u2fKeysText = text()
|
||||
onLoadFailed: root.u2fKeysText = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -30,11 +30,9 @@ import qs.Services
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("DMSShell")
|
||||
readonly property var _sessionsServiceRef: SessionsService
|
||||
readonly property var _nightModeServiceRef: NightModeService
|
||||
readonly property var _brightnessServiceRef: BrightnessService
|
||||
readonly property var _refreshRateServiceRef: RefreshRateService
|
||||
readonly property var _displayServiceRef: DisplayService
|
||||
|
||||
property var core: null
|
||||
|
||||
@@ -1251,9 +1249,7 @@ Item {
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("toast")
|
||||
|
||||
delegate: Toast {
|
||||
visible: ToastService.toastVisible
|
||||
}
|
||||
delegate: Toast {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -1264,37 +1260,37 @@ Item {
|
||||
sourceComponent: Component {
|
||||
Item {
|
||||
Variants {
|
||||
model: root.legacySystemLevelOsdScreens
|
||||
model: SettingsData.osdVolumeEnabled ? root.legacySystemLevelOsdScreens : []
|
||||
|
||||
delegate: VolumeOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("osd")
|
||||
model: SettingsData.osdMediaVolumeEnabled ? SettingsData.getFilteredScreens("osd") : []
|
||||
|
||||
delegate: MediaVolumeOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("osd")
|
||||
model: SettingsData.osdMediaPlaybackEnabled ? SettingsData.getFilteredScreens("osd") : []
|
||||
|
||||
delegate: MediaPlaybackOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: root.legacySystemLevelOsdScreens
|
||||
model: (SettingsData.osdMicMuteEnabled || SettingsData.osdMicVolumeEnabled) ? root.legacySystemLevelOsdScreens : []
|
||||
|
||||
delegate: MicVolumeOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: root.legacySystemLevelOsdScreens
|
||||
model: SettingsData.osdBrightnessEnabled ? root.legacySystemLevelOsdScreens : []
|
||||
|
||||
delegate: BrightnessOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: root.legacySystemLevelOsdScreens
|
||||
model: SettingsData.osdIdleInhibitorEnabled ? root.legacySystemLevelOsdScreens : []
|
||||
|
||||
delegate: IdleInhibitorOSD {}
|
||||
}
|
||||
@@ -1306,13 +1302,13 @@ Item {
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: root.legacySystemLevelOsdScreens
|
||||
model: SettingsData.osdCapsLockEnabled ? root.legacySystemLevelOsdScreens : []
|
||||
|
||||
delegate: CapsLockOSD {}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("osd")
|
||||
model: SettingsData.osdAudioOutputEnabled ? SettingsData.getFilteredScreens("osd") : []
|
||||
|
||||
delegate: AudioOutputOSD {}
|
||||
}
|
||||
|
||||
@@ -707,6 +707,7 @@ Item {
|
||||
_startDmenuSession(mode.slice("dmenu:".length));
|
||||
return;
|
||||
}
|
||||
DSearchService.ensureChecked();
|
||||
if (appCategory !== "" || Object.keys(collapsedSections).length > 0)
|
||||
_clearModeCache();
|
||||
appCategory = "";
|
||||
|
||||
@@ -8,10 +8,6 @@ import qs.Modules.Plugins
|
||||
PluginComponent {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
readonly property bool vpnActivating: DMSNetworkService.vpnIsBusy || DMSNetworkService.activeState === "activating"
|
||||
readonly property bool vpnActivated: DMSNetworkService.connected && DMSNetworkService.activeState === "activated"
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import "../../../Common/QmlUtils.js" as QmlUtils
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: BatteryService
|
||||
}
|
||||
|
||||
implicitHeight: column.height
|
||||
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
|
||||
@@ -27,6 +27,10 @@ CcTile {
|
||||
|
||||
iconName: "bluetooth"
|
||||
iconBlinking: BluetoothService.connecting
|
||||
|
||||
Ref {
|
||||
service: BatteryService
|
||||
}
|
||||
title: {
|
||||
if (!BluetoothService.available)
|
||||
return I18n.tr("Bluetooth", "bluetooth status");
|
||||
|
||||
@@ -677,7 +677,6 @@ Item {
|
||||
readonly property real surfaceImplicitWidth: isVertical ? hostThickness : 0
|
||||
|
||||
Component.onCompleted: {
|
||||
updateGpuTempConfig();
|
||||
_updateHasMaximizedToplevel();
|
||||
_updateShouldHideForWindows();
|
||||
fitSettle.schedule();
|
||||
@@ -695,27 +694,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function updateGpuTempConfig() {
|
||||
const leftWidgets = barConfig?.leftWidgets || [];
|
||||
const centerWidgets = barConfig?.centerWidgets || [];
|
||||
const rightWidgets = barConfig?.rightWidgets || [];
|
||||
const allWidgets = [...leftWidgets, ...centerWidgets, ...rightWidgets];
|
||||
|
||||
const hasGpuTempWidget = allWidgets.some(widget => {
|
||||
const widgetId = typeof widget === "string" ? widget : widget.id;
|
||||
const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false);
|
||||
return widgetId === "gpuTemp" && widgetEnabled;
|
||||
});
|
||||
|
||||
DgopService.gpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled || SessionData.nonNvidiaGpuTempEnabled;
|
||||
DgopService.nvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled;
|
||||
DgopService.nonNvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nonNvidiaGpuTempEnabled;
|
||||
}
|
||||
|
||||
readonly property var rootWindowBarConfig: rootWindow.barConfig
|
||||
|
||||
onRootWindowBarConfigChanged: {
|
||||
updateGpuTempConfig();
|
||||
_updateHasMaximizedToplevel();
|
||||
_updateShouldHideForWindows();
|
||||
}
|
||||
@@ -732,12 +713,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
readonly property bool sessionNvidiaGpuTempEnabled: SessionData.nvidiaGpuTempEnabled
|
||||
readonly property bool sessionNonNvidiaGpuTempEnabled: SessionData.nonNvidiaGpuTempEnabled
|
||||
|
||||
onSessionNvidiaGpuTempEnabledChanged: updateGpuTempConfig()
|
||||
onSessionNonNvidiaGpuTempEnabledChanged: updateGpuTempConfig()
|
||||
|
||||
readonly property int barPos: barConfig?.position ?? 0
|
||||
|
||||
readonly property bool reserveExclusiveWhenAutoHidden: FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome && !!barWindow.screen && SettingsData.isScreenInPreferences(barWindow.screen, SettingsData.frameScreenPreferences)
|
||||
|
||||
@@ -43,6 +43,7 @@ Item {
|
||||
onSectionsChanged: invalidateCandidateCache()
|
||||
|
||||
onHoverPopoutsEnabledChanged: {
|
||||
invalidateCandidateCache();
|
||||
if (hoverPopoutsEnabled)
|
||||
return;
|
||||
cancelQueuedHitTest();
|
||||
@@ -58,6 +59,7 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: root.widgetOwner || root.barContent
|
||||
enabled: root.hoverPopoutsEnabled
|
||||
|
||||
function onWidthChanged() {
|
||||
root.invalidateCandidateCache();
|
||||
@@ -74,6 +76,7 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: PopoutManager
|
||||
enabled: root.hoverPopoutsEnabled
|
||||
|
||||
function onPopoutChanged() {
|
||||
root._reconcileClosedHoverSurface();
|
||||
@@ -86,6 +89,7 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: BarWidgetService
|
||||
enabled: root.hoverPopoutsEnabled
|
||||
|
||||
function onWidgetRegistered(_widgetId, screenName) {
|
||||
if (screenName === root.barWindow?.screen?.name)
|
||||
@@ -455,6 +459,8 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: root.barWindow?.hostWindow ?? null
|
||||
enabled: root.hoverPopoutsEnabled
|
||||
|
||||
function onMarginsChanged() {
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ DPopout {
|
||||
|
||||
layerNamespace: "dms:vpn"
|
||||
|
||||
Ref {
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property bool wasVisible: false
|
||||
property var triggerScreen: null
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ BasePill {
|
||||
|
||||
opacity: hasActivePrivacy ? 1 : 0
|
||||
|
||||
Ref {
|
||||
service: PrivacyService
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hidden_horizontal"
|
||||
|
||||
@@ -8,10 +8,6 @@ import qs.Widgets
|
||||
BasePill {
|
||||
id: root
|
||||
|
||||
Ref {
|
||||
service: DMSNetworkService
|
||||
}
|
||||
|
||||
property bool isHovered: clickArea.containsMouse
|
||||
|
||||
signal toggleVpnPopup
|
||||
|
||||
@@ -18,6 +18,10 @@ Item {
|
||||
readonly property real textSize: root.tight ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||
readonly property real iconSize: root.textSize + Theme.spacingXS
|
||||
readonly property real statusIconSize: root.textSize + Theme.spacingXXS
|
||||
|
||||
Ref {
|
||||
service: PrivacyService
|
||||
}
|
||||
readonly property real groupSpacing: root.controller.homeSlotMargin
|
||||
readonly property bool isVertical: root.controller.isVertical
|
||||
readonly property string clockDisplay: root.controller.homeClockDisplay
|
||||
|
||||
@@ -87,9 +87,9 @@ Item {
|
||||
Item {
|
||||
id: islandHole
|
||||
|
||||
readonly property var mask: root.body?.inputMaskItem ?? null
|
||||
x: (mask?.x ?? 0) + root.hostOriginX
|
||||
y: (mask?.y ?? 0) + root.hostOriginY
|
||||
readonly property var mask: dismissWindow.visible ? root.body?.inputMaskItem ?? null : null
|
||||
x: mask ? mask.x + root.hostOriginX : 0
|
||||
y: mask ? mask.y + root.hostOriginY : 0
|
||||
width: mask?.width ?? 0
|
||||
height: mask?.height ?? 0
|
||||
}
|
||||
@@ -97,9 +97,9 @@ Item {
|
||||
Item {
|
||||
id: fittsStripHole
|
||||
|
||||
readonly property var strip: root.body?.fittsStripItem ?? null
|
||||
x: (strip?.x ?? 0) + root.hostOriginX
|
||||
y: (strip?.y ?? 0) + root.hostOriginY
|
||||
readonly property var strip: dismissWindow.visible ? root.body?.fittsStripItem ?? null : null
|
||||
x: strip ? strip.x + root.hostOriginX : 0
|
||||
y: strip ? strip.y + root.hostOriginY : 0
|
||||
width: strip?.visible ? strip.width : 0
|
||||
height: strip?.visible ? strip.height : 0
|
||||
}
|
||||
@@ -107,9 +107,9 @@ Item {
|
||||
Item {
|
||||
id: leadingSectionHole
|
||||
|
||||
readonly property var r: root.body?.leadingSectionRect ?? null
|
||||
x: (r?.x ?? 0) + (root.body?.sectionOriginX ?? 0)
|
||||
y: (r?.y ?? 0) + (root.body?.sectionOriginY ?? 0)
|
||||
readonly property var r: dismissWindow.visible ? root.body?.leadingSectionRect ?? null : null
|
||||
x: r ? r.x + root.body.sectionOriginX : 0
|
||||
y: r ? r.y + root.body.sectionOriginY : 0
|
||||
width: r?.w ?? 0
|
||||
height: r?.h ?? 0
|
||||
}
|
||||
@@ -118,9 +118,9 @@ Item {
|
||||
Item {
|
||||
id: centerSectionHole
|
||||
|
||||
readonly property var r: root.body?.centerSectionRect ?? null
|
||||
x: (r?.x ?? 0) + (root.body?.sectionOriginX ?? 0)
|
||||
y: (r?.y ?? 0) + (root.body?.sectionOriginY ?? 0)
|
||||
readonly property var r: dismissWindow.visible ? root.body?.centerSectionRect ?? null : null
|
||||
x: r ? r.x + root.body.sectionOriginX : 0
|
||||
y: r ? r.y + root.body.sectionOriginY : 0
|
||||
width: r?.w ?? 0
|
||||
height: r?.h ?? 0
|
||||
}
|
||||
@@ -128,9 +128,9 @@ Item {
|
||||
Item {
|
||||
id: trailingSectionHole
|
||||
|
||||
readonly property var r: root.body?.trailingSectionRect ?? null
|
||||
x: (r?.x ?? 0) + (root.body?.sectionOriginX ?? 0)
|
||||
y: (r?.y ?? 0) + (root.body?.sectionOriginY ?? 0)
|
||||
readonly property var r: dismissWindow.visible ? root.body?.trailingSectionRect ?? null : null
|
||||
x: r ? r.x + root.body.sectionOriginX : 0
|
||||
y: r ? r.y + root.body.sectionOriginY : 0
|
||||
width: r?.w ?? 0
|
||||
height: r?.h ?? 0
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ QtObject {
|
||||
|
||||
property Connections notificationConnections: Connections {
|
||||
target: NotificationService
|
||||
enabled: root.enabled
|
||||
|
||||
function onVisibleNotificationsChanged() {
|
||||
root.syncVisibleNotifications();
|
||||
|
||||
@@ -159,6 +159,7 @@ QtObject {
|
||||
|
||||
property Connections audioConnection: Connections {
|
||||
target: AudioService.sink?.audio ?? null
|
||||
enabled: root.enabled
|
||||
|
||||
function onVolumeChanged() {
|
||||
if (SettingsData.osdVolumeEnabled)
|
||||
@@ -173,6 +174,7 @@ QtObject {
|
||||
|
||||
property Connections micConnection: Connections {
|
||||
target: AudioService.source?.audio ?? null
|
||||
enabled: root.enabled
|
||||
|
||||
function onMutedChanged() {
|
||||
if (SettingsData.osdMicMuteEnabled)
|
||||
@@ -182,6 +184,7 @@ QtObject {
|
||||
|
||||
property Connections micServiceConnection: Connections {
|
||||
target: AudioService
|
||||
enabled: root.enabled
|
||||
|
||||
function onMicVolumeChanged() {
|
||||
if (SettingsData.osdMicVolumeEnabled)
|
||||
@@ -191,6 +194,7 @@ QtObject {
|
||||
|
||||
property Connections brightnessConnection: Connections {
|
||||
target: BrightnessService
|
||||
enabled: root.enabled
|
||||
|
||||
function onBrightnessChanged(showOsd) {
|
||||
if (showOsd && SettingsData.osdBrightnessEnabled)
|
||||
@@ -200,6 +204,7 @@ QtObject {
|
||||
|
||||
property Connections capsLockConnection: Connections {
|
||||
target: DMSService
|
||||
enabled: root.enabled
|
||||
|
||||
function onCapsLockStateChanged() {
|
||||
if (SettingsData.osdCapsLockEnabled)
|
||||
@@ -209,6 +214,7 @@ QtObject {
|
||||
|
||||
property Connections powerProfileConnection: Connections {
|
||||
target: PowerProfileWatcher
|
||||
enabled: root.enabled
|
||||
|
||||
function onProfileChanged() {
|
||||
if (SettingsData.osdPowerProfileEnabled)
|
||||
@@ -218,6 +224,7 @@ QtObject {
|
||||
|
||||
property Connections idleInhibitorConnection: Connections {
|
||||
target: SessionService
|
||||
enabled: root.enabled
|
||||
|
||||
function onInhibitorChanged() {
|
||||
if (SettingsData.osdIdleInhibitorEnabled)
|
||||
@@ -227,6 +234,7 @@ QtObject {
|
||||
|
||||
property Connections batteryConnection: Connections {
|
||||
target: BatteryService
|
||||
enabled: root.enabled
|
||||
|
||||
function onIsPluggedInChanged() {
|
||||
if (root.chargingPulseEnabled && BatteryService.batteryAvailable)
|
||||
|
||||
@@ -96,7 +96,12 @@ Variants {
|
||||
model: ScriptModel {
|
||||
objectProp: "id"
|
||||
// Reversed so the top of the list maps last and renders in front.
|
||||
values: screenDelegate.rebuilding ? [] : [...(SettingsData.desktopWidgetInstances || [])].reverse()
|
||||
values: {
|
||||
if (screenDelegate.rebuilding)
|
||||
return [];
|
||||
const instances = SettingsData.desktopWidgetInstances || [];
|
||||
return instances.filter(inst => inst.enabled && DesktopWidgetRegistry.showsOnScreen(inst.config?.displayPreferences ?? ["all"], screenDelegate.screen)).reverse();
|
||||
}
|
||||
}
|
||||
|
||||
DesktopPluginWrapper {
|
||||
|
||||
@@ -104,7 +104,7 @@ DOSD {
|
||||
|
||||
Image {
|
||||
id: artPreloader
|
||||
source: TrackArtService.resolvedArtUrl
|
||||
source: SettingsData.osdMediaPlaybackEnabled ? TrackArtService.resolvedArtUrl : ""
|
||||
visible: false
|
||||
asynchronous: true
|
||||
cache: true
|
||||
@@ -149,6 +149,7 @@ DOSD {
|
||||
|
||||
Connections {
|
||||
target: player
|
||||
enabled: SettingsData.osdMediaPlaybackEnabled
|
||||
|
||||
function handleUpdate() {
|
||||
if (!root.player?.trackTitle)
|
||||
|
||||
@@ -86,6 +86,7 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
AudioService.addRef();
|
||||
AudioService.queryMonoSetting(() => {});
|
||||
hiddenOutputDeviceNames = SessionData.hiddenOutputDeviceNames ?? [];
|
||||
hiddenInputDeviceNames = SessionData.hiddenInputDeviceNames ?? [];
|
||||
updateDeviceList();
|
||||
|
||||
@@ -343,6 +343,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
DesktopService.ensureProbed();
|
||||
desktopApps = AppSearchService.getVisibleApplications() || [];
|
||||
autostartInitMkDirComp.createObject(root, {
|
||||
running: true
|
||||
|
||||
@@ -14,6 +14,8 @@ Column {
|
||||
readonly property bool dockHosted: page?.dockHosted ?? false
|
||||
readonly property var apps: page.appStore
|
||||
|
||||
Component.onCompleted: TrashService.detectFileManagers()
|
||||
|
||||
readonly property var activeColorOptions: [({
|
||||
"value": "primary",
|
||||
"label": I18n.tr("Primary")
|
||||
|
||||
@@ -8,6 +8,8 @@ Column {
|
||||
|
||||
property var page: null
|
||||
|
||||
Component.onCompleted: DgopService.ensureMeta()
|
||||
|
||||
readonly property string type: page.widgetType
|
||||
readonly property var gpuLabels: (DgopService.availableGpus ?? []).map(gpu => (gpu.driver ? gpu.driver.toUpperCase() + " " : "") + (gpu.displayName ?? ""))
|
||||
readonly property var mountLabels: {
|
||||
|
||||
@@ -10,6 +10,8 @@ Item {
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
Component.onCompleted: BlurService.probe()
|
||||
|
||||
property var parentModal: null
|
||||
readonly property bool widgetBackgroundEnabled: !(bar.selectedBarConfig?.noBackground ?? false)
|
||||
readonly property var outlineColors: ["surfaceText", "secondary", "primary"]
|
||||
|
||||
@@ -9,6 +9,8 @@ DesktopWidgetInstanceSettings {
|
||||
id: root
|
||||
|
||||
readonly property var gpus: DgopService.availableGpus ?? []
|
||||
|
||||
Component.onCompleted: DgopService.ensureMeta()
|
||||
readonly property var gpuLabels: gpus.map(gpu => (gpu.driver ? gpu.driver.toUpperCase() + " " : "") + (gpu.displayName ?? ""))
|
||||
|
||||
SettingsToggleRow {
|
||||
|
||||
@@ -95,6 +95,7 @@ Item {
|
||||
Component.onDestruction: {
|
||||
_editAlive = false;
|
||||
_editRequest++;
|
||||
KeybindsService.removeRef();
|
||||
}
|
||||
|
||||
function beginEdit(binding, key) {
|
||||
@@ -470,6 +471,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
KeybindsService.addRef();
|
||||
_ensureCurrentProvider();
|
||||
Qt.callLater(_applyRequestedSearch);
|
||||
}
|
||||
|
||||
@@ -56,10 +56,13 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
KeybindsService.addRef();
|
||||
if (KeybindsService.available)
|
||||
KeybindsService.loadBinds(false);
|
||||
}
|
||||
|
||||
Component.onDestruction: KeybindsService.removeRef()
|
||||
|
||||
SettingsPage {
|
||||
id: mainColumn
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ Column {
|
||||
width: parent?.width ?? 0
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Component.onCompleted: CompositorService.refreshDmsWindowFloatingRule()
|
||||
Component.onCompleted: {
|
||||
BlurService.probe();
|
||||
CompositorService.refreshDmsWindowFloatingRule();
|
||||
}
|
||||
|
||||
ConfigInclude {
|
||||
id: windowRulesInclude
|
||||
|
||||
@@ -26,6 +26,8 @@ SettingsDropdownRow {
|
||||
|
||||
currentValue: SessionData.terminalOverride.length > 0 ? SessionData.terminalOverride : autoLabel
|
||||
|
||||
Component.onCompleted: SessionData.probeTerminals()
|
||||
|
||||
onValueChanged: label => {
|
||||
const next = label === autoLabel ? "" : label;
|
||||
SessionData.set("terminalOverride", next);
|
||||
|
||||
@@ -11,6 +11,8 @@ Column {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("WidgetsTabSection")
|
||||
|
||||
Component.onCompleted: DgopService.ensureMeta()
|
||||
|
||||
property var items: []
|
||||
property var allWidgets: []
|
||||
property string title: ""
|
||||
|
||||
@@ -75,18 +75,17 @@ Variants {
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
enabled: SessionData.perModeWallpaper
|
||||
|
||||
function onIsLightModeChanged() {
|
||||
if (SessionData.perModeWallpaper) {
|
||||
var newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
|
||||
if (newSource !== root.source) {
|
||||
root.source = newSource;
|
||||
}
|
||||
}
|
||||
const newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
|
||||
if (newSource !== root.source)
|
||||
root.source = newSource;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NiriService
|
||||
target: CompositorService.isNiri ? NiriService : null
|
||||
enabled: CompositorService.isNiri && root.scrollingEnabled
|
||||
|
||||
function onAllWorkspacesChanged() {
|
||||
|
||||
@@ -25,12 +25,24 @@ Singleton {
|
||||
readonly property string _album: MprisController.activePlayer?.trackAlbum || ""
|
||||
readonly property string _cacheKey: _artist !== "" && _album !== "" ? (_artist + "\n" + _album).toLowerCase() : ""
|
||||
|
||||
property bool _pruned: false
|
||||
|
||||
on_CacheKeyChanged: _schedule()
|
||||
onEnabledChanged: _schedule()
|
||||
Component.onCompleted: {
|
||||
_prune();
|
||||
onEnabledChanged: {
|
||||
_pruneOnce();
|
||||
_schedule();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
_pruneOnce();
|
||||
_schedule();
|
||||
}
|
||||
|
||||
function _pruneOnce() {
|
||||
if (_pruned || !enabled)
|
||||
return;
|
||||
_pruned = true;
|
||||
_prune();
|
||||
}
|
||||
|
||||
// Downloaded covers are a few MB each; drop anything untouched for 30 days so the
|
||||
// cache can't grow without bound.
|
||||
|
||||
@@ -62,8 +62,10 @@ Singleton {
|
||||
target: pipewireBackendLoader.item
|
||||
ignoreUnknownSignals: true
|
||||
function onNodesUpdated() {
|
||||
root.rebuildTypedNodeLists();
|
||||
root.adoptPendingCardSink();
|
||||
if (root.refCount === 0)
|
||||
return;
|
||||
root.rebuildTypedNodeLists();
|
||||
root.deviceRevision++;
|
||||
}
|
||||
}
|
||||
@@ -259,6 +261,8 @@ Singleton {
|
||||
function setDefaultSinkByName(name) {
|
||||
if (!name)
|
||||
return false;
|
||||
if (refCount === 0)
|
||||
rebuildTypedNodeLists();
|
||||
for (const node of typedSinks) {
|
||||
if (node?.name === name)
|
||||
return setSink(node);
|
||||
@@ -269,6 +273,8 @@ Singleton {
|
||||
function setDefaultSourceByName(name) {
|
||||
if (!name)
|
||||
return false;
|
||||
if (refCount === 0)
|
||||
rebuildTypedNodeLists();
|
||||
for (const node of typedSources) {
|
||||
if (node?.name === name)
|
||||
return setSource(node);
|
||||
@@ -893,6 +899,7 @@ EOFCONFIG
|
||||
|
||||
Connections {
|
||||
target: root.sink?.audio ?? null
|
||||
enabled: SettingsData.soundsEnabled && SettingsData.soundVolumeChanged
|
||||
|
||||
function onVolumeChanged() {
|
||||
if (SessionData.suppressOSD)
|
||||
@@ -1688,7 +1695,7 @@ EOFCONFIG
|
||||
|
||||
// Re-apply the saved preference: the live PipeWire value is runtime-only and resets on restart
|
||||
function applyMonoStartupPreference() {
|
||||
if (!SettingsData._hasLoaded)
|
||||
if (!SettingsData._hasLoaded || !SettingsData.audioMono)
|
||||
return;
|
||||
queryMonoSetting((supported, currentValue) => {
|
||||
if (supported && currentValue !== SettingsData.audioMono)
|
||||
@@ -1771,8 +1778,11 @@ EOFCONFIG
|
||||
}
|
||||
|
||||
onRefCountChanged: {
|
||||
if (root.refCount === 1)
|
||||
root.refreshFreeBsdDevices();
|
||||
if (root.refCount !== 1)
|
||||
return;
|
||||
root.rebuildTypedNodeLists();
|
||||
root.deviceRevision++;
|
||||
root.refreshFreeBsdDevices();
|
||||
}
|
||||
|
||||
Timer {
|
||||
|
||||
@@ -729,7 +729,7 @@ Singleton {
|
||||
|
||||
readonly property bool suggestPowerSaver: false
|
||||
|
||||
readonly property var peripheralDevices: UPower.devices.values.filter(dev => dev && dev.ready && !dev.isLaptopBattery && peripheralIcon(dev.type) !== "").map(dev => ({
|
||||
readonly property var peripheralDevices: refCount === 0 ? [] : UPower.devices.values.filter(dev => dev && dev.ready && !dev.isLaptopBattery && peripheralIcon(dev.type) !== "").map(dev => ({
|
||||
"name": dev.model || UPowerDeviceType.toString(dev.type),
|
||||
"percentage": Math.round(dev.percentage * 100),
|
||||
"type": dev.type,
|
||||
@@ -738,6 +738,8 @@ Singleton {
|
||||
}))
|
||||
|
||||
readonly property var bluetoothDevices: {
|
||||
if (refCount === 0)
|
||||
return [];
|
||||
const bluetoothTypes = [UPowerDeviceType.BluetoothGeneric, UPowerDeviceType.Headphones, UPowerDeviceType.Headset, UPowerDeviceType.Keyboard, UPowerDeviceType.Mouse, UPowerDeviceType.Speakers];
|
||||
|
||||
return peripheralDevices.filter(dev => bluetoothTypes.includes(dev.type));
|
||||
|
||||
@@ -23,6 +23,7 @@ Singleton {
|
||||
property bool wpexecChecked: false
|
||||
property var pendingCodecActions: []
|
||||
property bool _codecSignalsSubscribed: false
|
||||
property var _codecSubscriptionIds: []
|
||||
readonly property string bluezService: "org.bluez"
|
||||
readonly property string mediaTransportIface: "org.bluez.MediaTransport1"
|
||||
readonly property string mediaEndpointIface: "org.bluez.MediaEndpoint1"
|
||||
@@ -77,11 +78,22 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
detectWpexecProcess.running = true;
|
||||
maybeSubscribeCodecSignals();
|
||||
readonly property bool hasConnectedAudioDevice: {
|
||||
if (!adapter?.devices)
|
||||
return false;
|
||||
return adapter.devices.values.some(dev => dev?.connected && isAudioDevice(dev));
|
||||
}
|
||||
|
||||
onHasConnectedAudioDeviceChanged: {
|
||||
if (hasConnectedAudioDevice) {
|
||||
maybeSubscribeCodecSignals();
|
||||
return;
|
||||
}
|
||||
unsubscribeCodecSignals();
|
||||
}
|
||||
|
||||
Component.onCompleted: maybeSubscribeCodecSignals()
|
||||
|
||||
function setBluetoothEnabled(enabled) {
|
||||
if (bluetoothBridgeAvailable) {
|
||||
DMSService.sendRequest("bluetooth.setPowered", {
|
||||
@@ -117,12 +129,18 @@ Singleton {
|
||||
|
||||
function onConnectionStateChanged() {
|
||||
root._codecSignalsSubscribed = false;
|
||||
root._codecSubscriptionIds = [];
|
||||
root.maybeSubscribeCodecSignals();
|
||||
}
|
||||
|
||||
function onCapabilitiesReceived() {
|
||||
root.maybeSubscribeCodecSignals();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
enabled: root._codecSignalsSubscribed
|
||||
|
||||
function onDbusSignalReceived(subscriptionId, data) {
|
||||
root.handleCodecDbusSignal(data);
|
||||
@@ -143,12 +161,34 @@ Singleton {
|
||||
}
|
||||
|
||||
function maybeSubscribeCodecSignals() {
|
||||
if (!dbusBridgeAvailable || _codecSignalsSubscribed)
|
||||
if (!dbusBridgeAvailable || _codecSignalsSubscribed || !hasConnectedAudioDevice)
|
||||
return;
|
||||
_codecSignalsSubscribed = true;
|
||||
DMSService.dbusSubscribe("system", bluezService, "", objectManagerIface, "InterfacesAdded", null);
|
||||
DMSService.dbusSubscribe("system", bluezService, "", objectManagerIface, "InterfacesRemoved", null);
|
||||
DMSService.dbusSubscribe("system", bluezService, "", propertiesIface, "PropertiesChanged", null);
|
||||
DMSService.dbusSubscribe("system", bluezService, "", objectManagerIface, "InterfacesAdded", _rememberCodecSubscription);
|
||||
DMSService.dbusSubscribe("system", bluezService, "", objectManagerIface, "InterfacesRemoved", _rememberCodecSubscription);
|
||||
DMSService.dbusSubscribe("system", bluezService, "", propertiesIface, "PropertiesChanged", _rememberCodecSubscription);
|
||||
}
|
||||
|
||||
function _rememberCodecSubscription(response) {
|
||||
const id = response.result?.subscriptionId;
|
||||
if (response.error || !id)
|
||||
return;
|
||||
if (!_codecSignalsSubscribed) {
|
||||
DMSService.dbusUnsubscribe(id, null);
|
||||
return;
|
||||
}
|
||||
_codecSubscriptionIds = _codecSubscriptionIds.concat([id]);
|
||||
}
|
||||
|
||||
function unsubscribeCodecSignals() {
|
||||
if (!_codecSignalsSubscribed)
|
||||
return;
|
||||
_codecSignalsSubscribed = false;
|
||||
const ids = _codecSubscriptionIds;
|
||||
_codecSubscriptionIds = [];
|
||||
if (!DMSService.isConnected)
|
||||
return;
|
||||
ids.forEach(id => DMSService.dbusUnsubscribe(id, null));
|
||||
}
|
||||
|
||||
function handleCodecDbusSignal(data) {
|
||||
@@ -466,28 +506,116 @@ Singleton {
|
||||
|
||||
function codecMap() {
|
||||
return {
|
||||
"LHDC_V5": { "name": "LHDC v5", "description": "Highest quality • Low latency", "qualityColor": "#4CAF50" },
|
||||
"LHDC_V3": { "name": "LHDC v3", "description": "High quality • Low latency", "qualityColor": "#FF9800" },
|
||||
"LDAC": { "name": "LDAC", "description": "Highest quality • Higher battery usage", "qualityColor": "#4CAF50" },
|
||||
"APTX_HD": { "name": "aptX HD", "description": "High quality • Balanced battery", "qualityColor": "#FF9800" },
|
||||
"APTX_LL": { "name": "aptX LL", "description": "Low latency • Gaming and video", "qualityColor": "#FF9800" },
|
||||
"APTX_ADAPTIVE": { "name": "aptX Adaptive", "description": "Adaptive quality and latency", "qualityColor": "#FF9800" },
|
||||
"APTX": { "name": "aptX", "description": "Good quality • Low latency", "qualityColor": "#FF9800" },
|
||||
"AAC_ELD": { "name": "AAC-ELD", "description": "Low-delay AAC • Voice and video", "qualityColor": "#2196F3" },
|
||||
"AAC": { "name": "AAC", "description": "Balanced quality and battery", "qualityColor": "#2196F3" },
|
||||
"OPUS_05": { "name": "Opus", "description": "High quality • Modern Bluetooth LE audio", "qualityColor": "#4CAF50" },
|
||||
"OPUS_G": { "name": "Opus", "description": "High quality • Modern Bluetooth LE audio", "qualityColor": "#4CAF50" },
|
||||
"OPUS": { "name": "Opus", "description": "High quality • Efficient streaming", "qualityColor": "#4CAF50" },
|
||||
"LC3": { "name": "LC3", "description": "LE Audio • Efficient high quality", "qualityColor": "#4CAF50" },
|
||||
"LC3_SWB": { "name": "LC3-SWB", "description": "Wideband speech • Hands-free calls", "qualityColor": "#9E9E9E" },
|
||||
"LC3_A127": { "name": "LC3", "description": "LE Audio speech • Hands-free calls", "qualityColor": "#9E9E9E" },
|
||||
"LC3PLUS_HR": { "name": "LC3plus HR", "description": "High resolution LE Audio", "qualityColor": "#4CAF50" },
|
||||
"SBC_XQ": { "name": "SBC-XQ", "description": "Enhanced SBC • Better compatibility", "qualityColor": "#2196F3" },
|
||||
"SBC": { "name": "SBC", "description": "Basic quality • Universal compatibility", "qualityColor": "#9E9E9E" },
|
||||
"MSBC": { "name": "mSBC", "description": "Modified SBC • Optimized for speech", "qualityColor": "#9E9E9E" },
|
||||
"CVSD": { "name": "CVSD", "description": "Basic speech codec • Legacy compatibility", "qualityColor": "#9E9E9E" },
|
||||
"G722": { "name": "G722", "description": "ASHA • Hearing aid audio", "qualityColor": "#9E9E9E" },
|
||||
"FASTSTREAM": { "name": "FastStream", "description": "Low latency SBC variant", "qualityColor": "#2196F3" }
|
||||
"LHDC_V5": {
|
||||
"name": "LHDC v5",
|
||||
"description": "Highest quality • Low latency",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"LHDC_V3": {
|
||||
"name": "LHDC v3",
|
||||
"description": "High quality • Low latency",
|
||||
"qualityColor": "#FF9800"
|
||||
},
|
||||
"LDAC": {
|
||||
"name": "LDAC",
|
||||
"description": "Highest quality • Higher battery usage",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"APTX_HD": {
|
||||
"name": "aptX HD",
|
||||
"description": "High quality • Balanced battery",
|
||||
"qualityColor": "#FF9800"
|
||||
},
|
||||
"APTX_LL": {
|
||||
"name": "aptX LL",
|
||||
"description": "Low latency • Gaming and video",
|
||||
"qualityColor": "#FF9800"
|
||||
},
|
||||
"APTX_ADAPTIVE": {
|
||||
"name": "aptX Adaptive",
|
||||
"description": "Adaptive quality and latency",
|
||||
"qualityColor": "#FF9800"
|
||||
},
|
||||
"APTX": {
|
||||
"name": "aptX",
|
||||
"description": "Good quality • Low latency",
|
||||
"qualityColor": "#FF9800"
|
||||
},
|
||||
"AAC_ELD": {
|
||||
"name": "AAC-ELD",
|
||||
"description": "Low-delay AAC • Voice and video",
|
||||
"qualityColor": "#2196F3"
|
||||
},
|
||||
"AAC": {
|
||||
"name": "AAC",
|
||||
"description": "Balanced quality and battery",
|
||||
"qualityColor": "#2196F3"
|
||||
},
|
||||
"OPUS_05": {
|
||||
"name": "Opus",
|
||||
"description": "High quality • Modern Bluetooth LE audio",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"OPUS_G": {
|
||||
"name": "Opus",
|
||||
"description": "High quality • Modern Bluetooth LE audio",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"OPUS": {
|
||||
"name": "Opus",
|
||||
"description": "High quality • Efficient streaming",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"LC3": {
|
||||
"name": "LC3",
|
||||
"description": "LE Audio • Efficient high quality",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"LC3_SWB": {
|
||||
"name": "LC3-SWB",
|
||||
"description": "Wideband speech • Hands-free calls",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"LC3_A127": {
|
||||
"name": "LC3",
|
||||
"description": "LE Audio speech • Hands-free calls",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"LC3PLUS_HR": {
|
||||
"name": "LC3plus HR",
|
||||
"description": "High resolution LE Audio",
|
||||
"qualityColor": "#4CAF50"
|
||||
},
|
||||
"SBC_XQ": {
|
||||
"name": "SBC-XQ",
|
||||
"description": "Enhanced SBC • Better compatibility",
|
||||
"qualityColor": "#2196F3"
|
||||
},
|
||||
"SBC": {
|
||||
"name": "SBC",
|
||||
"description": "Basic quality • Universal compatibility",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"MSBC": {
|
||||
"name": "mSBC",
|
||||
"description": "Modified SBC • Optimized for speech",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"CVSD": {
|
||||
"name": "CVSD",
|
||||
"description": "Basic speech codec • Legacy compatibility",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"G722": {
|
||||
"name": "G722",
|
||||
"description": "ASHA • Hearing aid audio",
|
||||
"qualityColor": "#9E9E9E"
|
||||
},
|
||||
"FASTSTREAM": {
|
||||
"name": "FastStream",
|
||||
"description": "Low latency SBC variant",
|
||||
"qualityColor": "#2196F3"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,13 @@ Singleton {
|
||||
readonly property var log: Log.scoped("BlurService")
|
||||
|
||||
property bool compositorSupported: false
|
||||
property bool _probed: false
|
||||
readonly property bool available: compositorSupported
|
||||
readonly property bool blurRequested: SettingsData.blurEnabled ?? false
|
||||
onBlurRequestedChanged: {
|
||||
if (blurRequested)
|
||||
probe();
|
||||
}
|
||||
readonly property bool enabled: available && (SettingsData.blurEnabled ?? false)
|
||||
|
||||
// These settings predate non-blurred surface borders, so keep their keys for compatibility.
|
||||
@@ -68,5 +74,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: blurProbe.running = true
|
||||
Component.onCompleted: {
|
||||
if (blurRequested)
|
||||
probe();
|
||||
}
|
||||
|
||||
function probe() {
|
||||
if (_probed)
|
||||
return;
|
||||
_probed = true;
|
||||
blurProbe.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,20 +520,24 @@ Singleton {
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: screenChangeRescanTimer
|
||||
property int rescanAttempt: 0
|
||||
interval: 3000
|
||||
id: rescanTimer
|
||||
property var schedule: []
|
||||
property int attempt: 0
|
||||
repeat: false
|
||||
function run(intervals) {
|
||||
schedule = intervals;
|
||||
attempt = 0;
|
||||
interval = intervals[0];
|
||||
restart();
|
||||
}
|
||||
onTriggered: {
|
||||
rescanDevices();
|
||||
rescanAttempt++;
|
||||
if (rescanAttempt < 3) {
|
||||
interval = rescanAttempt === 1 ? 5000 : 8000;
|
||||
attempt++;
|
||||
if (attempt < schedule.length) {
|
||||
interval = schedule[attempt];
|
||||
restart();
|
||||
return;
|
||||
}
|
||||
rescanAttempt = 0;
|
||||
interval = 3000;
|
||||
osdSuppressTimer.restart();
|
||||
}
|
||||
}
|
||||
@@ -543,9 +547,7 @@ Singleton {
|
||||
|
||||
function onScreensChanged() {
|
||||
suppressOsd = true;
|
||||
screenChangeRescanTimer.rescanAttempt = 0;
|
||||
screenChangeRescanTimer.interval = 3000;
|
||||
screenChangeRescanTimer.restart();
|
||||
rescanTimer.run([3000, 5000, 8000]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,6 +577,7 @@ Singleton {
|
||||
function onSessionResumed() {
|
||||
suppressOsd = true;
|
||||
osdSuppressTimer.restart();
|
||||
rescanTimer.run([400, 1400, 2600]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ Item {
|
||||
readonly property var log: Log.scoped("CalendarDankBackend")
|
||||
|
||||
property bool enabled: false
|
||||
property bool preferred: false
|
||||
property int _rediscoverMisses: 0
|
||||
readonly property int _rediscoverCap: 20
|
||||
|
||||
property string socketPath: ""
|
||||
readonly property bool socketFound: socketPath.length > 0
|
||||
@@ -41,6 +44,7 @@ Item {
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled) {
|
||||
_rediscoverMisses = 0;
|
||||
if (!connected)
|
||||
discoverProcess.running = true;
|
||||
return;
|
||||
@@ -51,8 +55,14 @@ Item {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
onConnectedChanged: {
|
||||
if (!connected)
|
||||
_rediscoverMisses = 0;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
binaryCheck.running = true;
|
||||
if (!enabled)
|
||||
return;
|
||||
discoverProcess.running = true;
|
||||
}
|
||||
|
||||
@@ -87,6 +97,8 @@ Item {
|
||||
subscribeSocket.connected = false;
|
||||
root.socketPath = "";
|
||||
}
|
||||
if (!root.binaryChecked)
|
||||
binaryCheck.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,8 +107,9 @@ Item {
|
||||
id: rediscoverTimer
|
||||
interval: 3000
|
||||
repeat: true
|
||||
running: root.enabled && !root.connected
|
||||
running: root.enabled && !root.connected && (root.preferred || (root.binaryChecked && root.binaryExists)) && root._rediscoverMisses < root._rediscoverCap
|
||||
onTriggered: {
|
||||
root._rediscoverMisses++;
|
||||
if (!discoverProcess.running)
|
||||
discoverProcess.running = true;
|
||||
}
|
||||
@@ -113,8 +126,10 @@ Item {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (enabled && !connected)
|
||||
discoverProcess.running = true;
|
||||
if (!enabled || connected)
|
||||
return;
|
||||
_rediscoverMisses = 0;
|
||||
discoverProcess.running = true;
|
||||
}
|
||||
|
||||
function _applySocketPath(path) {
|
||||
|
||||
@@ -9,6 +9,8 @@ Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("CalendarKhalBackend")
|
||||
|
||||
property bool enabled: false
|
||||
property bool _checked: false
|
||||
property bool installed: false
|
||||
property var eventsByDate: ({})
|
||||
property bool isLoading: false
|
||||
@@ -18,10 +20,14 @@ Item {
|
||||
property string dateFormat: "MM/dd/yyyy"
|
||||
|
||||
function checkAvailability() {
|
||||
if (!formatProcess.running)
|
||||
formatProcess.running = true;
|
||||
if (_checked || !enabled)
|
||||
return;
|
||||
_checked = true;
|
||||
formatProcess.running = true;
|
||||
}
|
||||
|
||||
onEnabledChanged: checkAvailability()
|
||||
|
||||
function loadCurrentMonth() {
|
||||
let today = new Date();
|
||||
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
@@ -61,8 +67,9 @@ Item {
|
||||
command: ["khal", "printformats"]
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0)
|
||||
checkProcess.running = true;
|
||||
if (exitCode === 127)
|
||||
return;
|
||||
checkProcess.running = true;
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
@@ -75,7 +82,6 @@ Item {
|
||||
root.dateFormat = root._parseDateFormat(formatExample);
|
||||
break;
|
||||
}
|
||||
checkProcess.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,14 @@ Singleton {
|
||||
|
||||
CalendarKhalBackend {
|
||||
id: khalBackend
|
||||
enabled: root.backendPref !== "dankcal"
|
||||
onEventsByDateChanged: root.mergeEvents()
|
||||
}
|
||||
|
||||
CalendarDankBackend {
|
||||
id: dankBackend
|
||||
enabled: root.backendPref === "dankcal" || root.backendPref === "auto"
|
||||
preferred: root.backendPref === "dankcal"
|
||||
onEventsByDateChanged: root.mergeEvents()
|
||||
onTasksByDateChanged: root.mergeEvents()
|
||||
onConnectedChanged: {
|
||||
|
||||
@@ -13,6 +13,9 @@ Singleton {
|
||||
property list<int> values: Array(6)
|
||||
property int refCount: 0
|
||||
property bool cavaAvailable: false
|
||||
property bool _cavaChecked: false
|
||||
readonly property bool cavaWanted: SettingsData.audioVisualizerEnabled || refCount > 0
|
||||
onCavaWantedChanged: _checkCava()
|
||||
readonly property string _confPath: `${Paths.strip(StandardPaths.writableLocation(StandardPaths.TempLocation))}/dms-cava-${Date.now()}-${Math.floor(Math.random() * 1000000)}.conf`
|
||||
|
||||
Process {
|
||||
@@ -21,11 +24,16 @@ Singleton {
|
||||
command: ["sh", "-c", "command -v cava"]
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
root._cavaChecked = true;
|
||||
root.cavaAvailable = exitCode === 0 && Quickshell.env("DMS_DISABLE_CAVA") !== "1";
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Component.onCompleted: _checkCava()
|
||||
|
||||
function _checkCava() {
|
||||
if (!cavaWanted || _cavaChecked || cavaCheck.running)
|
||||
return;
|
||||
cavaCheck.running = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ Singleton {
|
||||
property int selectedIndex: 0
|
||||
property bool keyboardNavigationActive: false
|
||||
property int refCount: 0
|
||||
property bool _pasteSupportChecked: false
|
||||
property bool _launcherCacheValid: false
|
||||
property string _launcherCachedQuery: ""
|
||||
property var _launcherCachedEntries: []
|
||||
@@ -55,18 +56,40 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
enabled: root.refCount > 0
|
||||
function onIsConnectedChanged() {
|
||||
root.refreshPasteSupport();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: refreshPasteSupport()
|
||||
onRefCountChanged: {
|
||||
if (refCount > 0) {
|
||||
ensureSubscription();
|
||||
} else if (refCount === 0 && DMSService.activeSubscriptions.includes("clipboard")) {
|
||||
DMSService.removeSubscription("clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
function ensureSubscription() {
|
||||
if (refCount <= 0)
|
||||
return;
|
||||
if (!_pasteSupportChecked)
|
||||
refreshPasteSupport();
|
||||
if (DMSService.activeSubscriptions.includes("clipboard"))
|
||||
return;
|
||||
if (DMSService.activeSubscriptions.includes("all"))
|
||||
return;
|
||||
DMSService.addSubscription("clipboard");
|
||||
refresh();
|
||||
}
|
||||
|
||||
function refreshPasteSupport() {
|
||||
if (!DMSService.isConnected) {
|
||||
pasteSupported = false;
|
||||
_pasteSupportChecked = false;
|
||||
return;
|
||||
}
|
||||
_pasteSupportChecked = true;
|
||||
DMSService.sendRequest("clipboard.pasteSupported", null, function (response) {
|
||||
root.pasteSupported = !response.error && response.result && response.result.supported === true;
|
||||
});
|
||||
@@ -151,6 +174,8 @@ Singleton {
|
||||
if (!clipboardAvailable) {
|
||||
return;
|
||||
}
|
||||
if (!_pasteSupportChecked)
|
||||
refreshPasteSupport();
|
||||
|
||||
const trimmed = (query || "").toString().trim();
|
||||
const maxItems = limit > 0 ? limit : 20;
|
||||
|
||||
@@ -65,6 +65,7 @@ Singleton {
|
||||
target: Quickshell
|
||||
function onScreensChanged() {
|
||||
root.probeOutputPower();
|
||||
root.refreshHyprlandMonitorLayout();
|
||||
}
|
||||
}
|
||||
readonly property bool frameCompositorLayoutReady: (!isNiri || NiriService.frameLayoutReady) && (!isHyprland || HyprlandService.frameLayoutReady)
|
||||
@@ -194,9 +195,8 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: AqueousService
|
||||
enabled: root.isAqueous
|
||||
function onStateChanged() {
|
||||
if (!root.isAqueous)
|
||||
return;
|
||||
root.scheduleSort();
|
||||
if (AqueousService.available)
|
||||
root.workspaceStateChanged();
|
||||
@@ -496,6 +496,7 @@ Singleton {
|
||||
}
|
||||
Connections {
|
||||
target: NiriService
|
||||
enabled: root.isNiri
|
||||
function onWindowsChanged() {
|
||||
root.scheduleSort();
|
||||
}
|
||||
@@ -506,23 +507,17 @@ Singleton {
|
||||
detectCompositor();
|
||||
updateHyprlandVisibleSpecialWorkspaces(null);
|
||||
scheduleSort();
|
||||
Qt.callLater(() => {
|
||||
NiriService.generateNiriLayoutConfig();
|
||||
HyprlandService.generateLayoutConfig();
|
||||
});
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MangoService
|
||||
enabled: root.isMango
|
||||
function onStateChanged() {
|
||||
if (!root.isMango)
|
||||
return;
|
||||
root.scheduleSort();
|
||||
root.workspaceStateChanged();
|
||||
}
|
||||
function onWindowsChanged() {
|
||||
if (isMango)
|
||||
scheduleSort();
|
||||
root.scheduleSort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,13 +995,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Quickshell
|
||||
function onScreensChanged() {
|
||||
root.refreshHyprlandMonitorLayout();
|
||||
}
|
||||
}
|
||||
|
||||
// Workspace moves can land before Hyprland announces the monitor and before the Wayland
|
||||
// output reaches us, so re-read both models once the screen set settles (#3133)
|
||||
function refreshHyprlandMonitorLayout() {
|
||||
@@ -1378,22 +1366,6 @@ Singleton {
|
||||
return toplevels.filter(w => map.get(w) === currentWorkspaceId);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: compositorInitTimer
|
||||
interval: 100
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
detectCompositor();
|
||||
compositorDetected = true;
|
||||
Qt.callLater(() => {
|
||||
NiriService.generateNiriLayoutConfig();
|
||||
HyprlandService.generateLayoutConfig();
|
||||
MangoService.generateLayoutConfig();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Primary detection asks the kernel which process owns the $WAYLAND_DISPLAY
|
||||
// socket — the compositor quickshell is actually connected to. Env vars like
|
||||
// HYPRLAND_INSTANCE_SIGNATURE / MANGO_INSTANCE_SIGNATURE can leak into the
|
||||
|
||||
@@ -223,6 +223,8 @@ Singleton {
|
||||
return;
|
||||
cupsAvailable = DMSService.capabilities.includes("cups");
|
||||
|
||||
if (refCount <= 0)
|
||||
return;
|
||||
if (cupsAvailable && !stateInitialized) {
|
||||
stateInitialized = true;
|
||||
getState();
|
||||
|
||||
@@ -228,6 +228,7 @@ Singleton {
|
||||
|
||||
property int refCount: 0
|
||||
property bool stateInitialized: false
|
||||
property var _lastRawState: null
|
||||
|
||||
property string credentialsToken: ""
|
||||
property string credentialsSSID: ""
|
||||
@@ -330,9 +331,12 @@ Singleton {
|
||||
|
||||
function addRef() {
|
||||
refCount++;
|
||||
if (refCount === 1 && networkAvailable) {
|
||||
if (refCount !== 1)
|
||||
return;
|
||||
if (_lastRawState)
|
||||
syncLists(_lastRawState);
|
||||
if (networkAvailable)
|
||||
startAutoScan();
|
||||
}
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
@@ -350,7 +354,7 @@ Singleton {
|
||||
DMSService.sendRequest("network.getState", null, response => {
|
||||
if (response.result) {
|
||||
updateState(response.result);
|
||||
if (!initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) {
|
||||
if (refCount > 0 && !initialStateFetched && response.result.wifiEnabled && (!response.result.wifiNetworks || response.result.wifiNetworks.length === 0)) {
|
||||
initialStateFetched = true;
|
||||
Qt.callLater(() => scanWifi());
|
||||
}
|
||||
@@ -374,7 +378,41 @@ Singleton {
|
||||
return same ? previous : merged;
|
||||
}
|
||||
|
||||
function syncLists(state) {
|
||||
ethernetDevices = keepUnchanged(ethernetDevices, state.ethernetDevices || []);
|
||||
wiredConnections = keepUnchanged(wiredConnections, state.wiredConnections || []);
|
||||
cellularDevices = keepUnchanged(cellularDevices, state.cellularDevices || []);
|
||||
cellularConnections = keepUnchanged(cellularConnections, state.cellularConnections || []);
|
||||
|
||||
if (state.wifiNetworks) {
|
||||
wifiNetworks = keepUnchanged(wifiNetworks, state.wifiNetworks);
|
||||
}
|
||||
|
||||
if (!state.wifiNetworks && !state.savedWifiNetworks)
|
||||
return;
|
||||
const hasSavedWifiState = DMSService.apiVersion >= savedWifiStateApiVersion && Array.isArray(state.savedWifiNetworks);
|
||||
const sourceSavedNetworks = hasSavedWifiState ? state.savedWifiNetworks : (state.wifiNetworks || []).filter(network => network.saved);
|
||||
const normalized = sourceSavedNetworks.map(network => Object.assign({}, network, {
|
||||
saved: true,
|
||||
outOfRange: hasSavedWifiState ? network.outOfRange === true : false
|
||||
}));
|
||||
const saved = keepUnchanged(savedWifiNetworks, normalized);
|
||||
if (saved !== savedWifiNetworks) {
|
||||
const mapping = {};
|
||||
for (const network of saved) {
|
||||
if (network?.ssid)
|
||||
mapping[network.ssid] = network.ssid;
|
||||
}
|
||||
savedConnections = saved;
|
||||
savedWifiNetworks = saved;
|
||||
ssidToConnectionName = mapping;
|
||||
}
|
||||
|
||||
networksUpdated();
|
||||
}
|
||||
|
||||
function updateState(state) {
|
||||
_lastRawState = state;
|
||||
const previousConnecting = isConnecting;
|
||||
const previousConnectingSSID = connectingSSID;
|
||||
|
||||
@@ -387,9 +425,6 @@ Singleton {
|
||||
ethernetInterface = state.ethernetDevice || "";
|
||||
ethernetConnected = state.ethernetConnected || false;
|
||||
ethernetConnectionUuid = state.ethernetConnectionUuid || "";
|
||||
ethernetDevices = keepUnchanged(ethernetDevices, state.ethernetDevices || []);
|
||||
|
||||
wiredConnections = keepUnchanged(wiredConnections, state.wiredConnections || []);
|
||||
|
||||
cellularIP = state.cellularIP || "";
|
||||
cellularInterface = state.cellularDevice || "";
|
||||
@@ -397,8 +432,6 @@ Singleton {
|
||||
cellularEnabled = state.cellularEnabled !== undefined ? state.cellularEnabled : true;
|
||||
cellularHardwareEnabled = state.cellularHardwareEnabled !== undefined ? state.cellularHardwareEnabled : true;
|
||||
cellularConnectionUuid = state.cellularConnectionUuid || "";
|
||||
cellularDevices = keepUnchanged(cellularDevices, state.cellularDevices || []);
|
||||
cellularConnections = keepUnchanged(cellularConnections, state.cellularConnections || []);
|
||||
|
||||
wifiIP = state.wifiIP || "";
|
||||
wifiInterface = state.wifiDevice || "";
|
||||
@@ -450,31 +483,8 @@ Singleton {
|
||||
currentWifiSSID = state.wifiSSID || "";
|
||||
wifiSignalStrength = state.wifiSignal || 0;
|
||||
|
||||
if (state.wifiNetworks) {
|
||||
wifiNetworks = keepUnchanged(wifiNetworks, state.wifiNetworks);
|
||||
}
|
||||
|
||||
if (state.wifiNetworks || state.savedWifiNetworks) {
|
||||
const hasSavedWifiState = DMSService.apiVersion >= savedWifiStateApiVersion && Array.isArray(state.savedWifiNetworks);
|
||||
const sourceSavedNetworks = hasSavedWifiState ? state.savedWifiNetworks : (state.wifiNetworks || []).filter(network => network.saved);
|
||||
const normalized = sourceSavedNetworks.map(network => Object.assign({}, network, {
|
||||
saved: true,
|
||||
outOfRange: hasSavedWifiState ? network.outOfRange === true : false
|
||||
}));
|
||||
const saved = keepUnchanged(savedWifiNetworks, normalized);
|
||||
if (saved !== savedWifiNetworks) {
|
||||
const mapping = {};
|
||||
for (const network of saved) {
|
||||
if (network?.ssid)
|
||||
mapping[network.ssid] = network.ssid;
|
||||
}
|
||||
savedConnections = saved;
|
||||
savedWifiNetworks = saved;
|
||||
ssidToConnectionName = mapping;
|
||||
}
|
||||
|
||||
networksUpdated();
|
||||
}
|
||||
if (refCount > 0)
|
||||
syncLists(state);
|
||||
|
||||
if (state.vpnProfiles) {
|
||||
vpnProfiles = keepUnchanged(vpnProfiles, state.vpnProfiles);
|
||||
|
||||
@@ -82,7 +82,7 @@ Singleton {
|
||||
property bool screensaverInhibited: false
|
||||
property var screensaverInhibitors: []
|
||||
|
||||
property var activeSubscriptions: ["network", "network.credentials", "loginctl", "freedesktop", "freedesktop.screensaver", "gamma", "theme.auto", "wallpaper", "bluetooth", "bluetooth.pairing", "brightness", "wlroutput", "evdev", "browser", "dbus", "clipboard", "sysupdate", "files"]
|
||||
property var activeSubscriptions: ["network", "network.credentials", "loginctl", "freedesktop", "freedesktop.screensaver", "gamma", "wallpaper", "bluetooth", "bluetooth.pairing", "brightness", "wlroutput", "evdev", "browser", "dbus"]
|
||||
property var connectionCapabilities: null
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
@@ -14,6 +14,7 @@ Singleton {
|
||||
property int indexVersion: 0
|
||||
property bool supportsTypeFilter: false
|
||||
property bool versionChecked: false
|
||||
property bool _checked: false
|
||||
|
||||
signal searchResultsReceived(var results)
|
||||
signal statsReceived(var stats)
|
||||
@@ -22,7 +23,7 @@ Singleton {
|
||||
Process {
|
||||
id: checkProcess
|
||||
command: ["sh", "-c", "command -v dsearch"]
|
||||
running: true
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: line => {
|
||||
@@ -52,7 +53,14 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function ensureChecked() {
|
||||
if (_checked)
|
||||
return;
|
||||
rediscover();
|
||||
}
|
||||
|
||||
function ping(callback) {
|
||||
ensureChecked();
|
||||
if (!dsearchAvailable) {
|
||||
if (callback) {
|
||||
callback({
|
||||
@@ -94,6 +102,7 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureChecked();
|
||||
if (!dsearchAvailable) {
|
||||
if (callback) {
|
||||
callback({
|
||||
@@ -179,6 +188,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function rediscover() {
|
||||
_checked = true;
|
||||
checkProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@ Singleton {
|
||||
|
||||
property bool isSystemd: false
|
||||
property bool systemdAutostartTargetActive: false
|
||||
property bool systemdAutostartTargetChecked: false
|
||||
property bool systemdAutostartTargetChecked: Qt.platform.os === "unix"
|
||||
readonly property bool autostartAvailable: root.systemdAutostartTargetChecked && (!root.isSystemd || root.systemdAutostartTargetActive)
|
||||
property bool _probed: false
|
||||
|
||||
Component.onCompleted: {
|
||||
Paths.desktopIconResolver = name => resolveIconPath(name);
|
||||
if (Qt.platform.os === "unix") {
|
||||
root.isSystemd = false;
|
||||
root.systemdAutostartTargetChecked = true;
|
||||
} else {
|
||||
initSystemCheckProcess.running = true;
|
||||
}
|
||||
Component.onCompleted: Paths.desktopIconResolver = name => resolveIconPath(name)
|
||||
|
||||
function ensureProbed() {
|
||||
if (_probed || systemdAutostartTargetChecked)
|
||||
return;
|
||||
_probed = true;
|
||||
initSystemCheckProcess.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
|
||||
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -15,14 +14,16 @@ Singleton {
|
||||
signal statsUpdated
|
||||
|
||||
property int refCount: 0
|
||||
readonly property bool powerSaver: PowerProfileWatcher.currentProfile === PowerProfile.PowerSaver
|
||||
property int updateInterval: refCount > 0 ? (powerSaver ? 6000 : 3000) : (powerSaver ? 60000 : 30000)
|
||||
readonly property bool powerSaver: pollingActive && PowerProfileWatcher.currentProfile === PowerProfile.PowerSaver
|
||||
property int updateInterval: powerSaver ? 6000 : 3000
|
||||
property bool isUpdating: false
|
||||
property bool pendingUpdate: false
|
||||
property int subscriptionGeneration: 0
|
||||
readonly property bool pollingActive: dgopAvailable && refCount > 0 && enabledModules.length > 0
|
||||
readonly property bool dgopAvailable: DMSService.isConnected && DMSService.capabilities.includes("dgop")
|
||||
property bool sessionGpuIdsSeeded: false
|
||||
property bool _metaWanted: false
|
||||
property bool _metaLoaded: false
|
||||
|
||||
property var moduleRefCounts: ({})
|
||||
property var enabledModules: []
|
||||
@@ -97,7 +98,17 @@ Singleton {
|
||||
"write": []
|
||||
})
|
||||
|
||||
function ensureMeta() {
|
||||
_metaWanted = true;
|
||||
if (_metaLoaded || !dgopAvailable)
|
||||
return;
|
||||
_metaLoaded = true;
|
||||
initializeSystemMetadata();
|
||||
initializeGpuMetadata();
|
||||
}
|
||||
|
||||
function addRef(modules = null) {
|
||||
ensureMeta();
|
||||
refCount++;
|
||||
let modulesChanged = false;
|
||||
|
||||
@@ -205,6 +216,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function addGpuPciId(pciId) {
|
||||
ensureMeta();
|
||||
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||
gpuPciIdRefCounts[pciId] = currentCount + 1;
|
||||
|
||||
@@ -696,11 +708,13 @@ Singleton {
|
||||
}
|
||||
|
||||
onDgopAvailableChanged: {
|
||||
if (!dgopAvailable)
|
||||
if (!dgopAvailable) {
|
||||
_metaLoaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
initializeSystemMetadata();
|
||||
initializeGpuMetadata();
|
||||
if (_metaWanted)
|
||||
ensureMeta();
|
||||
initializeDiskMounts();
|
||||
|
||||
if (!sessionGpuIdsSeeded && SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.length > 0) {
|
||||
@@ -710,47 +724,4 @@ Singleton {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: osReleaseProcess
|
||||
command: ["cat", "/etc/os-release"]
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
log.warn("Failed to read /etc/os-release");
|
||||
}
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
try {
|
||||
const lines = text.trim().split('\n');
|
||||
let prettyName = "";
|
||||
let name = "";
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim();
|
||||
if (trimmedLine.startsWith('PRETTY_NAME=')) {
|
||||
prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, '');
|
||||
} else if (trimmedLine.startsWith('NAME=')) {
|
||||
name = trimmedLine.substring(5).replace(/^["']|["']$/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer PRETTY_NAME, fallback to NAME
|
||||
const distroName = prettyName || name || "Linux";
|
||||
distribution = distroName;
|
||||
log.info("Detected distribution:", distroName);
|
||||
} catch (e) {
|
||||
log.warn("Failed to parse /etc/os-release:", e);
|
||||
distribution = "Linux";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
osReleaseProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,34 @@ Singleton {
|
||||
"trash": true
|
||||
})
|
||||
property var userDirs: []
|
||||
property int refCount: 0
|
||||
|
||||
signal watchEvent(var data)
|
||||
|
||||
onRefCountChanged: {
|
||||
if (refCount > 0) {
|
||||
ensureSubscription();
|
||||
} else if (refCount === 0 && DMSService.activeSubscriptions.includes("files")) {
|
||||
DMSService.removeSubscription("files");
|
||||
}
|
||||
}
|
||||
|
||||
function addRef() {
|
||||
refCount++;
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
refCount = Math.max(0, refCount - 1);
|
||||
}
|
||||
|
||||
function ensureSubscription() {
|
||||
if (DMSService.activeSubscriptions.includes("files"))
|
||||
return;
|
||||
if (DMSService.activeSubscriptions.includes("all"))
|
||||
return;
|
||||
DMSService.addSubscription("files");
|
||||
}
|
||||
|
||||
onConnectedChanged: {
|
||||
if (connected)
|
||||
refreshUserDirs();
|
||||
@@ -32,6 +57,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function watch(path, options, callback) {
|
||||
ensureSubscription();
|
||||
_send("files.watch", Object.assign({
|
||||
"path": path
|
||||
}, _options(options)), callback);
|
||||
|
||||
@@ -44,7 +44,16 @@ Singleton {
|
||||
}
|
||||
|
||||
function checkFirstLaunch() {
|
||||
firstLaunchCheckProcess.running = true;
|
||||
markerProbe.path = root.firstLaunchMarkerPath;
|
||||
}
|
||||
|
||||
function _finishCheck(firstLaunch) {
|
||||
isFirstLaunch = firstLaunch;
|
||||
checkComplete = true;
|
||||
if (!firstLaunch)
|
||||
return;
|
||||
log.info("First launch detected, greeter will be shown");
|
||||
greeterRequested();
|
||||
}
|
||||
|
||||
function markFirstLaunchComplete() {
|
||||
@@ -57,45 +66,32 @@ Singleton {
|
||||
greeterDismissed = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: firstLaunchCheckProcess
|
||||
|
||||
command: ["sh", "-c", `
|
||||
SETTINGS='` + settingsPath + `'
|
||||
MARKER='` + firstLaunchMarkerPath + `'
|
||||
if [ -f "$MARKER" ]; then
|
||||
echo 'skip'
|
||||
elif [ -f "$SETTINGS" ]; then
|
||||
echo 'existing_user'
|
||||
else
|
||||
echo 'first'
|
||||
fi
|
||||
`]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
const result = data.trim();
|
||||
|
||||
if (result === "first") {
|
||||
root.isFirstLaunch = true;
|
||||
log.info("First launch detected, greeter will be shown");
|
||||
} else if (result === "existing_user") {
|
||||
root.isFirstLaunch = false;
|
||||
log.info("Existing user detected, silently creating marker");
|
||||
touchMarkerProcess.running = true;
|
||||
} else {
|
||||
root.isFirstLaunch = false;
|
||||
}
|
||||
|
||||
root.checkComplete = true;
|
||||
|
||||
if (root.isFirstLaunch)
|
||||
root.greeterRequested();
|
||||
FileView {
|
||||
id: markerProbe
|
||||
path: ""
|
||||
printErrors: false
|
||||
onLoaded: root._finishCheck(false)
|
||||
onLoadFailed: error => {
|
||||
if (error !== FileViewError.FileNotFound) {
|
||||
root._finishCheck(false);
|
||||
return;
|
||||
}
|
||||
settingsProbe.path = root.settingsPath;
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: settingsProbe
|
||||
path: ""
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
log.info("Existing user detected, silently creating marker");
|
||||
touchMarkerProcess.running = true;
|
||||
root._finishCheck(false);
|
||||
}
|
||||
onLoadFailed: error => root._finishCheck(error === FileViewError.FileNotFound)
|
||||
}
|
||||
|
||||
Process {
|
||||
id: touchMarkerProcess
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ Singleton {
|
||||
property bool syncing: false
|
||||
property string syncStatus: ""
|
||||
property bool fallbackFromPrecheck: false
|
||||
property bool _detected: false
|
||||
|
||||
readonly property bool linked: slotState === "live"
|
||||
readonly property bool profileSyncSufficient: configured && inGroup
|
||||
@@ -27,9 +28,16 @@ Singleton {
|
||||
signal syncFinished
|
||||
|
||||
function refresh() {
|
||||
_detected = true;
|
||||
detectProcess.running = true;
|
||||
}
|
||||
|
||||
function ensureDetected() {
|
||||
if (_detected)
|
||||
return;
|
||||
refresh();
|
||||
}
|
||||
|
||||
function sync() {
|
||||
if (!binaryExists || syncing)
|
||||
return;
|
||||
@@ -95,7 +103,7 @@ for f in "/var/cache/dms-greeter/users/$USER/settings.json" /var/cache/dms-greet
|
||||
done
|
||||
[ -f "/var/cache/dms-greeter/users/$USER/settings.json" ] && echo slot
|
||||
exit 0`]
|
||||
running: true
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
|
||||
@@ -18,7 +18,11 @@ Singleton {
|
||||
property bool loaded: false
|
||||
property bool refreshing: false
|
||||
|
||||
Component.onCompleted: refresh()
|
||||
Component.onCompleted: {
|
||||
if (!SessionData.isGreeterMode)
|
||||
return;
|
||||
refresh();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (refreshing)
|
||||
|
||||
@@ -71,9 +71,9 @@ Singleton {
|
||||
try {
|
||||
const rules = JSON.parse(output);
|
||||
workspaceRules = Array.isArray(rules) ? rules.filter(rule => rule.monitor).map(rule => ({
|
||||
"workspaceString": rule.workspaceString,
|
||||
"monitor": rule.monitor
|
||||
})) : [];
|
||||
"workspaceString": rule.workspaceString,
|
||||
"monitor": rule.monitor
|
||||
})) : [];
|
||||
} catch (error) {
|
||||
log.warn("workspacerules parse failed:", error);
|
||||
}
|
||||
@@ -105,9 +105,8 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
enabled: CompositorService.isHyprland
|
||||
function onBarConfigsChanged() {
|
||||
if (!CompositorService.isHyprland)
|
||||
return;
|
||||
const newGaps = Math.max(4, (SettingsData.getPrimaryBarConfig()?.spacing ?? 4));
|
||||
if (newGaps === root._lastGapValue)
|
||||
return;
|
||||
@@ -123,6 +122,7 @@ Singleton {
|
||||
refreshLuaConfigStatus();
|
||||
if (luaConfigActive)
|
||||
ensureDmsLuaConfigs();
|
||||
generateLayoutConfig();
|
||||
return;
|
||||
}
|
||||
luaConfigDetected = false;
|
||||
|
||||
@@ -70,17 +70,25 @@ Singleton {
|
||||
Component.onCompleted: {
|
||||
_initialized = true;
|
||||
Paths.iconResolver = name => resolve(name);
|
||||
_probeSystemTheme();
|
||||
if (!settingsTheme)
|
||||
_probeSystemTheme();
|
||||
Qt.callLater(_rebuild);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: rebuildDebounce
|
||||
interval: 2000
|
||||
repeat: false
|
||||
onTriggered: root._rebuild()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
|
||||
function onApplicationsChanged() {
|
||||
if (!root.ready)
|
||||
return;
|
||||
Qt.callLater(root._rebuild);
|
||||
rebuildDebounce.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ Singleton {
|
||||
readonly property int postLockMonitorTimeout: isOnBattery ? SettingsData.batteryPostLockMonitorTimeout : SettingsData.acPostLockMonitorTimeout
|
||||
readonly property bool postLockMonitorActive: isShellLocked && postLockMonitorTimeout > 0
|
||||
|
||||
readonly property bool mediaPlaying: MprisController.activePlayer !== null && MprisController.activePlayer.isPlaying
|
||||
|
||||
onEnabledChanged: _applyMonitorEnableds()
|
||||
onPostLockMonitorActiveChanged: _applyMonitorEnableds()
|
||||
onMonitorTimeoutChanged: _rearmIdleMonitors()
|
||||
|
||||
@@ -132,10 +132,29 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property int _consumers: 0
|
||||
property bool _bindsStale: false
|
||||
|
||||
function addRef() {
|
||||
_consumers++;
|
||||
if (!_bindsStale)
|
||||
return;
|
||||
_bindsStale = false;
|
||||
loadBinds(false);
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
_consumers = Math.max(0, _consumers - 1);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NiriService
|
||||
enabled: CompositorService.isNiri
|
||||
function onConfigReloaded() {
|
||||
if (root._consumers === 0) {
|
||||
root._bindsStale = true;
|
||||
return;
|
||||
}
|
||||
Qt.callLater(root.loadBinds, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ Singleton {
|
||||
return;
|
||||
|
||||
const argList = Array.from(args);
|
||||
const loc = _callerLocation();
|
||||
const loc = (lvl >= Log.Level.Warn || root.level === Log.Level.Debug) ? _callerLocation() : null;
|
||||
const msg = argList.map(_stringify).join(" ");
|
||||
|
||||
let tag;
|
||||
|
||||
@@ -14,6 +14,14 @@ Singleton {
|
||||
property int refCount: 0
|
||||
readonly property bool allowed: DashRegistry.option("media", "lyrics") === true && controller.available
|
||||
readonly property bool subscribed: refCount > 0 && controller.available
|
||||
onSubscribedChanged: {
|
||||
MediaAccentService.lyricsConsumers += subscribed ? 1 : -1;
|
||||
if (subscribed) {
|
||||
MprisController.addPositionRef();
|
||||
return;
|
||||
}
|
||||
MprisController.removePositionRef();
|
||||
}
|
||||
property var activePlayer: subscribed ? MprisController.activePlayer : null
|
||||
readonly property real stableLength: subscribed ? MprisController.activePlayerStableLength : 0
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Singleton {
|
||||
DCommon.DSocket {
|
||||
id: monitorsSocket
|
||||
path: root.socketPath
|
||||
connected: root.available
|
||||
connected: CompositorService.isMango && root.available
|
||||
|
||||
onConnectionStateChanged: {
|
||||
if (linkUp) {
|
||||
@@ -122,7 +122,7 @@ Singleton {
|
||||
DCommon.DSocket {
|
||||
id: clientsSocket
|
||||
path: root.socketPath
|
||||
connected: root.available
|
||||
connected: CompositorService.isMango && root.available
|
||||
|
||||
onConnectionStateChanged: {
|
||||
if (linkUp)
|
||||
@@ -139,7 +139,7 @@ Singleton {
|
||||
DCommon.DSocket {
|
||||
id: dispatchSocket
|
||||
path: root.socketPath
|
||||
connected: root.available
|
||||
connected: CompositorService.isMango && root.available
|
||||
reconnectBaseMs: 25
|
||||
|
||||
onConnectionStateChanged: {
|
||||
@@ -553,9 +553,8 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
enabled: CompositorService.isMango
|
||||
function onBarConfigsChanged() {
|
||||
if (!CompositorService.isMango)
|
||||
return;
|
||||
const newGaps = Math.max(4, (SettingsData.getPrimaryBarConfig()?.spacing ?? 4));
|
||||
if (newGaps === root._lastGapValue)
|
||||
return;
|
||||
|
||||
@@ -13,21 +13,25 @@ Singleton {
|
||||
|
||||
readonly property bool hasAccent: MediaOptions.albumArtAccent ? _accent !== null : true
|
||||
readonly property color accent: MediaOptions.albumArtAccent && _accent !== null ? _accent : Theme.primary
|
||||
readonly property var lyricsHues: _lyricsHues()
|
||||
readonly property var lyricsAccents: lyricsHues.map(color => _readableLyricColor(color))
|
||||
readonly property color lyricsGroupAccent: _readableLyricColor(_mixHues(lyricsHues[0], lyricsHues[1], lyricsHues[2]))
|
||||
property int lyricsConsumers: 0
|
||||
readonly property bool lyricsLive: lyricsConsumers > 0
|
||||
readonly property var lyricsHues: lyricsLive ? _lyricsHues() : [Theme.primary, Theme.tertiary, Theme.secondary]
|
||||
readonly property var lyricsAccents: lyricsLive ? lyricsHues.map(color => _readableLyricColor(color)) : lyricsHues
|
||||
readonly property color lyricsGroupAccent: lyricsLive ? _readableLyricColor(_mixHues(lyricsHues[0], lyricsHues[1], lyricsHues[2])) : Theme.primary
|
||||
readonly property real lyricsChromaMin: 36
|
||||
readonly property real lyricsContrast: 4.5
|
||||
readonly property real lyricsTintMax: 0.8
|
||||
readonly property color lyricsTint: Theme.withAlpha(Theme.cardSurface, Math.min(Theme.cardSurface.a * Theme.foregroundAlpha, lyricsTintMax))
|
||||
readonly property var lyricsBackgrounds: {
|
||||
if (!lyricsLive)
|
||||
return [];
|
||||
const surface = Theme.withAlpha(Theme.cardSurface, 1);
|
||||
return [surface].concat(TrackArtService.artwork.colors.map(art => Theme.blend(Theme.withAlpha(art, 1), surface, lyricsTint.a)));
|
||||
}
|
||||
|
||||
readonly property color accentContainer: _container(Theme.primaryContainer, Theme.isLightMode ? 0.3 : 0.55, Theme.isLightMode ? 0.9 : 0.42, 1.25)
|
||||
readonly property color accentSecondaryContainer: _container(Theme.secondaryContainer, Theme.isLightMode ? 0.12 : 0.22, Theme.isLightMode ? 0.94 : 0.3, 1.08)
|
||||
readonly property color readableAccent: contrastTo(accent, Theme.onSurface, Theme.chipSurface, 4.5)
|
||||
readonly property color readableAccent: lyricsLive ? contrastTo(accent, Theme.onSurface, Theme.chipSurface, 4.5) : accent
|
||||
|
||||
property color onAccent
|
||||
property color onAccentContainer
|
||||
|
||||
@@ -33,6 +33,15 @@ Singleton {
|
||||
property bool _mprisRequestInFlight: false
|
||||
property bool _mprisPublishDirty: false
|
||||
property int _mprisConnectionEpoch: 0
|
||||
property int positionConsumers: 0
|
||||
|
||||
function addPositionRef() {
|
||||
positionConsumers++;
|
||||
}
|
||||
|
||||
function removePositionRef() {
|
||||
positionConsumers = Math.max(0, positionConsumers - 1);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.activePlayer
|
||||
@@ -52,14 +61,23 @@ Singleton {
|
||||
if (root.activePlayer && root.activePlayer.lengthSupported && root.activePlayer.length > 1) {
|
||||
root.activePlayerStableLength = root.activePlayer.length;
|
||||
}
|
||||
}
|
||||
function onPlaybackStateChanged() {
|
||||
root._syncStableMeta();
|
||||
root._checkIdle();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.activePlayer
|
||||
enabled: SettingsData.bluetoothMprisEnabled
|
||||
function onLengthChanged() {
|
||||
root._scheduleMPRISPublish();
|
||||
}
|
||||
function onMetadataChanged() {
|
||||
root._scheduleMPRISPublish();
|
||||
}
|
||||
function onPlaybackStateChanged() {
|
||||
root._syncStableMeta();
|
||||
root._checkIdle();
|
||||
root._scheduleMPRISPublish();
|
||||
}
|
||||
function onCanControlChanged() {
|
||||
@@ -378,7 +396,7 @@ Singleton {
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: root.activePlayer?.playbackState === MprisPlaybackState.Playing
|
||||
running: root.positionConsumers > 0 && root.activePlayer?.playbackState === MprisPlaybackState.Playing
|
||||
repeat: true
|
||||
onTriggered: root.activePlayer?.positionChanged()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ Singleton {
|
||||
property bool automationAvailable: false
|
||||
property bool gammaControlAvailable: false
|
||||
property int resumeRecoveryAttempt: 0
|
||||
readonly property bool _gammaNeeded: nightModeEnabled || SessionData.displayGamma !== 1.0 || SessionData.displayContrast !== 1.0
|
||||
|
||||
property var gammaState: ({})
|
||||
property int gammaCurrentTemp: gammaState?.currentTemp ?? 0
|
||||
@@ -343,7 +344,6 @@ Singleton {
|
||||
|
||||
function runResumeRecoveryPass() {
|
||||
checkGammaControlAvailability();
|
||||
BrightnessService.rescanDevices();
|
||||
|
||||
if (nightModeEnabled) {
|
||||
evaluateNightMode();
|
||||
@@ -375,7 +375,8 @@ Singleton {
|
||||
} else {
|
||||
gammaControlAvailable = true;
|
||||
automationAvailable = true;
|
||||
applyGammaAdjustments();
|
||||
if (_gammaNeeded)
|
||||
applyGammaAdjustments();
|
||||
|
||||
if (nightModeEnabled) {
|
||||
_setNightModeEnabled(!nightModePaused, true, success => {
|
||||
@@ -467,6 +468,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
enabled: root._gammaNeeded
|
||||
|
||||
function onSessionResumed() {
|
||||
resumeRecoveryAttempt = 0;
|
||||
@@ -541,6 +543,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: ToplevelManager
|
||||
enabled: root.nightModeEnabled
|
||||
function onActiveToplevelChanged() {
|
||||
root.handleNightModeExceptions();
|
||||
}
|
||||
@@ -548,6 +551,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: ToplevelManager.activeToplevel
|
||||
enabled: root.nightModeEnabled
|
||||
function onFullscreenChanged() {
|
||||
root.handleNightModeExceptions();
|
||||
}
|
||||
@@ -555,6 +559,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: CompositorService
|
||||
enabled: root.nightModeEnabled
|
||||
function onInOverviewChanged() {
|
||||
root.handleNightModeExceptions();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ QtObject {
|
||||
property var _outputCycleState: OutputCycleState.emptyState()
|
||||
|
||||
Component.onCompleted: resolveOutputCycle()
|
||||
onIsNiriChanged: resolveOutputCycle()
|
||||
|
||||
readonly property Connections outputConnection: Connections {
|
||||
target: root.wlrOutputService
|
||||
enabled: root.isNiri
|
||||
|
||||
function onStateChanged() {
|
||||
root.resolveOutputCycle();
|
||||
@@ -23,6 +25,7 @@ QtObject {
|
||||
|
||||
readonly property Connections socketConnection: Connections {
|
||||
target: root.socket
|
||||
enabled: root.isNiri
|
||||
|
||||
function onConnectionStateChanged() {
|
||||
if (root.socket.linkUp)
|
||||
@@ -31,9 +34,10 @@ QtObject {
|
||||
}
|
||||
|
||||
function outputDescriptors() {
|
||||
return wlrOutputService.outputs
|
||||
.filter(output => output.name)
|
||||
.map(output => ({ id: output.name, enabled: output.enabled }));
|
||||
return wlrOutputService.outputs.filter(output => output.name).map(output => ({
|
||||
id: output.name,
|
||||
enabled: output.enabled
|
||||
}));
|
||||
}
|
||||
|
||||
function sendOutputAction(outputName, action) {
|
||||
|
||||
@@ -92,17 +92,14 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: Theme
|
||||
enabled: CompositorService.isNiri
|
||||
|
||||
function onScreenTransitionNeeded() {
|
||||
if (CompositorService.isNiri) {
|
||||
root.doScreenTransition();
|
||||
}
|
||||
root.doScreenTransition();
|
||||
}
|
||||
|
||||
function onThemeGenerationStarting() {
|
||||
if (CompositorService.isNiri) {
|
||||
root.suppressNextToast();
|
||||
}
|
||||
root.suppressNextToast();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +130,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
enabled: CompositorService.isNiri
|
||||
function onBarConfigsChanged() {
|
||||
const newGaps = Math.max(4, (SettingsData.getPrimaryBarConfig()?.spacing ?? 4));
|
||||
if (newGaps === root._lastGapValue)
|
||||
@@ -145,9 +143,10 @@ Singleton {
|
||||
Connections {
|
||||
target: CompositorService
|
||||
function onIsNiriChanged() {
|
||||
if (CompositorService.isNiri) {
|
||||
generateNiriInputConfig();
|
||||
}
|
||||
if (!CompositorService.isNiri)
|
||||
return;
|
||||
generateNiriInputConfig();
|
||||
generateNiriLayoutConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,12 +278,15 @@ Singleton {
|
||||
connected: CompositorService.isNiri
|
||||
}
|
||||
|
||||
NiriOutputCycle {
|
||||
id: outputCycle
|
||||
wlrOutputService: WlrOutputService
|
||||
socket: requestSocket
|
||||
isNiri: CompositorService.isNiri
|
||||
currentOutput: root.currentOutput
|
||||
Loader {
|
||||
id: outputCycleLoader
|
||||
active: CompositorService.isNiri
|
||||
sourceComponent: NiriOutputCycle {
|
||||
wlrOutputService: WlrOutputService
|
||||
socket: requestSocket
|
||||
isNiri: CompositorService.isNiri
|
||||
currentOutput: root.currentOutput
|
||||
}
|
||||
}
|
||||
|
||||
function fetchOutputs() {
|
||||
@@ -310,7 +312,9 @@ Singleton {
|
||||
}
|
||||
|
||||
function cycleSingleOutput() {
|
||||
return outputCycle.cycleSingleOutput();
|
||||
if (!outputCycleLoader.item)
|
||||
return "OUTPUT_CYCLE_UNSUPPORTED";
|
||||
return outputCycleLoader.item.cycleSingleOutput();
|
||||
}
|
||||
|
||||
function updateDisplayScales() {
|
||||
@@ -1304,13 +1308,20 @@ window-rule {
|
||||
writeAlttabProcess.running = true;
|
||||
}
|
||||
|
||||
for (const name of ["outputs", "binds", "cursor", "windowrules", "colors", "alttab", "layout", "input"]) {
|
||||
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)
|
||||
log.warn("Failed to ensure " + name + ".kdl, exit code:", exitCode);
|
||||
});
|
||||
}
|
||||
ensureDmsConfigFiles(niriDmsDir);
|
||||
}
|
||||
|
||||
property bool _dmsConfigFilesEnsured: false
|
||||
|
||||
function ensureDmsConfigFiles(niriDmsDir) {
|
||||
if (_dmsConfigFilesEnsured)
|
||||
return;
|
||||
_dmsConfigFilesEnsured = true;
|
||||
const script = `mkdir -p "${niriDmsDir}" && for f in outputs binds cursor windowrules colors alttab layout input; do [ -f "${niriDmsDir}/$f.kdl" ] || touch "${niriDmsDir}/$f.kdl"; done`;
|
||||
Proc.runCommand("niri-ensure-dms-configs", ["sh", "-c", script], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to ensure dms config files, exit code:", exitCode);
|
||||
});
|
||||
}
|
||||
|
||||
function generateNiriBlurrule() {
|
||||
|
||||
@@ -71,10 +71,6 @@ Singleton {
|
||||
conflictDiskContent = "";
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ensureDirectories();
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: metadataFile
|
||||
path: root.refCount > 0 ? root.metadataPath : ""
|
||||
@@ -100,13 +96,21 @@ Singleton {
|
||||
}
|
||||
|
||||
onRefCountChanged: {
|
||||
if (refCount === 1 && !metadataLoaded) {
|
||||
metadataFile.path = "";
|
||||
metadataFile.path = root.metadataPath;
|
||||
}
|
||||
if (refCount !== 1)
|
||||
return;
|
||||
ensureDirectories();
|
||||
if (metadataLoaded)
|
||||
return;
|
||||
metadataFile.path = "";
|
||||
metadataFile.path = root.metadataPath;
|
||||
}
|
||||
|
||||
property bool _directoriesEnsured: false
|
||||
|
||||
function ensureDirectories() {
|
||||
if (_directoriesEnsured)
|
||||
return;
|
||||
_directoriesEnsured = true;
|
||||
Proc.runCommand("", ["mkdir", "-p", root.baseDir, root.filesDir], null);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,13 +70,12 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
_recomputeGroups();
|
||||
Quickshell.execDetached(["mkdir", "-p", Paths.strip(Paths.cache)]);
|
||||
Quickshell.execDetached(["mkdir", "-p", imageCacheDir]);
|
||||
Quickshell.execDetached(["mkdir", "-p", Paths.strip(Paths.cache), imageCacheDir]);
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: historyFileView
|
||||
path: root.historyFile
|
||||
path: SettingsData.notificationHistoryEnabled ? root.historyFile : ""
|
||||
printErrors: false
|
||||
onLoaded: root.loadHistory()
|
||||
onLoadFailed: error => {
|
||||
@@ -173,6 +172,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function performSaveHistory() {
|
||||
if (historyFileView.path === "")
|
||||
return;
|
||||
try {
|
||||
historyAdapter.notifications = historyList;
|
||||
historyFileView.writeAdapter();
|
||||
@@ -559,10 +560,17 @@ Singleton {
|
||||
}
|
||||
historyList = [];
|
||||
historyAdapter.notifications = [];
|
||||
if (historyFileView.path === "") {
|
||||
Quickshell.execDetached(["rm", "-f", root.historyFile]);
|
||||
return;
|
||||
}
|
||||
historyFileView.writeAdapter();
|
||||
}
|
||||
|
||||
property bool overlayOpen: false
|
||||
|
||||
function onOverlayOpen() {
|
||||
overlayOpen = true;
|
||||
popupsDisabled = true;
|
||||
markNotificationsSeen();
|
||||
addGate.stop();
|
||||
@@ -580,6 +588,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function onOverlayClose() {
|
||||
overlayOpen = false;
|
||||
popupsDisabled = false;
|
||||
markNotificationsSeen();
|
||||
processQueue();
|
||||
@@ -600,7 +609,7 @@ Singleton {
|
||||
id: timeUpdateTimer
|
||||
interval: 30000
|
||||
repeat: true
|
||||
running: root.allWrappers.length > 0 || visibleNotifications.length > 0
|
||||
running: root.overlayOpen && (root.allWrappers.length > 0 || visibleNotifications.length > 0)
|
||||
triggeredOnStart: false
|
||||
onTriggered: {
|
||||
root.timeUpdateTick = !root.timeUpdateTick;
|
||||
@@ -1495,7 +1504,7 @@ Singleton {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PrivacyService
|
||||
target: SettingsData.notificationDndWhileScreenSharing ? PrivacyService : null
|
||||
function onScreensharingActiveChanged() {
|
||||
SessionData.syncScreenShareDnd();
|
||||
}
|
||||
|
||||
@@ -144,10 +144,6 @@ Singleton {
|
||||
}
|
||||
|
||||
function setSystemColorScheme(isLightMode) {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const preferLight = isLightMode && systemColorScheme === 2;
|
||||
const targetScheme = isLightMode ? (preferLight ? "prefer-light" : "default") : "prefer-dark";
|
||||
|
||||
@@ -209,6 +205,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: Theme
|
||||
enabled: SettingsData.syncModeWithPortal
|
||||
|
||||
function onIsLightModeChanged() {
|
||||
root.setSystemColorScheme(Theme.isLightMode);
|
||||
|
||||
@@ -13,7 +13,7 @@ Singleton {
|
||||
property int previousProfile: -1
|
||||
property bool daemonRunning: true
|
||||
|
||||
readonly property bool available: typeof PowerProfiles !== "undefined" && daemonRunning
|
||||
readonly property bool available: daemonRunning
|
||||
|
||||
function checkDaemon() {
|
||||
if (!DMSService.isConnected || !DMSService.capabilities.includes("dbus"))
|
||||
@@ -87,17 +87,14 @@ Singleton {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: typeof PowerProfiles !== "undefined" ? PowerProfiles : null
|
||||
target: PowerProfiles
|
||||
|
||||
function onProfileChanged() {
|
||||
if (typeof PowerProfiles !== "undefined") {
|
||||
root.daemonRunning = true;
|
||||
root.previousProfile = root.currentProfile;
|
||||
root.currentProfile = PowerProfiles.profile;
|
||||
if (root.previousProfile !== -1) {
|
||||
root.profileChanged(root.currentProfile);
|
||||
}
|
||||
}
|
||||
root.daemonRunning = true;
|
||||
root.previousProfile = root.currentProfile;
|
||||
root.currentProfile = PowerProfiles.profile;
|
||||
if (root.previousProfile !== -1)
|
||||
root.profileChanged(root.currentProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,9 +107,7 @@ Singleton {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (typeof PowerProfiles !== "undefined") {
|
||||
root.currentProfile = PowerProfiles.profile;
|
||||
root.previousProfile = PowerProfiles.profile;
|
||||
}
|
||||
root.currentProfile = PowerProfiles.profile;
|
||||
root.previousProfile = PowerProfiles.profile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,182 +1,195 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount: 0
|
||||
readonly property bool watching: refCount > 0 || SettingsData.notificationDndWhileScreenSharing
|
||||
|
||||
function addRef() {
|
||||
refCount++;
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
refCount = Math.max(0, refCount - 1);
|
||||
}
|
||||
|
||||
readonly property bool microphoneActive: {
|
||||
if (!AudioService.pipewireReady) {
|
||||
return false
|
||||
if (!watching || !AudioService.pipewireReady) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
||||
const node = AudioService.pipewireNodes[i]
|
||||
const node = AudioService.pipewireNodes[i];
|
||||
if (!node) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.properties?.["media.class"] === "Stream/Input/Audio") {
|
||||
if (!looksLikeSystemVirtualMic(node)) {
|
||||
if (node.audio && node.audio.muted) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
readonly property bool cameraActive: {
|
||||
if (!AudioService.pipewireReady) {
|
||||
return false
|
||||
if (!watching || !AudioService.pipewireReady) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
||||
const node = AudioService.pipewireNodes[i]
|
||||
const node = AudioService.pipewireNodes[i];
|
||||
if (!node || !node.ready || node.properties?.["media.role"] === "Screen") {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.properties && node.properties["media.class"] === "Stream/Input/Video") {
|
||||
if (node.properties["stream.is-live"] === "true") {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
readonly property bool screensharingActive: {
|
||||
if (!watching) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompositorService.isNiri && NiriService.hasActiveCast) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!AudioService.pipewireReady) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
||||
const node = AudioService.pipewireNodes[i]
|
||||
const node = AudioService.pipewireNodes[i];
|
||||
if (!node || !node.ready) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AudioService.isPipewireVideoSource(node)) {
|
||||
if (looksLikeScreencast(node)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (AudioService.isPipewireVideoSource(node)) {
|
||||
if (looksLikeScreencast(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.properties && node.properties["media.class"] === "Stream/Output/Video") {
|
||||
if (looksLikeScreencast(node)) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.properties && node.properties["media.class"] === "Stream/Input/Audio") {
|
||||
const mediaName = (node.properties["media.name"] || "").toLowerCase()
|
||||
const appName = (node.properties["application.name"] || "").toLowerCase()
|
||||
const mediaName = (node.properties["media.name"] || "").toLowerCase();
|
||||
const appName = (node.properties["application.name"] || "").toLowerCase();
|
||||
|
||||
if (mediaName.includes("desktop") || appName.includes("screen") || appName === "obs") {
|
||||
if (node.properties["stream.is-live"] === "true") {
|
||||
if (node.audio && node.audio.muted) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
readonly property bool anyPrivacyActive: microphoneActive || cameraActive || screensharingActive
|
||||
|
||||
function looksLikeSystemVirtualMic(node) {
|
||||
if (!node) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
const name = (node.name || "").toLowerCase()
|
||||
const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase()
|
||||
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase()
|
||||
const combined = name + " " + mediaName + " " + appName
|
||||
return /cava|monitor|system/.test(combined)
|
||||
const name = (node.name || "").toLowerCase();
|
||||
const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase();
|
||||
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase();
|
||||
const combined = name + " " + mediaName + " " + appName;
|
||||
return /cava|monitor|system/.test(combined);
|
||||
}
|
||||
|
||||
function looksLikeScreencast(node) {
|
||||
if (!node) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase()
|
||||
const nodeName = (node.name || "").toLowerCase()
|
||||
const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase()
|
||||
const combined = appName + " " + nodeName + " " + mediaName
|
||||
return /xdg-desktop-portal|xdpw|screencast|screen-cast|screen|gnome shell|kwin|obs|niri/.test(combined)
|
||||
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase();
|
||||
const nodeName = (node.name || "").toLowerCase();
|
||||
const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase();
|
||||
const combined = appName + " " + nodeName + " " + mediaName;
|
||||
return /xdg-desktop-portal|xdpw|screencast|screen-cast|screen|gnome shell|kwin|obs|niri/.test(combined);
|
||||
}
|
||||
|
||||
function screencastSourceIds() {
|
||||
const ids = []
|
||||
const ids = [];
|
||||
|
||||
if (CompositorService.isNiri) {
|
||||
for (const cast of NiriService.casts) {
|
||||
if (cast && cast.is_active) {
|
||||
ids.push("niri:" + cast.stream_id)
|
||||
ids.push("niri:" + cast.stream_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!AudioService.pipewireReady) {
|
||||
return ids
|
||||
return ids;
|
||||
}
|
||||
|
||||
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
||||
const node = AudioService.pipewireNodes[i]
|
||||
const node = AudioService.pipewireNodes[i];
|
||||
if (!node || !node.ready) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
const isVideoSource = node.properties?.["media.class"] === "Video/Source"
|
||||
const isVideoStream = node.properties && node.properties["media.class"] === "Stream/Output/Video"
|
||||
const isVideoSource = node.properties?.["media.class"] === "Video/Source";
|
||||
const isVideoStream = node.properties && node.properties["media.class"] === "Stream/Output/Video";
|
||||
if ((isVideoSource || isVideoStream) && looksLikeScreencast(node)) {
|
||||
ids.push("pw:" + node.id)
|
||||
ids.push("pw:" + node.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids.sort()
|
||||
return ids.sort();
|
||||
}
|
||||
|
||||
function getMicrophoneStatus() {
|
||||
return microphoneActive ? "active" : "inactive"
|
||||
return microphoneActive ? "active" : "inactive";
|
||||
}
|
||||
|
||||
function getCameraStatus() {
|
||||
return cameraActive ? "active" : "inactive"
|
||||
return cameraActive ? "active" : "inactive";
|
||||
}
|
||||
|
||||
function getScreensharingStatus() {
|
||||
return screensharingActive ? "active" : "inactive"
|
||||
return screensharingActive ? "active" : "inactive";
|
||||
}
|
||||
|
||||
function getPrivacySummary() {
|
||||
const active = []
|
||||
const active = [];
|
||||
if (microphoneActive) {
|
||||
active.push("microphone")
|
||||
active.push("microphone");
|
||||
}
|
||||
if (cameraActive) {
|
||||
active.push("camera")
|
||||
active.push("camera");
|
||||
}
|
||||
if (screensharingActive) {
|
||||
active.push("screensharing")
|
||||
active.push("screensharing");
|
||||
}
|
||||
|
||||
return active.length > 0 ? `Privacy active: ${active.join(", ")}` : "No privacy concerns detected"
|
||||
return active.length > 0 ? `Privacy active: ${active.join(", ")}` : "No privacy concerns detected";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ Singleton {
|
||||
readonly property int batteryRefreshRateTarget: 60000
|
||||
readonly property int batteryRefreshRateTolerance: 1000
|
||||
|
||||
property int powerSyncRecoveryAttempt: 0
|
||||
readonly property var powerSyncRecoveryIntervals: [1000, 5000, 15000, 30000]
|
||||
property int _resumeRecoveryAttempt: 0
|
||||
readonly property var _resumeRecoveryIntervals: [1000, 5000, 15000, 30000]
|
||||
property var _lastAppliedTargets: ({})
|
||||
|
||||
Timer {
|
||||
@@ -38,38 +38,40 @@ Singleton {
|
||||
id: startupRefreshRateSync
|
||||
interval: 500
|
||||
repeat: false
|
||||
running: true
|
||||
running: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
onTriggered: root.requestSync("startup")
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: powerSyncRecoveryTimer
|
||||
interval: root.powerSyncRecoveryIntervals[0]
|
||||
id: resumeRecoveryTimer
|
||||
interval: root._resumeRecoveryIntervals[0]
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.requestSync("resume-reconcile");
|
||||
root.powerSyncRecoveryAttempt++;
|
||||
if (root.powerSyncRecoveryAttempt < root.powerSyncRecoveryIntervals.length) {
|
||||
interval = root.powerSyncRecoveryIntervals[root.powerSyncRecoveryAttempt];
|
||||
root._resumeRecoveryAttempt++;
|
||||
if (root._resumeRecoveryAttempt < root._resumeRecoveryIntervals.length) {
|
||||
interval = root._resumeRecoveryIntervals[root._resumeRecoveryAttempt];
|
||||
restart();
|
||||
return;
|
||||
}
|
||||
root.powerSyncRecoveryAttempt = 0;
|
||||
interval = root.powerSyncRecoveryIntervals[0];
|
||||
root._resumeRecoveryAttempt = 0;
|
||||
interval = root._resumeRecoveryIntervals[0];
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
enabled: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
function onSessionResumed() {
|
||||
root.powerSyncRecoveryAttempt = 0;
|
||||
powerSyncRecoveryTimer.interval = root.powerSyncRecoveryIntervals[0];
|
||||
powerSyncRecoveryTimer.restart();
|
||||
root._resumeRecoveryAttempt = 0;
|
||||
resumeRecoveryTimer.interval = root._resumeRecoveryIntervals[0];
|
||||
resumeRecoveryTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: BatteryService
|
||||
enabled: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
function onIsPluggedInChanged() {
|
||||
root.requestSync("power-change");
|
||||
}
|
||||
@@ -78,12 +80,14 @@ Singleton {
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onLowerDisplayRefreshRateOnBatteryChanged() {
|
||||
resumeRecoveryTimer.stop();
|
||||
root.requestSync("setting-change");
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
enabled: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
function onActiveDisplayProfileChanged() {
|
||||
root.requestSync("profile-change");
|
||||
}
|
||||
@@ -95,6 +99,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: NiriService
|
||||
enabled: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
function onOutputsChanged() {
|
||||
root.requestSync("output-change");
|
||||
}
|
||||
@@ -102,6 +107,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: WlrOutputService
|
||||
enabled: SettingsData.lowerDisplayRefreshRateOnBattery
|
||||
function onStateChanged() {
|
||||
root.requestSync("output-change");
|
||||
}
|
||||
|
||||
@@ -64,12 +64,7 @@ Singleton {
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
detectUwsmProcess.running = true;
|
||||
detectElogindProcess.running = true;
|
||||
detectLoginctlProcess.running = true;
|
||||
detectSystemctlProcess.running = true;
|
||||
detectHibernateProcess.running = true;
|
||||
detectPrimeRunProcess.running = true;
|
||||
detectEnvironmentProcess.running = true;
|
||||
if (!SettingsData.loginctlLockIntegration) {
|
||||
log.debug("loginctl lock integration disabled by user");
|
||||
return;
|
||||
@@ -91,54 +86,49 @@ Singleton {
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectUwsmProcess
|
||||
id: detectEnvironmentProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "command -v uwsm > /dev/null 2>&1 && systemctl --user is-active --quiet 'wayland-wm@*.service' 2> /dev/null"]
|
||||
command: ["sh", "-c", `uwsm=0; command -v uwsm >/dev/null 2>&1 && systemctl --user is-active --quiet 'wayland-wm@*.service' 2>/dev/null && uwsm=1
|
||||
elogind=0; ps -eo comm= | grep -qE '^(elogind|elogind-daemon)$' && elogind=1
|
||||
loginctl=0; command -v loginctl >/dev/null 2>&1 && loginctl=1
|
||||
systemctl=0; usermgr=0
|
||||
if command -v systemctl >/dev/null 2>&1; then systemctl=1; systemctl --user --no-pager show-environment >/dev/null 2>&1 && usermgr=1; fi
|
||||
hibernate=0; grep -q disk /sys/power/state 2>/dev/null && hibernate=1
|
||||
nvidia=; command -v prime-run >/dev/null 2>&1 && nvidia=prime-run
|
||||
[ -z "$nvidia" ] && command -v nvidia-offload >/dev/null 2>&1 && nvidia=nvidia-offload
|
||||
printf 'uwsm=%s\nelogind=%s\nloginctl=%s\nsystemctl=%s\nusermgr=%s\nhibernate=%s\nnvidia=%s\n' "$uwsm" "$elogind" "$loginctl" "$systemctl" "$usermgr" "$hibernate" "$nvidia"`]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
hasUwsm = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectElogindProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
log.debug("Elogind detection exited with code", exitCode);
|
||||
isElogind = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectLoginctlProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "command -v loginctl"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
loginctlCommandAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectSystemctlProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "command -v systemctl > /dev/null || exit 1; systemctl --user show-environment > /dev/null 2>&1 || exit 2"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
systemctlCommandAvailable = (exitCode === 0 || exitCode === 2);
|
||||
userManagerAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectHibernateProcess
|
||||
running: false
|
||||
command: isBSD ? ["sh", "-c", "exit 1"] : ["grep", "-q", "disk", "/sys/power/state"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
hibernateSupported = (exitCode === 0);
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: line => {
|
||||
const eq = line.indexOf("=");
|
||||
if (eq <= 0)
|
||||
return;
|
||||
const value = line.substring(eq + 1).trim();
|
||||
switch (line.substring(0, eq)) {
|
||||
case "uwsm":
|
||||
root.hasUwsm = value === "1";
|
||||
return;
|
||||
case "elogind":
|
||||
root.isElogind = value === "1";
|
||||
return;
|
||||
case "loginctl":
|
||||
root.loginctlCommandAvailable = value === "1";
|
||||
return;
|
||||
case "systemctl":
|
||||
root.systemctlCommandAvailable = value === "1";
|
||||
return;
|
||||
case "usermgr":
|
||||
root.userManagerAvailable = value === "1";
|
||||
return;
|
||||
case "hibernate":
|
||||
root.hibernateSupported = !root.isBSD && value === "1";
|
||||
return;
|
||||
case "nvidia":
|
||||
root.nvidiaCommand = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,32 +153,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectPrimeRunProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "command -v prime-run"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode === 0) {
|
||||
nvidiaCommand = "prime-run";
|
||||
} else {
|
||||
detectNvidiaOffloadProcess.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectNvidiaOffloadProcess
|
||||
running: false
|
||||
command: ["sh", "-c", "command -v nvidia-offload"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode === 0) {
|
||||
nvidiaCommand = "nvidia-offload";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: uwsmLogout
|
||||
property bool notRunning: false
|
||||
|
||||
@@ -14,6 +14,8 @@ Singleton {
|
||||
property string currentSessionId: ""
|
||||
property string currentSeat: ""
|
||||
property bool refreshing: false
|
||||
property bool loaded: false
|
||||
property var _afterLoad: []
|
||||
property bool loginctlAvailable: false
|
||||
property bool isBSD: Qt.platform.os === "unix"
|
||||
|
||||
@@ -45,6 +47,24 @@ Singleton {
|
||||
return sessions.filter(s => !s.current);
|
||||
}
|
||||
|
||||
function ensureLoaded(fn) {
|
||||
if (loaded) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
_afterLoad = _afterLoad.concat([fn]);
|
||||
refresh();
|
||||
}
|
||||
|
||||
function _finishRefresh() {
|
||||
refreshing = false;
|
||||
loaded = true;
|
||||
const queued = _afterLoad;
|
||||
_afterLoad = [];
|
||||
for (const fn of queued)
|
||||
fn();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (refreshing)
|
||||
return;
|
||||
@@ -72,20 +92,22 @@ Singleton {
|
||||
const fields = (lines[0] || "self|0|").split("|");
|
||||
root.currentSessionId = fields[0] || "self";
|
||||
root.currentSeat = "";
|
||||
root.sessions = [{
|
||||
sessionId: root.currentSessionId,
|
||||
uid: parseInt(fields[1] || "0", 10),
|
||||
username: fields[2] || "",
|
||||
seat: "",
|
||||
tty: lines[1] || "",
|
||||
type: (Quickshell.env("WAYLAND_DISPLAY") || "") !== "" ? "wayland" : "",
|
||||
sessionClass: "user",
|
||||
active: true,
|
||||
state: "active",
|
||||
remote: false,
|
||||
current: true
|
||||
}];
|
||||
root.refreshing = false;
|
||||
root.sessions = [
|
||||
{
|
||||
sessionId: root.currentSessionId,
|
||||
uid: parseInt(fields[1] || "0", 10),
|
||||
username: fields[2] || "",
|
||||
seat: "",
|
||||
tty: lines[1] || "",
|
||||
type: (Quickshell.env("WAYLAND_DISPLAY") || "") !== "" ? "wayland" : "",
|
||||
sessionClass: "user",
|
||||
active: true,
|
||||
state: "active",
|
||||
remote: false,
|
||||
current: true
|
||||
}
|
||||
];
|
||||
root._finishRefresh();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -136,7 +158,7 @@ Singleton {
|
||||
return parseInt(a.sessionId, 10) - parseInt(b.sessionId, 10);
|
||||
});
|
||||
root.sessions = list;
|
||||
root.refreshing = false;
|
||||
root._finishRefresh();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -239,6 +261,8 @@ Singleton {
|
||||
target: "sessions"
|
||||
|
||||
function list(): string {
|
||||
if (!root.loaded)
|
||||
root.refresh();
|
||||
const lines = [];
|
||||
for (let i = 0; i < root.sessions.length; i++) {
|
||||
const s = root.sessions[i];
|
||||
@@ -261,17 +285,15 @@ Singleton {
|
||||
function activate(sessionId: string): string {
|
||||
if (!sessionId)
|
||||
return "ERROR: missing session id";
|
||||
root.activate(sessionId, null);
|
||||
root.ensureLoaded(() => root.activate(sessionId, null));
|
||||
return "ok";
|
||||
}
|
||||
|
||||
function switchTo(target: string): string {
|
||||
if (!target)
|
||||
return "ERROR: missing target (username or session id)";
|
||||
root.switchToUser(target, null);
|
||||
root.ensureLoaded(() => root.switchToUser(target, null));
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: refresh()
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ Singleton {
|
||||
property var settingsIndex: []
|
||||
property bool indexLoaded: false
|
||||
property var _translatedCache: []
|
||||
property bool _cacheDirty: false
|
||||
property int _scrollPass: 0
|
||||
property int _stablePasses: 0
|
||||
property real _appliedY: 0
|
||||
@@ -339,6 +340,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function _rebuildTranslationCache() {
|
||||
_cacheDirty = false;
|
||||
var cache = [];
|
||||
var items = settingsIndex.concat(_runtimeSearchEntries());
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
@@ -543,7 +545,7 @@ Singleton {
|
||||
function _refreshTranslatedCache() {
|
||||
if (!indexLoaded)
|
||||
return;
|
||||
_rebuildTranslationCache();
|
||||
_cacheDirty = true;
|
||||
if (query)
|
||||
results = _searchEntries(query, 15);
|
||||
}
|
||||
@@ -553,6 +555,8 @@ Singleton {
|
||||
if (!queryLower)
|
||||
return [];
|
||||
|
||||
if (_cacheDirty)
|
||||
_rebuildTranslationCache();
|
||||
var querySquash = _squash(queryLower);
|
||||
var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0);
|
||||
var scored = [];
|
||||
|
||||
@@ -19,30 +19,18 @@ Singleton {
|
||||
Process {
|
||||
id: versionDetection
|
||||
running: true
|
||||
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -d .git ]; then echo "(git) $(git rev-parse --short HEAD)"; elif [ -f VERSION ]; then cat VERSION; fi`]
|
||||
command: ["sh", "-c", `cd "${Quickshell.shellDir}" || exit 0
|
||||
semver=$(cat VERSION 2>/dev/null); codename=$(cat CODENAME 2>/dev/null)
|
||||
if [ -d .git ]; then shell="(git) $(git rev-parse --short HEAD)"; else shell=$semver; fi
|
||||
printf '%s\\n%s\\n%s\\n' "$shell" "$semver" "$codename"`]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: shellVersion = text.trim()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: semverDetection
|
||||
running: true
|
||||
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f VERSION ]; then cat VERSION; fi`]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: semverVersion = text.trim()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: codenameDetection
|
||||
running: true
|
||||
command: ["sh", "-c", `cd "${Quickshell.shellDir}" && if [ -f CODENAME ]; then cat CODENAME; fi`]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: shellCodename = text.trim()
|
||||
onStreamFinished: {
|
||||
const lines = text.split("\n");
|
||||
shellVersion = (lines[0] || "").trim();
|
||||
semverVersion = (lines[1] || "").trim();
|
||||
shellCodename = (lines[2] || "").trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -148,9 +148,11 @@ Singleton {
|
||||
const has = DMSService.capabilities.includes("sysupdate");
|
||||
if (has && !sysupdateAvailable) {
|
||||
sysupdateAvailable = true;
|
||||
requestState();
|
||||
// The daemon persists its last check but not the interval; re-apply it on every fresh connection.
|
||||
setInterval(SettingsData.updaterIntervalSeconds);
|
||||
if (pollWanted) {
|
||||
requestState();
|
||||
// The daemon persists its last check but not the interval; re-apply it on every fresh connection.
|
||||
setInterval(SettingsData.updaterIntervalSeconds);
|
||||
}
|
||||
} else if (!has) {
|
||||
sysupdateAvailable = false;
|
||||
}
|
||||
@@ -400,6 +402,7 @@ Singleton {
|
||||
onPollWantedChanged: {
|
||||
if (!pollWanted)
|
||||
_startupCheckDone = false;
|
||||
_syncSubscription();
|
||||
Qt.callLater(() => root._syncAcquire());
|
||||
Qt.callLater(() => root._maybeStartupCheck());
|
||||
}
|
||||
@@ -415,6 +418,19 @@ Singleton {
|
||||
loadReleases(false);
|
||||
}
|
||||
|
||||
function _syncSubscription() {
|
||||
if (!pollWanted) {
|
||||
if (DMSService.activeSubscriptions.includes("sysupdate"))
|
||||
DMSService.removeSubscription("sysupdate");
|
||||
return;
|
||||
}
|
||||
DMSService.addSubscription("sysupdate");
|
||||
if (!sysupdateAvailable)
|
||||
return;
|
||||
requestState();
|
||||
setInterval(SettingsData.updaterIntervalSeconds);
|
||||
}
|
||||
|
||||
property bool _acquired: false
|
||||
// Releasing the ref parks the daemon scheduler; its deadline is kept, so checks resume on AC.
|
||||
readonly property bool pausedOnBattery: SettingsData.updaterPauseOnBattery && BatteryService.batteryAvailable && !BatteryService.isPluggedIn
|
||||
|
||||
@@ -127,7 +127,7 @@ Singleton {
|
||||
|
||||
if (!available)
|
||||
return;
|
||||
if (!stateInitialized) {
|
||||
if (refCount > 0 && !stateInitialized) {
|
||||
stateInitialized = true;
|
||||
getStatus();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,16 @@ Singleton {
|
||||
readonly property var log: Log.scoped("ThemeAutoService")
|
||||
|
||||
property bool active: false
|
||||
readonly property bool backendAvailable: DMSService.isConnected && DMSService.capabilities.includes("theme.auto")
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
DMSService.addSubscription("theme.auto");
|
||||
return;
|
||||
}
|
||||
if (DMSService.activeSubscriptions.includes("theme.auto"))
|
||||
DMSService.removeSubscription("theme.auto");
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled) {
|
||||
@@ -20,7 +30,6 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
enabled: typeof SessionData !== "undefined"
|
||||
|
||||
function onThemeModeAutoEnabledChanged() {
|
||||
if (SessionData.themeModeAutoEnabled) {
|
||||
@@ -29,128 +38,112 @@ Singleton {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
enabled: root.active
|
||||
|
||||
function onThemeModeAutoModeChanged() {
|
||||
if (root.active) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeStartHourChanged() {
|
||||
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeStartMinuteChanged() {
|
||||
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeEndHourChanged() {
|
||||
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeEndMinuteChanged() {
|
||||
if (root.active && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
|
||||
function onThemeModeShareGammaSettingsChanged() {
|
||||
if (root.active) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
|
||||
function onThemeModeStartHourChanged() {
|
||||
root.resyncOwnSchedule();
|
||||
}
|
||||
|
||||
function onThemeModeStartMinuteChanged() {
|
||||
root.resyncOwnSchedule();
|
||||
}
|
||||
|
||||
function onThemeModeEndHourChanged() {
|
||||
root.resyncOwnSchedule();
|
||||
}
|
||||
|
||||
function onThemeModeEndMinuteChanged() {
|
||||
root.resyncOwnSchedule();
|
||||
}
|
||||
|
||||
function onNightModeStartHourChanged() {
|
||||
if (root.active && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
root.resyncSharedSchedule();
|
||||
}
|
||||
|
||||
function onNightModeStartMinuteChanged() {
|
||||
if (root.active && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
root.resyncSharedSchedule();
|
||||
}
|
||||
|
||||
function onNightModeEndHourChanged() {
|
||||
if (root.active && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
root.resyncSharedSchedule();
|
||||
}
|
||||
|
||||
function onNightModeEndMinuteChanged() {
|
||||
if (root.active && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluate();
|
||||
root.syncTimeSchedule();
|
||||
}
|
||||
root.resyncSharedSchedule();
|
||||
}
|
||||
|
||||
function onLatitudeChanged() {
|
||||
if (root.active && SessionData.themeModeAutoMode === "location") {
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
root.evaluate();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
root.resyncCoordinates();
|
||||
}
|
||||
|
||||
function onLongitudeChanged() {
|
||||
if (root.active && SessionData.themeModeAutoMode === "location") {
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
||||
root.resyncCoordinates();
|
||||
}
|
||||
|
||||
function onNightModeUseIPLocationChanged() {
|
||||
if (SessionData.themeModeAutoMode !== "location")
|
||||
return;
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": SessionData.nightModeUseIPLocation
|
||||
}, response => {
|
||||
if (!response.error && !SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
root.evaluate();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
});
|
||||
root.evaluate();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeUseIPLocationChanged() {
|
||||
if (root.active && SessionData.themeModeAutoMode === "location") {
|
||||
if (typeof DMSService !== "undefined") {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": SessionData.nightModeUseIPLocation
|
||||
}, response => {
|
||||
if (!response.error && !SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
root.evaluate();
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
function resyncOwnSchedule() {
|
||||
if (SessionData.themeModeShareGammaSettings)
|
||||
return;
|
||||
evaluate();
|
||||
syncTimeSchedule();
|
||||
}
|
||||
|
||||
function resyncSharedSchedule() {
|
||||
if (!SessionData.themeModeShareGammaSettings)
|
||||
return;
|
||||
evaluate();
|
||||
syncTimeSchedule();
|
||||
}
|
||||
|
||||
function resyncCoordinates() {
|
||||
if (SessionData.themeModeAutoMode !== "location")
|
||||
return;
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
evaluate();
|
||||
syncLocationSchedule();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NightModeService
|
||||
enabled: typeof NightModeService !== "undefined" && typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location" && !root.backendAvailable()
|
||||
enabled: typeof NightModeService !== "undefined" && typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location" && !root.backendAvailable
|
||||
|
||||
function onGammaIsDayChanged() {
|
||||
if (Theme.isLightMode !== NightModeService.gammaIsDay) {
|
||||
@@ -178,7 +171,7 @@ Singleton {
|
||||
root.syncLocationSchedule();
|
||||
}
|
||||
|
||||
if (root.backendAvailable() && SessionData.themeModeAutoEnabled) {
|
||||
if (root.backendAvailable && SessionData.themeModeAutoEnabled) {
|
||||
DMSService.sendRequest("theme.auto.getState", null, response => {
|
||||
if (response && response.result) {
|
||||
root.applyBackendState(response.result);
|
||||
@@ -235,17 +228,13 @@ Singleton {
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (!backendAvailable()) {
|
||||
if (!backendAvailable) {
|
||||
evaluate();
|
||||
return;
|
||||
}
|
||||
DMSService.sendRequest("theme.auto.trigger", {});
|
||||
}
|
||||
|
||||
function backendAvailable() {
|
||||
return typeof DMSService !== "undefined" && DMSService.isConnected && Array.isArray(DMSService.capabilities) && DMSService.capabilities.includes("theme.auto");
|
||||
}
|
||||
|
||||
function applyBackendState(state) {
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -349,7 +338,7 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
if (backendAvailable()) {
|
||||
if (backendAvailable) {
|
||||
DMSService.sendRequest("theme.auto.getState", null, response => {
|
||||
if (response && response.result) {
|
||||
applyBackendState(response.result);
|
||||
|
||||
@@ -291,22 +291,25 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: MprisController.availablePlayers
|
||||
delegate: QtObject {
|
||||
required property MprisPlayer modelData
|
||||
readonly property bool playerIsPlaying: modelData.isPlaying
|
||||
readonly property string playerTrackTitle: modelData.trackTitle
|
||||
readonly property string playerTrackArtist: modelData.trackArtist
|
||||
readonly property string playerTrackAlbum: modelData.trackAlbum
|
||||
readonly property string playerTrackArtUrl: modelData.trackArtUrl
|
||||
readonly property var playerMetadata: modelData.metadata
|
||||
onPlayerIsPlayingChanged: root._updateArtUrl()
|
||||
onPlayerTrackTitleChanged: root._updateArtUrl()
|
||||
onPlayerTrackArtistChanged: root._updateArtUrl()
|
||||
onPlayerTrackAlbumChanged: root._updateArtUrl()
|
||||
onPlayerTrackArtUrlChanged: root._updateArtUrl()
|
||||
onPlayerMetadataChanged: root._updateArtUrl()
|
||||
Connections {
|
||||
target: root.activePlayer
|
||||
function onIsPlayingChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
function onTrackTitleChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
function onTrackArtistChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
function onTrackAlbumChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
function onTrackArtUrlChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
function onMetadataChanged() {
|
||||
root._updateArtUrl();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ Singleton {
|
||||
readonly property bool isEmpty: count === 0
|
||||
|
||||
property var availableFileManagers: ["default"]
|
||||
property bool _fileManagersDetected: false
|
||||
property var _pendingOpenOptions: null
|
||||
property string defaultFileManagerLabel: "default (xdg-open)"
|
||||
|
||||
signal emptyTrashConfirmRequested(int itemCount)
|
||||
@@ -55,16 +57,27 @@ Singleton {
|
||||
onStreamFinished: {
|
||||
const detected = (text || "").split("\n").map(s => s.trim()).filter(s => s.length > 0);
|
||||
root.availableFileManagers = ["default"].concat(detected).concat(["custom"]);
|
||||
if (!root._pendingOpenOptions)
|
||||
return;
|
||||
const options = root._pendingOpenOptions;
|
||||
root._pendingOpenOptions = null;
|
||||
root.openTrash(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Paths.trashHandler = (path, callback) => trashPath(path, callback);
|
||||
detectProc.running = true;
|
||||
refreshCount();
|
||||
}
|
||||
|
||||
function detectFileManagers() {
|
||||
if (_fileManagersDetected)
|
||||
return;
|
||||
_fileManagersDetected = true;
|
||||
detectProc.running = true;
|
||||
}
|
||||
|
||||
function refreshCount() {
|
||||
if (!monitoring)
|
||||
return;
|
||||
@@ -105,6 +118,11 @@ Singleton {
|
||||
openCustom(options);
|
||||
return;
|
||||
}
|
||||
if (!_fileManagersDetected) {
|
||||
_pendingOpenOptions = options;
|
||||
detectFileManagers();
|
||||
return;
|
||||
}
|
||||
if (availableFileManagers.indexOf(choice) < 0) {
|
||||
ToastService.showInfo(I18n.tr("Cannot open trash: '%1' is not installed", "dock trash toast, %1 is the file manager name").arg(choice), I18n.tr("Pick a different file manager in Settings → Dock → Trash."));
|
||||
return;
|
||||
|
||||
@@ -13,8 +13,9 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property bool cyclingActive: false
|
||||
property bool _lastPushedEnabled: true
|
||||
readonly property bool fullscreenShowing: {
|
||||
if (!ToplevelManager.toplevels?.values)
|
||||
if (!cyclingActive || !ToplevelManager.toplevels?.values)
|
||||
return false;
|
||||
for (const toplevel of ToplevelManager.toplevels.values) {
|
||||
if (toplevel.fullscreen && toplevel.activated)
|
||||
@@ -53,6 +54,7 @@ Singleton {
|
||||
|
||||
function onCapabilitiesReceived() {
|
||||
lastCycleSeq = -1;
|
||||
_lastPushedEnabled = true;
|
||||
updateCyclingState();
|
||||
}
|
||||
}
|
||||
@@ -95,6 +97,9 @@ Singleton {
|
||||
|
||||
function updateCyclingState() {
|
||||
cyclingActive = serverSchedulingAvailable && (SessionData.wallpaperCyclingEnabled || SessionData.perMonitorWallpaper);
|
||||
if (!cyclingActive && !_lastPushedEnabled)
|
||||
return;
|
||||
_lastPushedEnabled = cyclingActive;
|
||||
pushConfigToServer();
|
||||
}
|
||||
|
||||
|
||||
@@ -1080,6 +1080,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: LocationService
|
||||
enabled: root.refCount > 0 && SettingsData.weatherEnabled
|
||||
|
||||
function onLocationChanged(data) {
|
||||
if (!SettingsData.useAutoLocation)
|
||||
|
||||
@@ -47,9 +47,17 @@ Singleton {
|
||||
enabled: root.tracking
|
||||
}
|
||||
|
||||
onManagerActiveToplevelChanged: updateFocusedApp()
|
||||
onManagerActiveToplevelChanged: {
|
||||
if (!tracking)
|
||||
return;
|
||||
updateFocusedApp();
|
||||
}
|
||||
onPushedStateChanged: pushState()
|
||||
onTrackingChanged: ensureSubscription()
|
||||
onTrackingChanged: {
|
||||
ensureSubscription();
|
||||
if (tracking)
|
||||
updateFocusedApp();
|
||||
}
|
||||
onLimitParamsChanged: pushLimits()
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -59,6 +67,7 @@ Singleton {
|
||||
|
||||
Connections {
|
||||
target: CompositorService
|
||||
enabled: root.tracking
|
||||
|
||||
function onToplevelsChanged() {
|
||||
root.updateFocusedApp();
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell.Services.Mpris
|
||||
import qs.Common
|
||||
import qs.DCommon.Widgets
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
@@ -16,6 +17,22 @@ Item {
|
||||
readonly property real minimumValue: 0
|
||||
readonly property real maximumValue: 1
|
||||
readonly property real stepSize: stableLength > 0 ? 5 / stableLength : 0
|
||||
readonly property bool wantsPosition: visible && (Window.window?.visible ?? false)
|
||||
property bool _positionHeld: false
|
||||
onWantsPositionChanged: _syncPositionRef(wantsPosition)
|
||||
Component.onCompleted: _syncPositionRef(wantsPosition)
|
||||
Component.onDestruction: _syncPositionRef(false)
|
||||
|
||||
function _syncPositionRef(wanted) {
|
||||
if (wanted === _positionHeld)
|
||||
return;
|
||||
_positionHeld = wanted;
|
||||
if (wanted) {
|
||||
MprisController.addPositionRef();
|
||||
return;
|
||||
}
|
||||
MprisController.removePositionRef();
|
||||
}
|
||||
activeFocusOnTab: canSeek
|
||||
Accessible.role: Accessible.Slider
|
||||
Accessible.name: I18n.tr("Playback position", "media seekbar accessible name")
|
||||
|
||||
Reference in New Issue
Block a user