mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
Compare commits
4 Commits
0221021078
...
6b8c35c27b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b8c35c27b | ||
|
|
dd409b4d1c | ||
|
|
94a1aebe2b | ||
|
|
d3030c3ec6 |
11
.github/workflows/run-ppa.yml
vendored
11
.github/workflows/run-ppa.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: 'Package to upload (dms, dms-git, or all)'
|
||||
description: 'Package to upload (dms, dms-git, dms-greeter, or all)'
|
||||
required: false
|
||||
default: 'dms-git'
|
||||
rebuild_release:
|
||||
@@ -83,6 +83,12 @@ jobs:
|
||||
echo "Uploading dms-git to PPA..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-git" dms-git questing
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Uploading dms-greeter to PPA..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-greeter" danklinux questing
|
||||
else
|
||||
PPA_NAME="$PACKAGES"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
@@ -101,10 +107,13 @@ jobs:
|
||||
if [[ "$PACKAGES" == "all" ]]; then
|
||||
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **PPA danklinux**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "$PACKAGES" == "dms" ]]; then
|
||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "$PACKAGES" == "dms-greeter" ]]; then
|
||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||
|
||||
@@ -182,8 +182,23 @@ case "$PACKAGE_NAME" in
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
dms-greeter)
|
||||
GIT_REPO="AvengeMedia/DankMaterialShell"
|
||||
info "Downloading source for dms-greeter..."
|
||||
VERSION=$(dpkg-parsechangelog -S Version | sed 's/-[^-]*$//' | sed 's/ppa[0-9]*$//')
|
||||
|
||||
if [ ! -f "dms-greeter-source.tar.gz" ]; then
|
||||
info "Downloading dms-greeter source..."
|
||||
if wget -O dms-greeter-source.tar.gz "https://github.com/AvengeMedia/DankMaterialShell/archive/refs/tags/v${VERSION}.tar.gz"; then
|
||||
success "source tarball downloaded"
|
||||
else
|
||||
error "Failed to download dms-greeter-source.tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
danksearch)
|
||||
# danksearch uses pre-built binary from releases, like dgop
|
||||
# danksearch uses pre-built binary from releases
|
||||
GIT_REPO="AvengeMedia/danksearch"
|
||||
;;
|
||||
dgop)
|
||||
|
||||
@@ -224,6 +224,13 @@ if [ "$KEEP_BUILDS" = "false" ]; then
|
||||
REMOVED=$((REMOVED + 1))
|
||||
fi
|
||||
;;
|
||||
dms-greeter)
|
||||
# Remove downloaded source
|
||||
if [ -f "$PACKAGE_DIR/dms-greeter-source.tar.gz" ]; then
|
||||
rm -f "$PACKAGE_DIR/dms-greeter-source.tar.gz"
|
||||
REMOVED=$((REMOVED + 1))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $REMOVED -gt 0 ]; then
|
||||
|
||||
5
distro/ubuntu/dms-greeter/debian/changelog
Normal file
5
distro/ubuntu/dms-greeter/debian/changelog
Normal file
@@ -0,0 +1,5 @@
|
||||
dms-greeter (0.6.2ppa3) questing; urgency=medium
|
||||
|
||||
* Rebuild for packaging fixes (ppa3)
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Thu, 27 Nov 2025 23:38:37 -0500
|
||||
23
distro/ubuntu/dms-greeter/debian/control
Normal file
23
distro/ubuntu/dms-greeter/debian/control
Normal file
@@ -0,0 +1,23 @@
|
||||
Source: dms-greeter
|
||||
Section: x11
|
||||
Priority: optional
|
||||
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||
Build-Depends: debhelper-compat (= 13)
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://github.com/AvengeMedia/DankMaterialShell
|
||||
Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||
|
||||
Package: dms-greeter
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends},
|
||||
greetd,
|
||||
quickshell-git | quickshell
|
||||
Recommends: niri | hyprland | sway
|
||||
Description: DankMaterialShell greeter for greetd
|
||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
||||
.
|
||||
Supports multiple compositors including Niri, Hyprland, and Sway with automatic
|
||||
compositor detection and configuration. Features session selection, user
|
||||
authentication, and dynamic theming.
|
||||
27
distro/ubuntu/dms-greeter/debian/copyright
Normal file
27
distro/ubuntu/dms-greeter/debian/copyright
Normal file
@@ -0,0 +1,27 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: dms-greeter
|
||||
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||
Source: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
Files: *
|
||||
Copyright: 2025 Avenge Media LLC
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
distro/ubuntu/dms-greeter/debian/files
Normal file
1
distro/ubuntu/dms-greeter/debian/files
Normal file
@@ -0,0 +1 @@
|
||||
dms-greeter_0.6.2ppa3_source.buildinfo x11 optional
|
||||
108
distro/ubuntu/dms-greeter/debian/postinst
Executable file
108
distro/ubuntu/dms-greeter/debian/postinst
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create greeter user/group if they don't exist
|
||||
if ! getent group greeter >/dev/null; then
|
||||
addgroup --system greeter
|
||||
fi
|
||||
|
||||
if ! getent passwd greeter >/dev/null; then
|
||||
adduser --system --ingroup greeter --home /var/lib/greeter \
|
||||
--shell /bin/bash --gecos "System Greeter" greeter
|
||||
fi
|
||||
|
||||
if [ -d /var/cache/dms-greeter ]; then
|
||||
chown -R greeter:greeter /var/cache/dms-greeter 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -d /var/lib/greeter ]; then
|
||||
chown -R greeter:greeter /var/lib/greeter 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check and set graphical.target as default
|
||||
CURRENT_TARGET=$(systemctl get-default 2>/dev/null || echo "unknown")
|
||||
if [ "$CURRENT_TARGET" != "graphical.target" ]; then
|
||||
systemctl set-default graphical.target >/dev/null 2>&1 || true
|
||||
TARGET_STATUS="Set to graphical.target (was: $CURRENT_TARGET) ✓"
|
||||
else
|
||||
TARGET_STATUS="Already graphical.target ✓"
|
||||
fi
|
||||
|
||||
GREETD_CONFIG="/etc/greetd/config.toml"
|
||||
CONFIG_STATUS="Not modified (already configured)"
|
||||
|
||||
# Check if niri or hyprland exists
|
||||
COMPOSITOR="niri"
|
||||
if ! command -v niri >/dev/null 2>&1; then
|
||||
if command -v Hyprland >/dev/null 2>&1; then
|
||||
COMPOSITOR="hyprland"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If config doesn't exist, create a default one
|
||||
if [ ! -f "$GREETD_CONFIG" ]; then
|
||||
mkdir -p /etc/greetd
|
||||
cat > "$GREETD_CONFIG" << 'GREETD_EOF'
|
||||
[terminal]
|
||||
vt = 1
|
||||
|
||||
[default_session]
|
||||
user = "greeter"
|
||||
command = "/usr/bin/dms-greeter --command COMPOSITOR_PLACEHOLDER"
|
||||
GREETD_EOF
|
||||
sed -i "s|COMPOSITOR_PLACEHOLDER|$COMPOSITOR|" "$GREETD_CONFIG"
|
||||
CONFIG_STATUS="Created new config with $COMPOSITOR ✓"
|
||||
elif ! grep -q "dms-greeter" "$GREETD_CONFIG"; then
|
||||
# Backup existing config
|
||||
BACKUP_FILE="${GREETD_CONFIG}.backup-$(date +%Y%m%d-%H%M%S)"
|
||||
cp "$GREETD_CONFIG" "$BACKUP_FILE" 2>/dev/null || true
|
||||
|
||||
# Update command in default_session section
|
||||
sed -i "/^\[default_session\]/,/^\[/ s|^command =.*|command = \"/usr/bin/dms-greeter --command $COMPOSITOR\"|" "$GREETD_CONFIG"
|
||||
sed -i '/^\[default_session\]/,/^\[/ s|^user =.*|user = "greeter"|' "$GREETD_CONFIG"
|
||||
CONFIG_STATUS="Updated existing config (backed up) with $COMPOSITOR ✓"
|
||||
fi
|
||||
|
||||
# Only show banner on initial install
|
||||
if [ -z "$2" ]; then
|
||||
cat << 'EOF'
|
||||
|
||||
=========================================================================
|
||||
DMS Greeter Installation Complete!
|
||||
=========================================================================
|
||||
|
||||
Status:
|
||||
EOF
|
||||
echo " ✓ Greetd config: $CONFIG_STATUS"
|
||||
echo " ✓ Default target: $TARGET_STATUS"
|
||||
cat << 'EOF'
|
||||
✓ Greeter user: Created
|
||||
✓ Greeter directories: /var/cache/dms-greeter, /var/lib/greeter
|
||||
|
||||
Next steps:
|
||||
|
||||
1. Enable the greeter:
|
||||
dms greeter enable
|
||||
(This will automatically disable conflicting display managers,
|
||||
set graphical.target, and enable greetd)
|
||||
|
||||
2. Sync your theme with the greeter (optional):
|
||||
dms greeter sync
|
||||
|
||||
3. Check your setup:
|
||||
dms greeter status
|
||||
|
||||
Ready to test? Run: sudo systemctl start greetd
|
||||
Documentation: https://danklinux.com/docs/dankgreeter/
|
||||
=========================================================================
|
||||
|
||||
EOF
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
14
distro/ubuntu/dms-greeter/debian/postrm
Executable file
14
distro/ubuntu/dms-greeter/debian/postrm
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
purge)
|
||||
# Remove greeter cache directory on purge
|
||||
rm -rf /var/cache/dms-greeter 2>/dev/null || true
|
||||
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
55
distro/ubuntu/dms-greeter/debian/rules
Executable file
55
distro/ubuntu/dms-greeter/debian/rules
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export DH_VERBOSE = 1
|
||||
|
||||
# Extract version from debian/changelog
|
||||
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||
# Get upstream version (strip -1ppa1 suffix)
|
||||
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||
BASE_VERSION := $(shell echo $(UPSTREAM_VERSION) | sed 's/ppa[0-9]*$$//' | sed 's/+git.*//')
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
# All files are included in source package
|
||||
test -f dms-greeter-source.tar.gz || (echo "ERROR: dms-greeter-source.tar.gz not found!" && exit 1)
|
||||
|
||||
# Extract source tarball
|
||||
tar -xzf dms-greeter-source.tar.gz
|
||||
# Find the extracted directory
|
||||
SOURCE_DIR=$$(find . -maxdepth 1 -type d -name "DankMaterialShell*" | head -n1); \
|
||||
if [ -n "$$SOURCE_DIR" ]; then \
|
||||
ln -sf $$SOURCE_DIR DankMaterialShell-$(BASE_VERSION); \
|
||||
fi
|
||||
|
||||
override_dh_auto_install:
|
||||
# Install greeter files to shared data location
|
||||
mkdir -p debian/dms-greeter/usr/share/quickshell/dms-greeter
|
||||
cp -r DankMaterialShell-$(BASE_VERSION)/quickshell/* debian/dms-greeter/usr/share/quickshell/dms-greeter/
|
||||
|
||||
# Install launcher script
|
||||
install -Dm755 DankMaterialShell-$(BASE_VERSION)/quickshell/Modules/Greetd/assets/dms-greeter \
|
||||
debian/dms-greeter/usr/bin/dms-greeter
|
||||
|
||||
# Install documentation
|
||||
install -Dm644 DankMaterialShell-$(BASE_VERSION)/quickshell/Modules/Greetd/README.md \
|
||||
debian/dms-greeter/usr/share/doc/dms-greeter/README.md
|
||||
|
||||
# Install LICENSE file
|
||||
install -Dm644 DankMaterialShell-$(BASE_VERSION)/LICENSE \
|
||||
debian/dms-greeter/usr/share/doc/dms-greeter/LICENSE
|
||||
|
||||
# Create cache directory structure (will be created by postinst)
|
||||
mkdir -p debian/dms-greeter/var/cache/dms-greeter
|
||||
|
||||
# Remove build and development files
|
||||
rm -rf debian/dms-greeter/usr/share/quickshell/dms-greeter/core
|
||||
rm -rf debian/dms-greeter/usr/share/quickshell/dms-greeter/distro
|
||||
rm -rf debian/dms-greeter/usr/share/quickshell/dms-greeter/.git*
|
||||
rm -f debian/dms-greeter/usr/share/quickshell/dms-greeter/.gitignore
|
||||
rm -rf debian/dms-greeter/usr/share/quickshell/dms-greeter/.github
|
||||
|
||||
override_dh_auto_clean:
|
||||
rm -rf DankMaterialShell-*
|
||||
dh_auto_clean
|
||||
1
distro/ubuntu/dms-greeter/debian/source/format
Normal file
1
distro/ubuntu/dms-greeter/debian/source/format
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
1
distro/ubuntu/dms-greeter/debian/source/include-binaries
Normal file
1
distro/ubuntu/dms-greeter/debian/source/include-binaries
Normal file
@@ -0,0 +1 @@
|
||||
dms-greeter-source.tar.gz
|
||||
3
distro/ubuntu/dms-greeter/debian/source/options
Normal file
3
distro/ubuntu/dms-greeter/debian/source/options
Normal file
@@ -0,0 +1,3 @@
|
||||
# Include files that are normally excluded by .gitignore
|
||||
# These are needed for the build process on Launchpad (which has no internet access)
|
||||
tar-ignore = !dms-greeter-source.tar.gz
|
||||
@@ -21,6 +21,22 @@ Singleton {
|
||||
property bool isLightMode: false
|
||||
property bool doNotDisturb: false
|
||||
property bool isSwitchingMode: false
|
||||
property bool suppressOSD: true
|
||||
|
||||
Timer {
|
||||
id: osdSuppressTimer
|
||||
interval: 2000
|
||||
running: true
|
||||
onTriggered: root.suppressOSD = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
function onSessionResumed() {
|
||||
root.suppressOSD = true;
|
||||
osdSuppressTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
property string wallpaperPath: ""
|
||||
property bool perMonitorWallpaper: false
|
||||
|
||||
@@ -51,6 +51,7 @@ Item {
|
||||
signal dialogClosed
|
||||
signal backgroundClicked
|
||||
|
||||
property bool animationsEnabled: true
|
||||
readonly property bool useBackgroundWindow: true
|
||||
|
||||
function open() {
|
||||
@@ -75,6 +76,18 @@ Item {
|
||||
closeTimer.restart();
|
||||
}
|
||||
|
||||
function instantClose() {
|
||||
animationsEnabled = false;
|
||||
shouldBeVisible = false;
|
||||
shouldHaveFocus = false;
|
||||
closeTimer.stop();
|
||||
contentWindow.visible = false;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.visible = false;
|
||||
dialogClosed();
|
||||
Qt.callLater(() => animationsEnabled = true);
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
shouldBeVisible ? close() : open();
|
||||
}
|
||||
@@ -180,6 +193,7 @@ Item {
|
||||
visible: root.showBackground && SettingsData.modalDarkenBackground
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
@@ -272,6 +286,7 @@ Item {
|
||||
}
|
||||
|
||||
Behavior on animX {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
@@ -280,6 +295,7 @@ Item {
|
||||
}
|
||||
|
||||
Behavior on animY {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
@@ -288,6 +304,7 @@ Item {
|
||||
}
|
||||
|
||||
Behavior on scaleValue {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
@@ -312,6 +329,7 @@ Item {
|
||||
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
|
||||
@@ -45,8 +45,7 @@ DankModal {
|
||||
|
||||
function hideInstant() {
|
||||
onColorSelectedCallback = null;
|
||||
shouldBeVisible = false;
|
||||
visible = false;
|
||||
instantClose();
|
||||
}
|
||||
|
||||
onColorSelected: color => {
|
||||
@@ -81,26 +80,57 @@ DankModal {
|
||||
selectedColor = currentColor;
|
||||
}
|
||||
|
||||
function pickColorFromScreen() {
|
||||
hideInstant();
|
||||
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
console.warn("hyprpicker exited with code:", errorCode);
|
||||
function applyPickedColor(colorStr) {
|
||||
if (colorStr.length < 7 || !colorStr.startsWith('#'))
|
||||
return;
|
||||
const pickedColor = Qt.color(colorStr);
|
||||
root.selectedColor = pickedColor;
|
||||
root.currentColor = pickedColor;
|
||||
root.updateFromColor(pickedColor);
|
||||
copyColorToClipboard(colorStr);
|
||||
root.show();
|
||||
}
|
||||
|
||||
function runNiriPicker() {
|
||||
Proc.runCommand("niri-pick-color", ["niri", "msg", "pick-color"], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("niri msg pick-color exited with code:", exitCode);
|
||||
root.show();
|
||||
return;
|
||||
}
|
||||
const colorStr = output.trim();
|
||||
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
||||
const pickedColor = Qt.color(colorStr);
|
||||
root.selectedColor = pickedColor;
|
||||
root.currentColor = pickedColor;
|
||||
root.updateFromColor(pickedColor);
|
||||
copyColorToClipboard(colorStr);
|
||||
const hexMatch = output.match(/Hex:\s*(#[0-9A-Fa-f]{6})/);
|
||||
if (hexMatch) {
|
||||
applyPickedColor(hexMatch[1]);
|
||||
} else {
|
||||
console.warn("Failed to parse niri pick-color output:", output);
|
||||
root.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pickColorFromScreen() {
|
||||
hideInstant();
|
||||
Proc.runCommand("check-hyprpicker", ["which", "hyprpicker"], (output, exitCode) => {
|
||||
if (exitCode === 0) {
|
||||
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (hpOutput, hpCode) => {
|
||||
if (hpCode !== 0) {
|
||||
console.warn("hyprpicker exited with code:", hpCode);
|
||||
root.show();
|
||||
return;
|
||||
}
|
||||
applyPickedColor(hpOutput.trim());
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (CompositorService.isNiri) {
|
||||
runNiriPicker();
|
||||
return;
|
||||
}
|
||||
console.warn("No color picker available");
|
||||
root.show();
|
||||
});
|
||||
}
|
||||
|
||||
modalWidth: 680
|
||||
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
|
||||
backgroundColor: Theme.surfaceContainer
|
||||
|
||||
@@ -22,7 +22,6 @@ FloatingWindow {
|
||||
return;
|
||||
}
|
||||
visible = true;
|
||||
UserInfoService.getUptime();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
|
||||
@@ -8,16 +8,18 @@ Rectangle {
|
||||
|
||||
property bool editMode: false
|
||||
|
||||
signal powerButtonClicked()
|
||||
signal lockRequested()
|
||||
signal editModeToggled()
|
||||
signal settingsButtonClicked()
|
||||
signal powerButtonClicked
|
||||
signal lockRequested
|
||||
signal editModeToggled
|
||||
signal settingsButtonClicked
|
||||
|
||||
Component.onCompleted: DgopService.addRef("system")
|
||||
Component.onDestruction: DgopService.removeRef("system")
|
||||
|
||||
implicitHeight: 70
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
@@ -34,12 +36,12 @@ Rectangle {
|
||||
height: 60
|
||||
imageSource: {
|
||||
if (PortalService.profileImage === "")
|
||||
return ""
|
||||
return "";
|
||||
|
||||
if (PortalService.profileImage.startsWith("/"))
|
||||
return "file://" + PortalService.profileImage
|
||||
return "file://" + PortalService.profileImage;
|
||||
|
||||
return PortalService.profileImage
|
||||
return PortalService.profileImage;
|
||||
}
|
||||
fallbackIcon: "person"
|
||||
}
|
||||
@@ -49,14 +51,13 @@ Rectangle {
|
||||
spacing: 2
|
||||
|
||||
Typography {
|
||||
text: UserInfoService.fullName
|
||||
|| UserInfoService.username || "User"
|
||||
text: UserInfoService.fullName || UserInfoService.username || "User"
|
||||
style: Typography.Style.Subtitle
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: (UserInfoService.uptime || "Unknown")
|
||||
text: DgopService.uptime || "Unknown"
|
||||
style: Typography.Style.Caption
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
@@ -77,7 +78,7 @@ Rectangle {
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: {
|
||||
root.lockRequested()
|
||||
root.lockRequested();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +98,8 @@ Rectangle {
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: {
|
||||
root.settingsButtonClicked()
|
||||
settingsModal.show()
|
||||
root.settingsButtonClicked();
|
||||
settingsModal.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +112,4 @@ Rectangle {
|
||||
onClicked: root.editModeToggled()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,11 +84,8 @@ DankPopout {
|
||||
if (shouldBeVisible) {
|
||||
collapseAll();
|
||||
Qt.callLater(() => {
|
||||
if (NetworkService.activeService) {
|
||||
if (NetworkService.activeService)
|
||||
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
|
||||
}
|
||||
if (UserInfoService)
|
||||
UserInfoService.getUptime();
|
||||
});
|
||||
} else {
|
||||
Qt.callLater(() => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -7,6 +6,9 @@ import qs.Widgets
|
||||
Card {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: DgopService.addRef("system")
|
||||
Component.onDestruction: DgopService.removeRef("system")
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
@@ -21,12 +23,12 @@ Card {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
imageSource: {
|
||||
if (PortalService.profileImage === "")
|
||||
return ""
|
||||
return "";
|
||||
|
||||
if (PortalService.profileImage.startsWith("/"))
|
||||
return "file://" + PortalService.profileImage
|
||||
return "file://" + PortalService.profileImage;
|
||||
|
||||
return PortalService.profileImage
|
||||
return PortalService.profileImage;
|
||||
}
|
||||
fallbackIcon: "person"
|
||||
}
|
||||
@@ -56,12 +58,16 @@ Card {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (CompositorService.isNiri) return "on niri"
|
||||
if (CompositorService.isHyprland) return "on Hyprland"
|
||||
if (CompositorService.isNiri)
|
||||
return "on niri";
|
||||
if (CompositorService.isHyprland)
|
||||
return "on Hyprland";
|
||||
// technically they might not be on mangowc, but its what we support in the docs
|
||||
if (CompositorService.isDwl) return "on MangoWC"
|
||||
if (CompositorService.isSway) return "on Sway"
|
||||
return ""
|
||||
if (CompositorService.isDwl)
|
||||
return "on MangoWC";
|
||||
if (CompositorService.isSway)
|
||||
return "on Sway";
|
||||
return "";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
||||
@@ -82,29 +88,12 @@ Card {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: uptimeText
|
||||
|
||||
property real availableWidth: parent.parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 - 16 - Theme.spacingS
|
||||
property real longTextWidth: {
|
||||
const fontSize = Math.round(Theme.fontSizeSmall || 12)
|
||||
const testMetrics = Qt.createQmlObject('import QtQuick; TextMetrics { font.pixelSize: ' + fontSize + ' }', uptimeText)
|
||||
testMetrics.text = UserInfoService.uptime || "up 1 hour, 23 minutes"
|
||||
const result = testMetrics.width
|
||||
testMetrics.destroy()
|
||||
return result
|
||||
}
|
||||
// Just using truncated is always true initially idk
|
||||
property bool shouldUseShort: longTextWidth > availableWidth
|
||||
|
||||
text: shouldUseShort ? UserInfoService.shortUptime : UserInfoService.uptime || "up 1h 23m"
|
||||
text: DgopService.shortUptime || "up"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
width: availableWidth
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ Item {
|
||||
}
|
||||
|
||||
WeatherService.addRef();
|
||||
UserInfoService.refreshUserInfo();
|
||||
UserInfoService.getUserInfo();
|
||||
|
||||
if (CompositorService.isHyprland) {
|
||||
updateHyprlandLayout();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -69,7 +68,7 @@ DankFlickable {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `${UserInfoService.uptime} • Boot: ${DgopService.bootTime}`
|
||||
text: `${DgopService.uptime} • Boot: ${DgopService.bootTime}`
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
@@ -83,9 +82,7 @@ DankFlickable {
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -134,7 +131,6 @@ DankFlickable {
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -181,9 +177,7 @@ DankFlickable {
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -275,7 +269,6 @@ DankFlickable {
|
||||
color: Theme.secondary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -365,22 +358,16 @@ DankFlickable {
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -418,7 +405,6 @@ DankFlickable {
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -495,7 +481,6 @@ DankFlickable {
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -596,19 +581,11 @@ DankFlickable {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtCore
|
||||
@@ -15,7 +14,6 @@ Singleton {
|
||||
readonly property PwNode sink: Pipewire.defaultAudioSink
|
||||
readonly property PwNode source: Pipewire.defaultAudioSource
|
||||
|
||||
property bool suppressOSD: true
|
||||
property bool soundsAvailable: false
|
||||
property bool gsettingsAvailable: false
|
||||
property var availableSoundThemes: []
|
||||
@@ -33,12 +31,14 @@ Singleton {
|
||||
|
||||
signal micMuteChanged
|
||||
|
||||
Timer {
|
||||
id: startupTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
running: true
|
||||
onTriggered: root.suppressOSD = false
|
||||
Connections {
|
||||
target: root.sink?.audio ?? null
|
||||
|
||||
function onVolumeChanged() {
|
||||
if (SessionData.suppressOSD)
|
||||
return;
|
||||
root.playVolumeChangeSoundIfEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
function detectSoundsAvailability() {
|
||||
@@ -47,35 +47,33 @@ Singleton {
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
Item {}
|
||||
`, root, "AudioService.TestComponent")
|
||||
`, root, "AudioService.TestComponent");
|
||||
if (testObj) {
|
||||
testObj.destroy()
|
||||
testObj.destroy();
|
||||
}
|
||||
soundsAvailable = true
|
||||
return true
|
||||
soundsAvailable = true;
|
||||
return true;
|
||||
} catch (e) {
|
||||
soundsAvailable = false
|
||||
return false
|
||||
soundsAvailable = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function checkGsettings() {
|
||||
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
|
||||
gsettingsAvailable = (exitCode === 0)
|
||||
gsettingsAvailable = (exitCode === 0);
|
||||
if (gsettingsAvailable) {
|
||||
scanSoundThemes()
|
||||
getCurrentSoundTheme()
|
||||
scanSoundThemes();
|
||||
getCurrentSoundTheme();
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function scanSoundThemes() {
|
||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS")
|
||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== ""
|
||||
? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation)))
|
||||
: ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]
|
||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
|
||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))];
|
||||
|
||||
const basePaths = searchPaths.map(p => p + "/sounds").join(" ")
|
||||
const basePaths = searchPaths.map(p => p + "/sounds").join(" ");
|
||||
const script = `
|
||||
for base_dir in ${basePaths}; do
|
||||
[ -d "$base_dir" ] || continue
|
||||
@@ -84,65 +82,63 @@ Singleton {
|
||||
basename "$theme_dir"
|
||||
done
|
||||
done | sort -u
|
||||
`
|
||||
`;
|
||||
|
||||
Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => {
|
||||
if (exitCode === 0 && output.trim()) {
|
||||
const themes = output.trim().split('\n').filter(t => t && t.length > 0)
|
||||
availableSoundThemes = themes
|
||||
const themes = output.trim().split('\n').filter(t => t && t.length > 0);
|
||||
availableSoundThemes = themes;
|
||||
} else {
|
||||
availableSoundThemes = []
|
||||
availableSoundThemes = [];
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getCurrentSoundTheme() {
|
||||
Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => {
|
||||
if (exitCode === 0 && output.trim()) {
|
||||
currentSoundTheme = output.trim()
|
||||
console.log("AudioService: Current system sound theme:", currentSoundTheme)
|
||||
currentSoundTheme = output.trim();
|
||||
console.log("AudioService: Current system sound theme:", currentSoundTheme);
|
||||
if (SettingsData.useSystemSoundTheme) {
|
||||
discoverSoundFiles(currentSoundTheme)
|
||||
discoverSoundFiles(currentSoundTheme);
|
||||
}
|
||||
} else {
|
||||
currentSoundTheme = ""
|
||||
console.log("AudioService: No system sound theme found")
|
||||
currentSoundTheme = "";
|
||||
console.log("AudioService: No system sound theme found");
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function setSoundTheme(themeName) {
|
||||
if (!themeName || themeName === currentSoundTheme) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => {
|
||||
if (exitCode === 0) {
|
||||
currentSoundTheme = themeName
|
||||
currentSoundTheme = themeName;
|
||||
if (SettingsData.useSystemSoundTheme) {
|
||||
discoverSoundFiles(themeName)
|
||||
discoverSoundFiles(themeName);
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function discoverSoundFiles(themeName) {
|
||||
if (!themeName) {
|
||||
soundFilePaths = {}
|
||||
soundFilePaths = {};
|
||||
if (soundsAvailable) {
|
||||
destroySoundPlayers()
|
||||
createSoundPlayers()
|
||||
destroySoundPlayers();
|
||||
createSoundPlayers();
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS")
|
||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== ""
|
||||
? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation)))
|
||||
: ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]
|
||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
|
||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))];
|
||||
|
||||
const extensions = ["oga", "ogg", "wav", "mp3", "flac"]
|
||||
const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName
|
||||
const extensions = ["oga", "ogg", "wav", "mp3", "flac"];
|
||||
const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName;
|
||||
|
||||
const script = `
|
||||
for event_key in audio-volume-change power-plug power-unplug message message-new-instant; do
|
||||
@@ -179,26 +175,26 @@ Singleton {
|
||||
[ $found -eq 1 ] && break
|
||||
done
|
||||
done
|
||||
`
|
||||
`;
|
||||
|
||||
Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => {
|
||||
const paths = {}
|
||||
const paths = {};
|
||||
if (exitCode === 0 && output.trim()) {
|
||||
const lines = output.trim().split('\n')
|
||||
const lines = output.trim().split('\n');
|
||||
for (let line of lines) {
|
||||
const parts = line.split('=')
|
||||
const parts = line.split('=');
|
||||
if (parts.length === 2) {
|
||||
paths[parts[0]] = "file://" + parts[1]
|
||||
paths[parts[0]] = "file://" + parts[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
soundFilePaths = paths
|
||||
soundFilePaths = paths;
|
||||
|
||||
if (soundsAvailable) {
|
||||
destroySoundPlayers()
|
||||
createSoundPlayers()
|
||||
destroySoundPlayers();
|
||||
createSoundPlayers();
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getSoundPath(soundEvent) {
|
||||
@@ -208,45 +204,45 @@ Singleton {
|
||||
"power-unplug": "../assets/sounds/plasma/power-unplug.wav",
|
||||
"message": "../assets/sounds/freedesktop/message.wav",
|
||||
"message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav"
|
||||
}
|
||||
};
|
||||
|
||||
const specialConditions = {
|
||||
"smooth": ["audio-volume-change"]
|
||||
}
|
||||
};
|
||||
|
||||
const themeLower = currentSoundTheme.toLowerCase()
|
||||
const themeLower = currentSoundTheme.toLowerCase();
|
||||
if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) {
|
||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav")
|
||||
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath)
|
||||
return bundledPath
|
||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
|
||||
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath);
|
||||
return bundledPath;
|
||||
}
|
||||
|
||||
if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) {
|
||||
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent])
|
||||
return soundFilePaths[soundEvent]
|
||||
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]);
|
||||
return soundFilePaths[soundEvent];
|
||||
}
|
||||
|
||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav")
|
||||
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath)
|
||||
return bundledPath
|
||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
|
||||
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath);
|
||||
return bundledPath;
|
||||
}
|
||||
|
||||
function reloadSounds() {
|
||||
console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme)
|
||||
console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme);
|
||||
if (SettingsData.useSystemSoundTheme && currentSoundTheme) {
|
||||
discoverSoundFiles(currentSoundTheme)
|
||||
discoverSoundFiles(currentSoundTheme);
|
||||
} else {
|
||||
soundFilePaths = {}
|
||||
soundFilePaths = {};
|
||||
if (soundsAvailable) {
|
||||
destroySoundPlayers()
|
||||
createSoundPlayers()
|
||||
destroySoundPlayers();
|
||||
createSoundPlayers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupMediaDevices() {
|
||||
if (!soundsAvailable || mediaDevices) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -259,7 +255,7 @@ Singleton {
|
||||
console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description)
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.MediaDevices")
|
||||
`, root, "AudioService.MediaDevices");
|
||||
|
||||
if (mediaDevices) {
|
||||
mediaDevicesConnections = Qt.createQmlObject(`
|
||||
@@ -272,48 +268,48 @@ Singleton {
|
||||
root.createSoundPlayers()
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.MediaDevicesConnections")
|
||||
`, root, "AudioService.MediaDevicesConnections");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("AudioService: MediaDevices not available, using default audio output")
|
||||
mediaDevices = null
|
||||
console.log("AudioService: MediaDevices not available, using default audio output");
|
||||
mediaDevices = null;
|
||||
}
|
||||
}
|
||||
|
||||
function destroySoundPlayers() {
|
||||
if (volumeChangeSound) {
|
||||
volumeChangeSound.destroy()
|
||||
volumeChangeSound = null
|
||||
volumeChangeSound.destroy();
|
||||
volumeChangeSound = null;
|
||||
}
|
||||
if (powerPlugSound) {
|
||||
powerPlugSound.destroy()
|
||||
powerPlugSound = null
|
||||
powerPlugSound.destroy();
|
||||
powerPlugSound = null;
|
||||
}
|
||||
if (powerUnplugSound) {
|
||||
powerUnplugSound.destroy()
|
||||
powerUnplugSound = null
|
||||
powerUnplugSound.destroy();
|
||||
powerUnplugSound = null;
|
||||
}
|
||||
if (normalNotificationSound) {
|
||||
normalNotificationSound.destroy()
|
||||
normalNotificationSound = null
|
||||
normalNotificationSound.destroy();
|
||||
normalNotificationSound = null;
|
||||
}
|
||||
if (criticalNotificationSound) {
|
||||
criticalNotificationSound.destroy()
|
||||
criticalNotificationSound = null
|
||||
criticalNotificationSound.destroy();
|
||||
criticalNotificationSound = null;
|
||||
}
|
||||
}
|
||||
|
||||
function createSoundPlayers() {
|
||||
if (!soundsAvailable) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
setupMediaDevices()
|
||||
setupMediaDevices();
|
||||
|
||||
try {
|
||||
const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : ""
|
||||
const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : "";
|
||||
|
||||
const volumeChangePath = getSoundPath("audio-volume-change")
|
||||
const volumeChangePath = getSoundPath("audio-volume-change");
|
||||
volumeChangeSound = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
@@ -323,9 +319,9 @@ Singleton {
|
||||
${deviceProperty}volume: 1.0
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.VolumeChangeSound")
|
||||
`, root, "AudioService.VolumeChangeSound");
|
||||
|
||||
const powerPlugPath = getSoundPath("power-plug")
|
||||
const powerPlugPath = getSoundPath("power-plug");
|
||||
powerPlugSound = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
@@ -335,9 +331,9 @@ Singleton {
|
||||
${deviceProperty}volume: 1.0
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.PowerPlugSound")
|
||||
`, root, "AudioService.PowerPlugSound");
|
||||
|
||||
const powerUnplugPath = getSoundPath("power-unplug")
|
||||
const powerUnplugPath = getSoundPath("power-unplug");
|
||||
powerUnplugSound = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
@@ -347,9 +343,9 @@ Singleton {
|
||||
${deviceProperty}volume: 1.0
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.PowerUnplugSound")
|
||||
`, root, "AudioService.PowerUnplugSound");
|
||||
|
||||
const messagePath = getSoundPath("message")
|
||||
const messagePath = getSoundPath("message");
|
||||
normalNotificationSound = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
@@ -359,9 +355,9 @@ Singleton {
|
||||
${deviceProperty}volume: 1.0
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.NormalNotificationSound")
|
||||
`, root, "AudioService.NormalNotificationSound");
|
||||
|
||||
const messageNewInstantPath = getSoundPath("message-new-instant")
|
||||
const messageNewInstantPath = getSoundPath("message-new-instant");
|
||||
criticalNotificationSound = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
@@ -371,114 +367,114 @@ Singleton {
|
||||
${deviceProperty}volume: 1.0
|
||||
}
|
||||
}
|
||||
`, root, "AudioService.CriticalNotificationSound")
|
||||
`, root, "AudioService.CriticalNotificationSound");
|
||||
} catch (e) {
|
||||
console.warn("AudioService: Error creating sound players:", e)
|
||||
console.warn("AudioService: Error creating sound players:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function playVolumeChangeSound() {
|
||||
if (soundsAvailable && volumeChangeSound) {
|
||||
volumeChangeSound.play()
|
||||
volumeChangeSound.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playPowerPlugSound() {
|
||||
if (soundsAvailable && powerPlugSound) {
|
||||
powerPlugSound.play()
|
||||
powerPlugSound.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playPowerUnplugSound() {
|
||||
if (soundsAvailable && powerUnplugSound) {
|
||||
powerUnplugSound.play()
|
||||
powerUnplugSound.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playNormalNotificationSound() {
|
||||
if (soundsAvailable && normalNotificationSound && !SessionData.doNotDisturb) {
|
||||
normalNotificationSound.play()
|
||||
normalNotificationSound.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playCriticalNotificationSound() {
|
||||
if (soundsAvailable && criticalNotificationSound && !SessionData.doNotDisturb) {
|
||||
criticalNotificationSound.play()
|
||||
criticalNotificationSound.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playVolumeChangeSoundIfEnabled() {
|
||||
if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged) {
|
||||
playVolumeChangeSound()
|
||||
playVolumeChangeSound();
|
||||
}
|
||||
}
|
||||
|
||||
function displayName(node) {
|
||||
if (!node) {
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
if (node.properties && node.properties["device.description"]) {
|
||||
return node.properties["device.description"]
|
||||
return node.properties["device.description"];
|
||||
}
|
||||
|
||||
if (node.description && node.description !== node.name) {
|
||||
return node.description
|
||||
return node.description;
|
||||
}
|
||||
|
||||
if (node.nickname && node.nickname !== node.name) {
|
||||
return node.nickname
|
||||
return node.nickname;
|
||||
}
|
||||
|
||||
if (node.name.includes("analog-stereo")) {
|
||||
return "Built-in Speakers"
|
||||
return "Built-in Speakers";
|
||||
}
|
||||
if (node.name.includes("bluez")) {
|
||||
return "Bluetooth Audio"
|
||||
return "Bluetooth Audio";
|
||||
}
|
||||
if (node.name.includes("usb")) {
|
||||
return "USB Audio"
|
||||
return "USB Audio";
|
||||
}
|
||||
if (node.name.includes("hdmi")) {
|
||||
return "HDMI Audio"
|
||||
return "HDMI Audio";
|
||||
}
|
||||
|
||||
return node.name
|
||||
return node.name;
|
||||
}
|
||||
|
||||
function subtitle(name) {
|
||||
if (!name) {
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
if (name.includes('usb-')) {
|
||||
if (name.includes('SteelSeries')) {
|
||||
return "USB Gaming Headset"
|
||||
return "USB Gaming Headset";
|
||||
}
|
||||
if (name.includes('Generic')) {
|
||||
return "USB Audio Device"
|
||||
return "USB Audio Device";
|
||||
}
|
||||
return "USB Audio"
|
||||
return "USB Audio";
|
||||
}
|
||||
|
||||
if (name.includes('pci-')) {
|
||||
if (name.includes('01_00.1') || name.includes('01:00.1')) {
|
||||
return "NVIDIA GPU Audio"
|
||||
return "NVIDIA GPU Audio";
|
||||
}
|
||||
return "PCI Audio"
|
||||
return "PCI Audio";
|
||||
}
|
||||
|
||||
if (name.includes('bluez')) {
|
||||
return "Bluetooth Audio"
|
||||
return "Bluetooth Audio";
|
||||
}
|
||||
if (name.includes('analog')) {
|
||||
return "Built-in Audio"
|
||||
return "Built-in Audio";
|
||||
}
|
||||
if (name.includes('hdmi')) {
|
||||
return "HDMI Audio"
|
||||
return "HDMI Audio";
|
||||
}
|
||||
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
PwObjectTracker {
|
||||
@@ -487,136 +483,134 @@ Singleton {
|
||||
|
||||
function setVolume(percentage) {
|
||||
if (!root.sink?.audio) {
|
||||
return "No audio sink available"
|
||||
return "No audio sink available";
|
||||
}
|
||||
|
||||
const clampedVolume = Math.max(0, Math.min(100, percentage))
|
||||
root.sink.audio.volume = clampedVolume / 100
|
||||
return `Volume set to ${clampedVolume}%`
|
||||
const clampedVolume = Math.max(0, Math.min(100, percentage));
|
||||
root.sink.audio.volume = clampedVolume / 100;
|
||||
return `Volume set to ${clampedVolume}%`;
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
if (!root.sink?.audio) {
|
||||
return "No audio sink available"
|
||||
return "No audio sink available";
|
||||
}
|
||||
|
||||
root.sink.audio.muted = !root.sink.audio.muted
|
||||
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted"
|
||||
root.sink.audio.muted = !root.sink.audio.muted;
|
||||
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
|
||||
}
|
||||
|
||||
function setMicVolume(percentage) {
|
||||
if (!root.source?.audio) {
|
||||
return "No audio source available"
|
||||
return "No audio source available";
|
||||
}
|
||||
|
||||
const clampedVolume = Math.max(0, Math.min(100, percentage))
|
||||
root.source.audio.volume = clampedVolume / 100
|
||||
return `Microphone volume set to ${clampedVolume}%`
|
||||
const clampedVolume = Math.max(0, Math.min(100, percentage));
|
||||
root.source.audio.volume = clampedVolume / 100;
|
||||
return `Microphone volume set to ${clampedVolume}%`;
|
||||
}
|
||||
|
||||
function toggleMicMute() {
|
||||
if (!root.source?.audio) {
|
||||
return "No audio source available"
|
||||
return "No audio source available";
|
||||
}
|
||||
|
||||
root.source.audio.muted = !root.source.audio.muted
|
||||
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted"
|
||||
root.source.audio.muted = !root.source.audio.muted;
|
||||
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted";
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "audio"
|
||||
|
||||
function setvolume(percentage: string): string {
|
||||
return root.setVolume(parseInt(percentage))
|
||||
return root.setVolume(parseInt(percentage));
|
||||
}
|
||||
|
||||
function increment(step: string): string {
|
||||
if (!root.sink?.audio) {
|
||||
return "No audio sink available"
|
||||
return "No audio sink available";
|
||||
}
|
||||
|
||||
if (root.sink.audio.muted) {
|
||||
root.sink.audio.muted = false
|
||||
root.sink.audio.muted = false;
|
||||
}
|
||||
|
||||
const currentVolume = Math.round(root.sink.audio.volume * 100)
|
||||
const stepValue = parseInt(step || "5")
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue))
|
||||
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||
const stepValue = parseInt(step || "5");
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue));
|
||||
|
||||
root.sink.audio.volume = newVolume / 100
|
||||
root.playVolumeChangeSoundIfEnabled()
|
||||
return `Volume increased to ${newVolume}%`
|
||||
root.sink.audio.volume = newVolume / 100;
|
||||
return `Volume increased to ${newVolume}%`;
|
||||
}
|
||||
|
||||
function decrement(step: string): string {
|
||||
if (!root.sink?.audio) {
|
||||
return "No audio sink available"
|
||||
return "No audio sink available";
|
||||
}
|
||||
|
||||
if (root.sink.audio.muted) {
|
||||
root.sink.audio.muted = false
|
||||
root.sink.audio.muted = false;
|
||||
}
|
||||
|
||||
const currentVolume = Math.round(root.sink.audio.volume * 100)
|
||||
const stepValue = parseInt(step || "5")
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue))
|
||||
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||
const stepValue = parseInt(step || "5");
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue));
|
||||
|
||||
root.sink.audio.volume = newVolume / 100
|
||||
root.playVolumeChangeSoundIfEnabled()
|
||||
return `Volume decreased to ${newVolume}%`
|
||||
root.sink.audio.volume = newVolume / 100;
|
||||
return `Volume decreased to ${newVolume}%`;
|
||||
}
|
||||
|
||||
function mute(): string {
|
||||
return root.toggleMute()
|
||||
return root.toggleMute();
|
||||
}
|
||||
|
||||
function setmic(percentage: string): string {
|
||||
return root.setMicVolume(parseInt(percentage))
|
||||
return root.setMicVolume(parseInt(percentage));
|
||||
}
|
||||
|
||||
function micmute(): string {
|
||||
const result = root.toggleMicMute()
|
||||
root.micMuteChanged()
|
||||
return result
|
||||
const result = root.toggleMicMute();
|
||||
root.micMuteChanged();
|
||||
return result;
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
let result = "Audio Status:\n"
|
||||
let result = "Audio Status:\n";
|
||||
|
||||
if (root.sink?.audio) {
|
||||
const volume = Math.round(root.sink.audio.volume * 100)
|
||||
const muteStatus = root.sink.audio.muted ? " (muted)" : ""
|
||||
result += `Output: ${volume}%${muteStatus}\n`
|
||||
const volume = Math.round(root.sink.audio.volume * 100);
|
||||
const muteStatus = root.sink.audio.muted ? " (muted)" : "";
|
||||
result += `Output: ${volume}%${muteStatus}\n`;
|
||||
} else {
|
||||
result += "Output: No sink available\n"
|
||||
result += "Output: No sink available\n";
|
||||
}
|
||||
|
||||
if (root.source?.audio) {
|
||||
const micVolume = Math.round(root.source.audio.volume * 100)
|
||||
const muteStatus = root.source.audio.muted ? " (muted)" : ""
|
||||
result += `Input: ${micVolume}%${muteStatus}`
|
||||
const micVolume = Math.round(root.source.audio.volume * 100);
|
||||
const muteStatus = root.source.audio.muted ? " (muted)" : "";
|
||||
result += `Input: ${micVolume}%${muteStatus}`;
|
||||
} else {
|
||||
result += "Input: No source available"
|
||||
result += "Input: No source available";
|
||||
}
|
||||
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onUseSystemSoundThemeChanged() {
|
||||
reloadSounds()
|
||||
reloadSounds();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!detectSoundsAvailability()) {
|
||||
console.warn("AudioService: QtMultimedia not available - sound effects disabled")
|
||||
console.warn("AudioService: QtMultimedia not available - sound effects disabled");
|
||||
} else {
|
||||
console.info("AudioService: Sound effects enabled")
|
||||
checkGsettings()
|
||||
Qt.callLater(createSoundPlayers)
|
||||
console.info("AudioService: Sound effects enabled");
|
||||
checkGsettings();
|
||||
Qt.callLater(createSoundPlayers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
@@ -72,504 +71,547 @@ Singleton {
|
||||
property string bootTime: ""
|
||||
property string motherboard: ""
|
||||
property string biosVersion: ""
|
||||
property string uptime: ""
|
||||
property string shortUptime: ""
|
||||
|
||||
property int historySize: 60
|
||||
property var cpuHistory: []
|
||||
property var memoryHistory: []
|
||||
property var networkHistory: ({
|
||||
"rx": [],
|
||||
"tx": []
|
||||
})
|
||||
"rx": [],
|
||||
"tx": []
|
||||
})
|
||||
property var diskHistory: ({
|
||||
"read": [],
|
||||
"write": []
|
||||
})
|
||||
"read": [],
|
||||
"write": []
|
||||
})
|
||||
|
||||
function addRef(modules = null) {
|
||||
refCount++
|
||||
let modulesChanged = false
|
||||
refCount++;
|
||||
let modulesChanged = false;
|
||||
|
||||
if (modules) {
|
||||
const modulesToAdd = Array.isArray(modules) ? modules : [modules]
|
||||
const modulesToAdd = Array.isArray(modules) ? modules : [modules];
|
||||
for (const module of modulesToAdd) {
|
||||
// Increment reference count for this module
|
||||
const currentCount = moduleRefCounts[module] || 0
|
||||
moduleRefCounts[module] = currentCount + 1
|
||||
console.log("Adding ref for module:", module, "count:", moduleRefCounts[module])
|
||||
const currentCount = moduleRefCounts[module] || 0;
|
||||
moduleRefCounts[module] = currentCount + 1;
|
||||
console.log("Adding ref for module:", module, "count:", moduleRefCounts[module]);
|
||||
|
||||
// Add to enabled modules if not already there
|
||||
if (enabledModules.indexOf(module) === -1) {
|
||||
enabledModules.push(module)
|
||||
modulesChanged = true
|
||||
enabledModules.push(module);
|
||||
modulesChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modulesChanged || refCount === 1) {
|
||||
enabledModules = enabledModules.slice() // Force property change
|
||||
moduleRefCounts = Object.assign({}, moduleRefCounts) // Force property change
|
||||
updateAllStats()
|
||||
enabledModules = enabledModules.slice(); // Force property change
|
||||
moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change
|
||||
updateAllStats();
|
||||
} else if (gpuPciIds.length > 0 && refCount > 0) {
|
||||
// If we have GPU PCI IDs and active modules, make sure to update
|
||||
// This handles the case where PCI IDs were loaded after modules were added
|
||||
updateAllStats()
|
||||
updateAllStats();
|
||||
}
|
||||
}
|
||||
|
||||
function removeRef(modules = null) {
|
||||
refCount = Math.max(0, refCount - 1)
|
||||
let modulesChanged = false
|
||||
refCount = Math.max(0, refCount - 1);
|
||||
let modulesChanged = false;
|
||||
|
||||
if (modules) {
|
||||
const modulesToRemove = Array.isArray(modules) ? modules : [modules]
|
||||
const modulesToRemove = Array.isArray(modules) ? modules : [modules];
|
||||
for (const module of modulesToRemove) {
|
||||
const currentCount = moduleRefCounts[module] || 0
|
||||
const currentCount = moduleRefCounts[module] || 0;
|
||||
if (currentCount > 1) {
|
||||
// Decrement reference count
|
||||
moduleRefCounts[module] = currentCount - 1
|
||||
console.log("Removing ref for module:", module, "count:", moduleRefCounts[module])
|
||||
moduleRefCounts[module] = currentCount - 1;
|
||||
console.log("Removing ref for module:", module, "count:", moduleRefCounts[module]);
|
||||
} else if (currentCount === 1) {
|
||||
// Remove completely when count reaches 0
|
||||
delete moduleRefCounts[module]
|
||||
const index = enabledModules.indexOf(module)
|
||||
delete moduleRefCounts[module];
|
||||
const index = enabledModules.indexOf(module);
|
||||
if (index > -1) {
|
||||
enabledModules.splice(index, 1)
|
||||
modulesChanged = true
|
||||
console.log("Disabling module:", module, "(no more refs)")
|
||||
enabledModules.splice(index, 1);
|
||||
modulesChanged = true;
|
||||
console.log("Disabling module:", module, "(no more refs)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modulesChanged) {
|
||||
enabledModules = enabledModules.slice() // Force property change
|
||||
moduleRefCounts = Object.assign({}, moduleRefCounts) // Force property change
|
||||
enabledModules = enabledModules.slice(); // Force property change
|
||||
moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change
|
||||
|
||||
// Clear cursor data when CPU or process modules are no longer active
|
||||
if (!enabledModules.includes("cpu")) {
|
||||
cpuCursor = ""
|
||||
cpuSampleCount = 0
|
||||
cpuCursor = "";
|
||||
cpuSampleCount = 0;
|
||||
}
|
||||
if (!enabledModules.includes("processes")) {
|
||||
procCursor = ""
|
||||
processSampleCount = 0
|
||||
procCursor = "";
|
||||
processSampleCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setGpuPciIds(pciIds) {
|
||||
gpuPciIds = Array.isArray(pciIds) ? pciIds : []
|
||||
gpuPciIds = Array.isArray(pciIds) ? pciIds : [];
|
||||
}
|
||||
|
||||
function addGpuPciId(pciId) {
|
||||
const currentCount = gpuPciIdRefCounts[pciId] || 0
|
||||
gpuPciIdRefCounts[pciId] = currentCount + 1
|
||||
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||
gpuPciIdRefCounts[pciId] = currentCount + 1;
|
||||
|
||||
// Add to gpuPciIds array if not already there
|
||||
if (!gpuPciIds.includes(pciId)) {
|
||||
gpuPciIds = gpuPciIds.concat([pciId])
|
||||
gpuPciIds = gpuPciIds.concat([pciId]);
|
||||
}
|
||||
|
||||
console.log("Adding GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId])
|
||||
console.log("Adding GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId]);
|
||||
// Force property change notification
|
||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts)
|
||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
||||
}
|
||||
|
||||
function removeGpuPciId(pciId) {
|
||||
const currentCount = gpuPciIdRefCounts[pciId] || 0
|
||||
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||
if (currentCount > 1) {
|
||||
// Decrement reference count
|
||||
gpuPciIdRefCounts[pciId] = currentCount - 1
|
||||
console.log("Removing GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId])
|
||||
gpuPciIdRefCounts[pciId] = currentCount - 1;
|
||||
console.log("Removing GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId]);
|
||||
} else if (currentCount === 1) {
|
||||
// Remove completely when count reaches 0
|
||||
delete gpuPciIdRefCounts[pciId]
|
||||
const index = gpuPciIds.indexOf(pciId)
|
||||
delete gpuPciIdRefCounts[pciId];
|
||||
const index = gpuPciIds.indexOf(pciId);
|
||||
if (index > -1) {
|
||||
gpuPciIds = gpuPciIds.slice()
|
||||
gpuPciIds.splice(index, 1)
|
||||
gpuPciIds = gpuPciIds.slice();
|
||||
gpuPciIds.splice(index, 1);
|
||||
}
|
||||
|
||||
// Clear temperature data for this GPU when no longer monitored
|
||||
if (availableGpus && availableGpus.length > 0) {
|
||||
const updatedGpus = availableGpus.slice()
|
||||
const updatedGpus = availableGpus.slice();
|
||||
for (var i = 0; i < updatedGpus.length; i++) {
|
||||
if (updatedGpus[i].pciId === pciId) {
|
||||
updatedGpus[i] = Object.assign({}, updatedGpus[i], {
|
||||
"temperature": 0
|
||||
})
|
||||
"temperature": 0
|
||||
});
|
||||
}
|
||||
}
|
||||
availableGpus = updatedGpus
|
||||
availableGpus = updatedGpus;
|
||||
}
|
||||
|
||||
console.log("Removing GPU PCI ID completely:", pciId)
|
||||
console.log("Removing GPU PCI ID completely:", pciId);
|
||||
}
|
||||
|
||||
// Force property change notification
|
||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts)
|
||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
||||
}
|
||||
|
||||
function setProcessOptions(limit = 20, sort = "cpu", disableCpu = false) {
|
||||
processLimit = limit
|
||||
processSort = sort
|
||||
noCpu = disableCpu
|
||||
processLimit = limit;
|
||||
processSort = sort;
|
||||
noCpu = disableCpu;
|
||||
}
|
||||
|
||||
function updateAllStats() {
|
||||
if (dgopAvailable && refCount > 0 && enabledModules.length > 0) {
|
||||
isUpdating = true
|
||||
dgopProcess.running = true
|
||||
isUpdating = true;
|
||||
dgopProcess.running = true;
|
||||
} else {
|
||||
isUpdating = false
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeGpuMetadata() {
|
||||
if (!dgopAvailable)
|
||||
return
|
||||
gpuInitProcess.running = true
|
||||
return;
|
||||
gpuInitProcess.running = true;
|
||||
}
|
||||
|
||||
function initializeSystemMetadata() {
|
||||
if (!dgopAvailable)
|
||||
return
|
||||
systemInitProcess.running = true
|
||||
return;
|
||||
systemInitProcess.running = true;
|
||||
}
|
||||
|
||||
function buildDgopCommand() {
|
||||
const cmd = ["dgop", "meta", "--json"]
|
||||
const cmd = ["dgop", "meta", "--json"];
|
||||
|
||||
if (enabledModules.length === 0) {
|
||||
// Don't run if no modules are needed
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
|
||||
// Replace 'gpu' with 'gpu-temp' when we have PCI IDs to monitor
|
||||
const finalModules = []
|
||||
const finalModules = [];
|
||||
for (const module of enabledModules) {
|
||||
if (module === "gpu" && gpuPciIds.length > 0) {
|
||||
finalModules.push("gpu-temp")
|
||||
finalModules.push("gpu-temp");
|
||||
} else if (module !== "gpu") {
|
||||
finalModules.push(module)
|
||||
finalModules.push(module);
|
||||
}
|
||||
}
|
||||
|
||||
// Add gpu-temp module automatically when we have PCI IDs to monitor
|
||||
if (gpuPciIds.length > 0 && finalModules.indexOf("gpu-temp") === -1) {
|
||||
finalModules.push("gpu-temp")
|
||||
finalModules.push("gpu-temp");
|
||||
}
|
||||
|
||||
if (enabledModules.indexOf("all") !== -1) {
|
||||
cmd.push("--modules", "all")
|
||||
cmd.push("--modules", "all");
|
||||
} else if (finalModules.length > 0) {
|
||||
const moduleList = finalModules.join(",")
|
||||
cmd.push("--modules", moduleList)
|
||||
const moduleList = finalModules.join(",");
|
||||
cmd.push("--modules", moduleList);
|
||||
} else {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
|
||||
// Add cursor data if available for accurate CPU percentages
|
||||
if ((enabledModules.includes("cpu") || enabledModules.includes("all")) && cpuCursor) {
|
||||
cmd.push("--cpu-cursor", cpuCursor)
|
||||
cmd.push("--cpu-cursor", cpuCursor);
|
||||
}
|
||||
if ((enabledModules.includes("processes") || enabledModules.includes("all")) && procCursor) {
|
||||
cmd.push("--proc-cursor", procCursor)
|
||||
cmd.push("--proc-cursor", procCursor);
|
||||
}
|
||||
|
||||
if (gpuPciIds.length > 0) {
|
||||
cmd.push("--gpu-pci-ids", gpuPciIds.join(","))
|
||||
cmd.push("--gpu-pci-ids", gpuPciIds.join(","));
|
||||
}
|
||||
|
||||
if (enabledModules.indexOf("processes") !== -1 || enabledModules.indexOf("all") !== -1) {
|
||||
cmd.push("--limit", "100") // Get more data for client sorting
|
||||
cmd.push("--sort", "cpu") // Always get CPU sorted data
|
||||
cmd.push("--limit", "100"); // Get more data for client sorting
|
||||
cmd.push("--sort", "cpu"); // Always get CPU sorted data
|
||||
if (noCpu) {
|
||||
cmd.push("--no-cpu")
|
||||
cmd.push("--no-cpu");
|
||||
}
|
||||
}
|
||||
|
||||
return cmd
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function parseData(data) {
|
||||
if (data.cpu) {
|
||||
const cpu = data.cpu
|
||||
cpuSampleCount++
|
||||
const cpu = data.cpu;
|
||||
cpuSampleCount++;
|
||||
|
||||
cpuUsage = cpu.usage || 0
|
||||
cpuFrequency = cpu.frequency || 0
|
||||
cpuTemperature = cpu.temperature || 0
|
||||
cpuCores = cpu.count || 1
|
||||
cpuModel = cpu.model || ""
|
||||
perCoreCpuUsage = cpu.coreUsage || []
|
||||
addToHistory(cpuHistory, cpuUsage)
|
||||
cpuUsage = cpu.usage || 0;
|
||||
cpuFrequency = cpu.frequency || 0;
|
||||
cpuTemperature = cpu.temperature || 0;
|
||||
cpuCores = cpu.count || 1;
|
||||
cpuModel = cpu.model || "";
|
||||
perCoreCpuUsage = cpu.coreUsage || [];
|
||||
addToHistory(cpuHistory, cpuUsage);
|
||||
|
||||
if (cpu.cursor) {
|
||||
cpuCursor = cpu.cursor
|
||||
cpuCursor = cpu.cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.memory) {
|
||||
const mem = data.memory
|
||||
const totalKB = mem.total || 0
|
||||
const availableKB = mem.available || 0
|
||||
const freeKB = mem.free || 0
|
||||
const mem = data.memory;
|
||||
const totalKB = mem.total || 0;
|
||||
const availableKB = mem.available || 0;
|
||||
const freeKB = mem.free || 0;
|
||||
|
||||
totalMemoryMB = totalKB / 1024
|
||||
availableMemoryMB = availableKB / 1024
|
||||
freeMemoryMB = freeKB / 1024
|
||||
usedMemoryMB = totalMemoryMB - availableMemoryMB
|
||||
memoryUsage = totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0
|
||||
totalMemoryMB = totalKB / 1024;
|
||||
availableMemoryMB = availableKB / 1024;
|
||||
freeMemoryMB = freeKB / 1024;
|
||||
usedMemoryMB = totalMemoryMB - availableMemoryMB;
|
||||
memoryUsage = totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0;
|
||||
|
||||
totalMemoryKB = totalKB
|
||||
usedMemoryKB = totalKB - availableKB
|
||||
totalSwapKB = mem.swaptotal || 0
|
||||
usedSwapKB = (mem.swaptotal || 0) - (mem.swapfree || 0)
|
||||
totalMemoryKB = totalKB;
|
||||
usedMemoryKB = totalKB - availableKB;
|
||||
totalSwapKB = mem.swaptotal || 0;
|
||||
usedSwapKB = (mem.swaptotal || 0) - (mem.swapfree || 0);
|
||||
|
||||
addToHistory(memoryHistory, memoryUsage)
|
||||
addToHistory(memoryHistory, memoryUsage);
|
||||
}
|
||||
|
||||
if (data.network && Array.isArray(data.network)) {
|
||||
networkInterfaces = data.network
|
||||
networkInterfaces = data.network;
|
||||
|
||||
let totalRx = 0
|
||||
let totalTx = 0
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
for (const iface of data.network) {
|
||||
totalRx += iface.rx || 0
|
||||
totalTx += iface.tx || 0
|
||||
totalRx += iface.rx || 0;
|
||||
totalTx += iface.tx || 0;
|
||||
}
|
||||
|
||||
if (lastNetworkStats) {
|
||||
const timeDiff = updateInterval / 1000
|
||||
const rxDiff = totalRx - lastNetworkStats.rx
|
||||
const txDiff = totalTx - lastNetworkStats.tx
|
||||
networkRxRate = Math.max(0, rxDiff / timeDiff)
|
||||
networkTxRate = Math.max(0, txDiff / timeDiff)
|
||||
addToHistory(networkHistory.rx, networkRxRate / 1024)
|
||||
addToHistory(networkHistory.tx, networkTxRate / 1024)
|
||||
const timeDiff = updateInterval / 1000;
|
||||
const rxDiff = totalRx - lastNetworkStats.rx;
|
||||
const txDiff = totalTx - lastNetworkStats.tx;
|
||||
networkRxRate = Math.max(0, rxDiff / timeDiff);
|
||||
networkTxRate = Math.max(0, txDiff / timeDiff);
|
||||
addToHistory(networkHistory.rx, networkRxRate / 1024);
|
||||
addToHistory(networkHistory.tx, networkTxRate / 1024);
|
||||
}
|
||||
lastNetworkStats = {
|
||||
"rx": totalRx,
|
||||
"tx": totalTx
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (data.disk && Array.isArray(data.disk)) {
|
||||
diskDevices = data.disk
|
||||
diskDevices = data.disk;
|
||||
|
||||
let totalRead = 0
|
||||
let totalWrite = 0
|
||||
let totalRead = 0;
|
||||
let totalWrite = 0;
|
||||
for (const disk of data.disk) {
|
||||
totalRead += (disk.read || 0) * 512
|
||||
totalWrite += (disk.write || 0) * 512
|
||||
totalRead += (disk.read || 0) * 512;
|
||||
totalWrite += (disk.write || 0) * 512;
|
||||
}
|
||||
|
||||
if (lastDiskStats) {
|
||||
const timeDiff = updateInterval / 1000
|
||||
const readDiff = totalRead - lastDiskStats.read
|
||||
const writeDiff = totalWrite - lastDiskStats.write
|
||||
diskReadRate = Math.max(0, readDiff / timeDiff)
|
||||
diskWriteRate = Math.max(0, writeDiff / timeDiff)
|
||||
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024))
|
||||
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024))
|
||||
const timeDiff = updateInterval / 1000;
|
||||
const readDiff = totalRead - lastDiskStats.read;
|
||||
const writeDiff = totalWrite - lastDiskStats.write;
|
||||
diskReadRate = Math.max(0, readDiff / timeDiff);
|
||||
diskWriteRate = Math.max(0, writeDiff / timeDiff);
|
||||
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024));
|
||||
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024));
|
||||
}
|
||||
lastDiskStats = {
|
||||
"read": totalRead,
|
||||
"write": totalWrite
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (data.diskmounts) {
|
||||
diskMounts = data.diskmounts || []
|
||||
diskMounts = data.diskmounts || [];
|
||||
}
|
||||
|
||||
if (data.processes && Array.isArray(data.processes)) {
|
||||
const newProcesses = []
|
||||
processSampleCount++
|
||||
const newProcesses = [];
|
||||
processSampleCount++;
|
||||
|
||||
for (const proc of data.processes) {
|
||||
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0
|
||||
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0;
|
||||
|
||||
newProcesses.push({
|
||||
"pid": proc.pid || 0,
|
||||
"ppid": proc.ppid || 0,
|
||||
"cpu": cpuUsage,
|
||||
"memoryPercent": proc.memoryPercent || proc.pssPercent || 0,
|
||||
"memoryKB": proc.memoryKB || proc.pssKB || 0,
|
||||
"command": proc.command || "",
|
||||
"fullCommand": proc.fullCommand || "",
|
||||
"displayName": (proc.command && proc.command.length > 15) ? proc.command.substring(0, 15) + "..." : (proc.command || "")
|
||||
})
|
||||
"pid": proc.pid || 0,
|
||||
"ppid": proc.ppid || 0,
|
||||
"cpu": cpuUsage,
|
||||
"memoryPercent": proc.memoryPercent || proc.pssPercent || 0,
|
||||
"memoryKB": proc.memoryKB || proc.pssKB || 0,
|
||||
"command": proc.command || "",
|
||||
"fullCommand": proc.fullCommand || "",
|
||||
"displayName": (proc.command && proc.command.length > 15) ? proc.command.substring(0, 15) + "..." : (proc.command || "")
|
||||
});
|
||||
}
|
||||
allProcesses = newProcesses
|
||||
applySorting()
|
||||
allProcesses = newProcesses;
|
||||
applySorting();
|
||||
|
||||
if (data.cursor) {
|
||||
procCursor = data.cursor
|
||||
procCursor = data.cursor;
|
||||
}
|
||||
}
|
||||
|
||||
const gpuData = (data.gpu && data.gpu.gpus) || data.gpus
|
||||
const gpuData = (data.gpu && data.gpu.gpus) || data.gpus;
|
||||
if (gpuData && Array.isArray(gpuData)) {
|
||||
// Check if this is temperature update data (has PCI IDs being monitored)
|
||||
if (gpuPciIds.length > 0 && availableGpus && availableGpus.length > 0) {
|
||||
// This is temperature data - merge with existing GPU metadata
|
||||
const updatedGpus = availableGpus.slice()
|
||||
const updatedGpus = availableGpus.slice();
|
||||
for (var i = 0; i < updatedGpus.length; i++) {
|
||||
const existingGpu = updatedGpus[i]
|
||||
const tempGpu = gpuData.find(g => g.pciId === existingGpu.pciId)
|
||||
const existingGpu = updatedGpus[i];
|
||||
const tempGpu = gpuData.find(g => g.pciId === existingGpu.pciId);
|
||||
// Only update temperature if this GPU's PCI ID is being monitored
|
||||
if (tempGpu && gpuPciIds.includes(existingGpu.pciId)) {
|
||||
updatedGpus[i] = Object.assign({}, existingGpu, {
|
||||
"temperature": tempGpu.temperature || 0
|
||||
})
|
||||
"temperature": tempGpu.temperature || 0
|
||||
});
|
||||
}
|
||||
}
|
||||
availableGpus = updatedGpus
|
||||
availableGpus = updatedGpus;
|
||||
} else {
|
||||
// This is initial GPU metadata - set the full list
|
||||
const gpuList = []
|
||||
const gpuList = [];
|
||||
for (const gpu of gpuData) {
|
||||
let displayName = gpu.displayName || gpu.name || "Unknown GPU"
|
||||
let fullName = gpu.fullName || gpu.name || "Unknown GPU"
|
||||
let displayName = gpu.displayName || gpu.name || "Unknown GPU";
|
||||
let fullName = gpu.fullName || gpu.name || "Unknown GPU";
|
||||
|
||||
gpuList.push({
|
||||
"driver": gpu.driver || "",
|
||||
"vendor": gpu.vendor || "",
|
||||
"displayName": displayName,
|
||||
"fullName": fullName,
|
||||
"pciId": gpu.pciId || "",
|
||||
"temperature": gpu.temperature || 0
|
||||
})
|
||||
"driver": gpu.driver || "",
|
||||
"vendor": gpu.vendor || "",
|
||||
"displayName": displayName,
|
||||
"fullName": fullName,
|
||||
"pciId": gpu.pciId || "",
|
||||
"temperature": gpu.temperature || 0
|
||||
});
|
||||
}
|
||||
availableGpus = gpuList
|
||||
availableGpus = gpuList;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.system) {
|
||||
const sys = data.system
|
||||
loadAverage = sys.loadavg || ""
|
||||
processCount = sys.processes || 0
|
||||
threadCount = sys.threads || 0
|
||||
bootTime = sys.boottime || ""
|
||||
const sys = data.system;
|
||||
loadAverage = sys.loadavg || "";
|
||||
processCount = sys.processes || 0;
|
||||
threadCount = sys.threads || 0;
|
||||
bootTime = sys.boottime || "";
|
||||
updateUptime();
|
||||
}
|
||||
|
||||
const hwData = data.hardware || (data.hostname || data.kernel || data.distro || data.arch) ? data : null
|
||||
const hwData = data.hardware || (data.hostname || data.kernel || data.distro || data.arch) ? data : null;
|
||||
if (hwData) {
|
||||
hostname = hwData.hostname || ""
|
||||
kernelVersion = hwData.kernel || ""
|
||||
distribution = hwData.distro || ""
|
||||
architecture = hwData.arch || ""
|
||||
motherboard = (hwData.bios && hwData.bios.motherboard) || ""
|
||||
biosVersion = (hwData.bios && hwData.bios.version) || ""
|
||||
hostname = hwData.hostname || "";
|
||||
kernelVersion = hwData.kernel || "";
|
||||
distribution = hwData.distro || "";
|
||||
architecture = hwData.arch || "";
|
||||
motherboard = (hwData.bios && hwData.bios.motherboard) || "";
|
||||
biosVersion = (hwData.bios && hwData.bios.version) || "";
|
||||
}
|
||||
|
||||
isUpdating = false
|
||||
isUpdating = false;
|
||||
}
|
||||
|
||||
function addToHistory(array, value) {
|
||||
array.push(value)
|
||||
array.push(value);
|
||||
if (array.length > historySize) {
|
||||
array.shift()
|
||||
array.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessIcon(command) {
|
||||
const cmd = command.toLowerCase()
|
||||
const cmd = command.toLowerCase();
|
||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser") || cmd.includes("chromium")) {
|
||||
return "web"
|
||||
return "web";
|
||||
}
|
||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) {
|
||||
return "code"
|
||||
return "code";
|
||||
}
|
||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) {
|
||||
return "terminal"
|
||||
return "terminal";
|
||||
}
|
||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify")) {
|
||||
return "music_note"
|
||||
return "music_note";
|
||||
}
|
||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv")) {
|
||||
return "play_circle"
|
||||
return "play_circle";
|
||||
}
|
||||
if (cmd.includes("systemd") || cmd.includes("elogind") || cmd.includes("kernel") || cmd.includes("kthread") || cmd.includes("kworker")) {
|
||||
return "settings"
|
||||
return "settings";
|
||||
}
|
||||
return "memory"
|
||||
return "memory";
|
||||
}
|
||||
|
||||
function formatCpuUsage(cpu) {
|
||||
return (cpu || 0).toFixed(1) + "%"
|
||||
return (cpu || 0).toFixed(1) + "%";
|
||||
}
|
||||
|
||||
function formatMemoryUsage(memoryKB) {
|
||||
const mem = memoryKB || 0
|
||||
const mem = memoryKB || 0;
|
||||
if (mem < 1024) {
|
||||
return mem.toFixed(0) + " KB"
|
||||
return mem.toFixed(0) + " KB";
|
||||
} else if (mem < 1024 * 1024) {
|
||||
return (mem / 1024).toFixed(1) + " MB"
|
||||
return (mem / 1024).toFixed(1) + " MB";
|
||||
} else {
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB"
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
}
|
||||
|
||||
function formatSystemMemory(memoryKB) {
|
||||
const mem = memoryKB || 0
|
||||
const mem = memoryKB || 0;
|
||||
if (mem === 0) {
|
||||
return "--"
|
||||
return "--";
|
||||
}
|
||||
if (mem < 1024 * 1024) {
|
||||
return (mem / 1024).toFixed(0) + " MB"
|
||||
return (mem / 1024).toFixed(0) + " MB";
|
||||
} else {
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB"
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
}
|
||||
|
||||
function killProcess(pid) {
|
||||
if (pid > 0) {
|
||||
Quickshell.execDetached("kill", [pid.toString()])
|
||||
Quickshell.execDetached("kill", [pid.toString()]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateUptime() {
|
||||
if (!bootTime) {
|
||||
uptime = "";
|
||||
shortUptime = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const bootDate = new Date(bootTime.replace(" ", "T"));
|
||||
if (isNaN(bootDate.getTime())) {
|
||||
uptime = "";
|
||||
shortUptime = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const seconds = Math.floor((now - bootDate) / 1000);
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
const parts = [];
|
||||
if (days > 0)
|
||||
parts.push(`${days} day${days === 1 ? "" : "s"}`);
|
||||
if (hours > 0)
|
||||
parts.push(`${hours} hour${hours === 1 ? "" : "s"}`);
|
||||
if (minutes > 0)
|
||||
parts.push(`${minutes} minute${minutes === 1 ? "" : "s"}`);
|
||||
|
||||
uptime = parts.length > 0 ? `up ${parts.join(", ")}` : `up ${seconds} seconds`;
|
||||
|
||||
var shortStr = "up";
|
||||
if (days > 0)
|
||||
shortStr += ` ${days}d`;
|
||||
if (hours > 0)
|
||||
shortStr += ` ${hours}h`;
|
||||
if (minutes > 0)
|
||||
shortStr += ` ${minutes}m`;
|
||||
shortUptime = shortStr;
|
||||
}
|
||||
|
||||
function setSortBy(newSortBy) {
|
||||
if (newSortBy !== currentSort) {
|
||||
currentSort = newSortBy
|
||||
applySorting()
|
||||
currentSort = newSortBy;
|
||||
applySorting();
|
||||
}
|
||||
}
|
||||
|
||||
function applySorting() {
|
||||
if (!allProcesses || allProcesses.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const sorted = allProcesses.slice()
|
||||
const sorted = allProcesses.slice();
|
||||
sorted.sort((a, b) => {
|
||||
let valueA, valueB
|
||||
let valueA, valueB;
|
||||
|
||||
switch (currentSort) {
|
||||
case "cpu":
|
||||
valueA = a.cpu || 0
|
||||
valueB = b.cpu || 0
|
||||
return valueB - valueA
|
||||
case "memory":
|
||||
valueA = a.memoryKB || 0
|
||||
valueB = b.memoryKB || 0
|
||||
return valueB - valueA
|
||||
case "name":
|
||||
valueA = (a.command || "").toLowerCase()
|
||||
valueB = (b.command || "").toLowerCase()
|
||||
return valueA.localeCompare(valueB)
|
||||
case "pid":
|
||||
valueA = a.pid || 0
|
||||
valueB = b.pid || 0
|
||||
return valueA - valueB
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
switch (currentSort) {
|
||||
case "cpu":
|
||||
valueA = a.cpu || 0;
|
||||
valueB = b.cpu || 0;
|
||||
return valueB - valueA;
|
||||
case "memory":
|
||||
valueA = a.memoryKB || 0;
|
||||
valueB = b.memoryKB || 0;
|
||||
return valueB - valueA;
|
||||
case "name":
|
||||
valueA = (a.command || "").toLowerCase();
|
||||
valueB = (b.command || "").toLowerCase();
|
||||
return valueA.localeCompare(valueB);
|
||||
case "pid":
|
||||
valueA = a.pid || 0;
|
||||
valueB = b.pid || 0;
|
||||
return valueA - valueB;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
processes = sorted.slice(0, processLimit)
|
||||
processes = sorted.slice(0, processLimit);
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -585,26 +627,26 @@ Singleton {
|
||||
id: dgopProcess
|
||||
command: root.buildDgopCommand()
|
||||
running: false
|
||||
onCommandChanged: {
|
||||
onCommandChanged:
|
||||
|
||||
//console.log("DgopService command:", JSON.stringify(command))
|
||||
}
|
||||
//console.log("DgopService command:", JSON.stringify(command))
|
||||
{}
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Dgop process failed with exit code:", exitCode)
|
||||
isUpdating = false
|
||||
console.warn("Dgop process failed with exit code:", exitCode);
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
try {
|
||||
const data = JSON.parse(text.trim())
|
||||
parseData(data)
|
||||
const data = JSON.parse(text.trim());
|
||||
parseData(data);
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse dgop JSON:", e)
|
||||
console.warn("Raw text was:", text.substring(0, 200))
|
||||
isUpdating = false
|
||||
console.warn("Failed to parse dgop JSON:", e);
|
||||
console.warn("Raw text was:", text.substring(0, 200));
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,17 +659,17 @@ Singleton {
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("GPU init process failed with exit code:", exitCode)
|
||||
console.warn("GPU init process failed with exit code:", exitCode);
|
||||
}
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
try {
|
||||
const data = JSON.parse(text.trim())
|
||||
parseData(data)
|
||||
const data = JSON.parse(text.trim());
|
||||
parseData(data);
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse GPU init JSON:", e)
|
||||
console.warn("Failed to parse GPU init JSON:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,17 +682,17 @@ Singleton {
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("System init process failed with exit code:", exitCode)
|
||||
console.warn("System init process failed with exit code:", exitCode);
|
||||
}
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
try {
|
||||
const data = JSON.parse(text.trim())
|
||||
parseData(data)
|
||||
const data = JSON.parse(text.trim());
|
||||
parseData(data);
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse system init JSON:", e)
|
||||
console.warn("Failed to parse system init JSON:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -662,20 +704,20 @@ Singleton {
|
||||
command: ["which", "dgop"]
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
dgopAvailable = (exitCode === 0)
|
||||
dgopAvailable = (exitCode === 0);
|
||||
if (dgopAvailable) {
|
||||
initializeGpuMetadata()
|
||||
initializeSystemMetadata()
|
||||
initializeGpuMetadata();
|
||||
initializeSystemMetadata();
|
||||
if (SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.length > 0) {
|
||||
for (const pciId of SessionData.enabledGpuPciIds) {
|
||||
addGpuPciId(pciId)
|
||||
addGpuPciId(pciId);
|
||||
}
|
||||
if (refCount > 0 && enabledModules.length > 0) {
|
||||
updateAllStats()
|
||||
updateAllStats();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn("dgop is not installed or not in PATH")
|
||||
console.warn("dgop is not installed or not in PATH");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -686,33 +728,33 @@ Singleton {
|
||||
running: false
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Failed to read /etc/os-release")
|
||||
console.warn("Failed to read /etc/os-release");
|
||||
}
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
try {
|
||||
const lines = text.trim().split('\n')
|
||||
let prettyName = ""
|
||||
let name = ""
|
||||
const lines = text.trim().split('\n');
|
||||
let prettyName = "";
|
||||
let name = "";
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
const trimmedLine = line.trim();
|
||||
if (trimmedLine.startsWith('PRETTY_NAME=')) {
|
||||
prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, '')
|
||||
prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, '');
|
||||
} else if (trimmedLine.startsWith('NAME=')) {
|
||||
name = trimmedLine.substring(5).replace(/^["']|["']$/g, '')
|
||||
name = trimmedLine.substring(5).replace(/^["']|["']$/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer PRETTY_NAME, fallback to NAME
|
||||
const distroName = prettyName || name || "Linux"
|
||||
distribution = distroName
|
||||
console.info("Detected distribution:", distroName)
|
||||
const distroName = prettyName || name || "Linux";
|
||||
distribution = distroName;
|
||||
console.info("Detected distribution:", distroName);
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse /etc/os-release:", e)
|
||||
distribution = "Linux"
|
||||
console.warn("Failed to parse /etc/os-release:", e);
|
||||
distribution = "Linux";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,7 +762,7 @@ Singleton {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
dgopCheckProcess.running = true
|
||||
osReleaseProcess.running = true
|
||||
dgopCheckProcess.running = true;
|
||||
osReleaseProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
@@ -23,9 +22,9 @@ Singleton {
|
||||
|
||||
readonly property bool nativeInhibitorAvailable: {
|
||||
try {
|
||||
return typeof IdleInhibitor !== "undefined"
|
||||
return typeof IdleInhibitor !== "undefined";
|
||||
} catch (e) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +41,10 @@ Singleton {
|
||||
property string seat: ""
|
||||
property string display: ""
|
||||
|
||||
signal sessionLocked()
|
||||
signal sessionUnlocked()
|
||||
signal prepareForSleep()
|
||||
signal loginctlStateChanged()
|
||||
signal sessionLocked
|
||||
signal sessionUnlocked
|
||||
signal sessionResumed
|
||||
signal loginctlStateChanged
|
||||
|
||||
property bool stateInitialized: false
|
||||
|
||||
@@ -57,30 +56,29 @@ Singleton {
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
detectElogindProcess.running = true
|
||||
detectHibernateProcess.running = true
|
||||
detectPrimeRunProcess.running = true
|
||||
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable)
|
||||
detectElogindProcess.running = true;
|
||||
detectHibernateProcess.running = true;
|
||||
detectPrimeRunProcess.running = true;
|
||||
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable);
|
||||
if (!SettingsData.loginctlLockIntegration) {
|
||||
console.log("SessionService: loginctl lock integration disabled by user")
|
||||
return
|
||||
console.log("SessionService: loginctl lock integration disabled by user");
|
||||
return;
|
||||
}
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
checkDMSCapabilities()
|
||||
checkDMSCapabilities();
|
||||
} else {
|
||||
console.log("SessionService: DMS_SOCKET not set")
|
||||
console.log("SessionService: DMS_SOCKET not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: detectUwsmProcess
|
||||
running: false
|
||||
command: ["which", "uwsm"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
hasUwsm = (exitCode === 0)
|
||||
hasUwsm = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +88,8 @@ Singleton {
|
||||
command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
console.log("SessionService: Elogind detection exited with code", exitCode)
|
||||
isElogind = (exitCode === 0)
|
||||
console.log("SessionService: Elogind detection exited with code", exitCode);
|
||||
isElogind = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +99,7 @@ Singleton {
|
||||
command: ["grep", "-q", "disk", "/sys/power/state"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
hibernateSupported = (exitCode === 0)
|
||||
hibernateSupported = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +109,7 @@ Singleton {
|
||||
command: ["which", "prime-run"]
|
||||
|
||||
onExited: function (exitCode) {
|
||||
hasPrimeRun = (exitCode === 0)
|
||||
hasPrimeRun = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,164 +122,167 @@ Singleton {
|
||||
splitMarker: "\n"
|
||||
onRead: data => {
|
||||
if (data.trim().toLowerCase().includes("not running")) {
|
||||
_logout()
|
||||
_logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
_logout()
|
||||
_logout();
|
||||
}
|
||||
}
|
||||
|
||||
function escapeShellArg(arg) {
|
||||
return "'" + arg.replace(/'/g, "'\\''") + "'"
|
||||
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
||||
}
|
||||
|
||||
function needsShellExecution(prefix) {
|
||||
if (!prefix || prefix.length === 0) return false
|
||||
return /[;&|<>()$`\\"']/.test(prefix)
|
||||
if (!prefix || prefix.length === 0)
|
||||
return false;
|
||||
return /[;&|<>()$`\\"']/.test(prefix);
|
||||
}
|
||||
|
||||
function launchDesktopEntry(desktopEntry, usePrimeRun) {
|
||||
let cmd = desktopEntry.command
|
||||
let cmd = desktopEntry.command;
|
||||
if (usePrimeRun && hasPrimeRun) {
|
||||
cmd = ["prime-run"].concat(cmd)
|
||||
cmd = ["prime-run"].concat(cmd);
|
||||
}
|
||||
|
||||
const userPrefix = SettingsData.launchPrefix?.trim() || ""
|
||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""
|
||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix
|
||||
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||
|
||||
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ")
|
||||
const shellCmd = `${prefix} ${escapedCmd}`
|
||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||
const shellCmd = `${prefix} ${escapedCmd}`;
|
||||
|
||||
Quickshell.execDetached({
|
||||
command: ["sh", "-c", shellCmd],
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
||||
})
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||
});
|
||||
} else {
|
||||
if (prefix.length > 0) {
|
||||
const launchPrefix = prefix.split(" ")
|
||||
cmd = launchPrefix.concat(cmd)
|
||||
const launchPrefix = prefix.split(" ");
|
||||
cmd = launchPrefix.concat(cmd);
|
||||
}
|
||||
|
||||
Quickshell.execDetached({
|
||||
command: cmd,
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
||||
})
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function launchDesktopAction(desktopEntry, action, usePrimeRun) {
|
||||
let cmd = action.command
|
||||
let cmd = action.command;
|
||||
if (usePrimeRun && hasPrimeRun) {
|
||||
cmd = ["prime-run"].concat(cmd)
|
||||
cmd = ["prime-run"].concat(cmd);
|
||||
}
|
||||
|
||||
const userPrefix = SettingsData.launchPrefix?.trim() || ""
|
||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""
|
||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix
|
||||
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||
|
||||
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ")
|
||||
const shellCmd = `${prefix} ${escapedCmd}`
|
||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||
const shellCmd = `${prefix} ${escapedCmd}`;
|
||||
|
||||
Quickshell.execDetached({
|
||||
command: ["sh", "-c", shellCmd],
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
||||
})
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||
});
|
||||
} else {
|
||||
if (prefix.length > 0) {
|
||||
const launchPrefix = prefix.split(" ")
|
||||
cmd = launchPrefix.concat(cmd)
|
||||
const launchPrefix = prefix.split(" ");
|
||||
cmd = launchPrefix.concat(cmd);
|
||||
}
|
||||
|
||||
Quickshell.execDetached({
|
||||
command: cmd,
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
||||
})
|
||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// * Session management
|
||||
function logout() {
|
||||
if (hasUwsm) {
|
||||
uwsmLogout.running = true
|
||||
uwsmLogout.running = true;
|
||||
}
|
||||
_logout()
|
||||
_logout();
|
||||
}
|
||||
|
||||
function _logout() {
|
||||
if (SettingsData.customPowerActionLogout.length === 0) {
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.quit()
|
||||
return
|
||||
NiriService.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CompositorService.isDwl) {
|
||||
DwlService.quit()
|
||||
return
|
||||
DwlService.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CompositorService.isSway) {
|
||||
try { I3.dispatch("exit") } catch(_){}
|
||||
return
|
||||
try {
|
||||
I3.dispatch("exit");
|
||||
} catch (_) {}
|
||||
return;
|
||||
}
|
||||
|
||||
Hyprland.dispatch("exit")
|
||||
Hyprland.dispatch("exit");
|
||||
} else {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]);
|
||||
}
|
||||
}
|
||||
|
||||
function suspend() {
|
||||
if (SettingsData.customPowerActionSuspend.length === 0) {
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"])
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"]);
|
||||
} else {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend])
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend]);
|
||||
}
|
||||
}
|
||||
|
||||
function hibernate() {
|
||||
if (SettingsData.customPowerActionHibernate.length === 0) {
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"])
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"]);
|
||||
} else {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate])
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate]);
|
||||
}
|
||||
}
|
||||
|
||||
function suspendThenHibernate() {
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"])
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"]);
|
||||
}
|
||||
|
||||
function suspendWithBehavior(behavior) {
|
||||
if (behavior === SettingsData.SuspendBehavior.Hibernate) {
|
||||
hibernate()
|
||||
hibernate();
|
||||
} else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) {
|
||||
suspendThenHibernate()
|
||||
suspendThenHibernate();
|
||||
} else {
|
||||
suspend()
|
||||
suspend();
|
||||
}
|
||||
}
|
||||
|
||||
function reboot() {
|
||||
if (SettingsData.customPowerActionReboot.length === 0) {
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"])
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"]);
|
||||
} else {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot])
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot]);
|
||||
}
|
||||
}
|
||||
|
||||
function poweroff() {
|
||||
if (SettingsData.customPowerActionPowerOff.length === 0) {
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"])
|
||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"]);
|
||||
} else {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff])
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,42 +291,42 @@ Singleton {
|
||||
|
||||
function enableIdleInhibit() {
|
||||
if (idleInhibited) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")")
|
||||
idleInhibited = true
|
||||
inhibitorChanged()
|
||||
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
|
||||
idleInhibited = true;
|
||||
inhibitorChanged();
|
||||
}
|
||||
|
||||
function disableIdleInhibit() {
|
||||
if (!idleInhibited) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")")
|
||||
idleInhibited = false
|
||||
inhibitorChanged()
|
||||
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
|
||||
idleInhibited = false;
|
||||
inhibitorChanged();
|
||||
}
|
||||
|
||||
function toggleIdleInhibit() {
|
||||
if (idleInhibited) {
|
||||
disableIdleInhibit()
|
||||
disableIdleInhibit();
|
||||
} else {
|
||||
enableIdleInhibit()
|
||||
enableIdleInhibit();
|
||||
}
|
||||
}
|
||||
|
||||
function setInhibitReason(reason) {
|
||||
inhibitReason = reason
|
||||
inhibitReason = reason;
|
||||
|
||||
if (idleInhibited && !nativeInhibitorAvailable) {
|
||||
const wasActive = idleInhibited
|
||||
idleInhibited = false
|
||||
const wasActive = idleInhibited;
|
||||
idleInhibited = false;
|
||||
|
||||
Qt.callLater(() => {
|
||||
if (wasActive) {
|
||||
idleInhibited = true
|
||||
}
|
||||
})
|
||||
if (wasActive) {
|
||||
idleInhibited = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,24 +335,24 @@ Singleton {
|
||||
|
||||
command: {
|
||||
if (!idleInhibited || nativeInhibitorAvailable) {
|
||||
return ["true"]
|
||||
return ["true"];
|
||||
}
|
||||
|
||||
console.log("SessionService: Starting systemd/elogind inhibit process")
|
||||
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"]
|
||||
console.log("SessionService: Starting systemd/elogind inhibit process");
|
||||
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"];
|
||||
}
|
||||
|
||||
running: idleInhibited && !nativeInhibitorAvailable
|
||||
|
||||
onRunningChanged: {
|
||||
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")")
|
||||
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
|
||||
}
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
|
||||
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode)
|
||||
idleInhibited = false
|
||||
ToastService.showWarning("Idle inhibitor failed")
|
||||
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode);
|
||||
idleInhibited = false;
|
||||
ToastService.showWarning("Idle inhibitor failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,12 +362,12 @@ Singleton {
|
||||
|
||||
function onConnectionStateChanged() {
|
||||
if (DMSService.isConnected) {
|
||||
checkDMSCapabilities()
|
||||
checkDMSCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
function onCapabilitiesReceived() {
|
||||
syncSleepInhibitor()
|
||||
syncSleepInhibitor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +376,7 @@ Singleton {
|
||||
enabled: DMSService.isConnected
|
||||
|
||||
function onCapabilitiesChanged() {
|
||||
checkDMSCapabilities()
|
||||
checkDMSCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,22 +387,22 @@ Singleton {
|
||||
if (SettingsData.loginctlLockIntegration) {
|
||||
if (socketPath && socketPath.length > 0 && loginctlAvailable) {
|
||||
if (!stateInitialized) {
|
||||
stateInitialized = true
|
||||
getLoginctlState()
|
||||
syncLockBeforeSuspend()
|
||||
stateInitialized = true;
|
||||
getLoginctlState();
|
||||
syncLockBeforeSuspend();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stateInitialized = false
|
||||
stateInitialized = false;
|
||||
}
|
||||
syncSleepInhibitor()
|
||||
syncSleepInhibitor();
|
||||
}
|
||||
|
||||
function onLockBeforeSuspendChanged() {
|
||||
if (SettingsData.loginctlLockIntegration) {
|
||||
syncLockBeforeSuspend()
|
||||
syncLockBeforeSuspend();
|
||||
}
|
||||
syncSleepInhibitor()
|
||||
syncSleepInhibitor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,109 +411,114 @@ Singleton {
|
||||
enabled: SettingsData.loginctlLockIntegration
|
||||
|
||||
function onLoginctlStateUpdate(data) {
|
||||
updateLoginctlState(data)
|
||||
updateLoginctlState(data);
|
||||
}
|
||||
|
||||
function onLoginctlEvent(event) {
|
||||
handleLoginctlEvent(event)
|
||||
handleLoginctlEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function checkDMSCapabilities() {
|
||||
if (!DMSService.isConnected) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMSService.capabilities.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMSService.capabilities.includes("loginctl")) {
|
||||
loginctlAvailable = true
|
||||
loginctlAvailable = true;
|
||||
if (SettingsData.loginctlLockIntegration && !stateInitialized) {
|
||||
stateInitialized = true
|
||||
getLoginctlState()
|
||||
syncLockBeforeSuspend()
|
||||
stateInitialized = true;
|
||||
getLoginctlState();
|
||||
syncLockBeforeSuspend();
|
||||
}
|
||||
} else {
|
||||
loginctlAvailable = false
|
||||
console.log("SessionService: loginctl capability not available in DMS")
|
||||
loginctlAvailable = false;
|
||||
console.log("SessionService: loginctl capability not available in DMS");
|
||||
}
|
||||
}
|
||||
|
||||
function getLoginctlState() {
|
||||
if (!loginctlAvailable) return
|
||||
|
||||
if (!loginctlAvailable)
|
||||
return;
|
||||
DMSService.sendRequest("loginctl.getState", null, response => {
|
||||
if (response.result) {
|
||||
updateLoginctlState(response.result)
|
||||
updateLoginctlState(response.result);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function syncLockBeforeSuspend() {
|
||||
if (!loginctlAvailable) return
|
||||
|
||||
if (!loginctlAvailable)
|
||||
return;
|
||||
DMSService.sendRequest("loginctl.setLockBeforeSuspend", {
|
||||
enabled: SettingsData.lockBeforeSuspend
|
||||
}, response => {
|
||||
if (response.error) {
|
||||
console.warn("SessionService: Failed to sync lock before suspend:", response.error)
|
||||
console.warn("SessionService: Failed to sync lock before suspend:", response.error);
|
||||
} else {
|
||||
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend)
|
||||
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function syncSleepInhibitor() {
|
||||
if (!loginctlAvailable) return
|
||||
|
||||
if (!DMSService.apiVersion || DMSService.apiVersion < 4) return
|
||||
|
||||
if (!loginctlAvailable)
|
||||
return;
|
||||
if (!DMSService.apiVersion || DMSService.apiVersion < 4)
|
||||
return;
|
||||
DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", {
|
||||
enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend
|
||||
}, response => {
|
||||
if (response.error) {
|
||||
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error)
|
||||
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error);
|
||||
} else {
|
||||
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration)
|
||||
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function updateLoginctlState(state) {
|
||||
const wasLocked = locked
|
||||
const wasLocked = locked;
|
||||
const wasSleeping = preparingForSleep;
|
||||
|
||||
sessionId = state.sessionId || ""
|
||||
sessionPath = state.sessionPath || ""
|
||||
locked = state.locked || false
|
||||
active = state.active || false
|
||||
idleHint = state.idleHint || false
|
||||
lockedHint = state.lockedHint || false
|
||||
sessionType = state.sessionType || ""
|
||||
userName = state.userName || ""
|
||||
seat = state.seat || ""
|
||||
display = state.display || ""
|
||||
sessionId = state.sessionId || "";
|
||||
sessionPath = state.sessionPath || "";
|
||||
locked = state.locked || false;
|
||||
active = state.active || false;
|
||||
idleHint = state.idleHint || false;
|
||||
lockedHint = state.lockedHint || false;
|
||||
preparingForSleep = state.preparingForSleep || false;
|
||||
sessionType = state.sessionType || "";
|
||||
userName = state.userName || "";
|
||||
seat = state.seat || "";
|
||||
display = state.display || "";
|
||||
|
||||
if (locked && !wasLocked) {
|
||||
sessionLocked()
|
||||
sessionLocked();
|
||||
} else if (!locked && wasLocked) {
|
||||
sessionUnlocked()
|
||||
sessionUnlocked();
|
||||
}
|
||||
|
||||
loginctlStateChanged()
|
||||
if (wasSleeping && !preparingForSleep) {
|
||||
sessionResumed();
|
||||
}
|
||||
|
||||
loginctlStateChanged();
|
||||
}
|
||||
|
||||
function handleLoginctlEvent(event) {
|
||||
if (event.event === "Lock") {
|
||||
locked = true
|
||||
lockedHint = true
|
||||
sessionLocked()
|
||||
locked = true;
|
||||
lockedHint = true;
|
||||
sessionLocked();
|
||||
} else if (event.event === "Unlock") {
|
||||
locked = false
|
||||
lockedHint = false
|
||||
sessionUnlocked()
|
||||
locked = false;
|
||||
lockedHint = false;
|
||||
sessionUnlocked();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
@@ -12,77 +11,25 @@ Singleton {
|
||||
property string username: ""
|
||||
property string fullName: ""
|
||||
property string profilePicture: ""
|
||||
property string uptime: ""
|
||||
property string shortUptime: ""
|
||||
property string hostname: ""
|
||||
property bool profileAvailable: false
|
||||
|
||||
function getUserInfo() {
|
||||
Proc.runCommand("userInfo", ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""], (output, exitCode) => {
|
||||
Proc.runCommand("userInfo", ["sh", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
root.username = "User"
|
||||
root.fullName = "User"
|
||||
root.hostname = "System"
|
||||
return
|
||||
root.username = "User";
|
||||
root.fullName = "User";
|
||||
root.hostname = "System";
|
||||
return;
|
||||
}
|
||||
const parts = output.trim().split("|")
|
||||
const parts = output.trim().split("|");
|
||||
if (parts.length >= 3) {
|
||||
root.username = parts[0] || ""
|
||||
root.fullName = parts[1] || parts[0] || ""
|
||||
root.hostname = parts[2] || ""
|
||||
root.username = parts[0] || "";
|
||||
root.fullName = parts[1] || parts[0] || "";
|
||||
root.hostname = parts[2] || "";
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getUptime() {
|
||||
Proc.runCommand("uptime", ["cat", "/proc/uptime"], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
root.uptime = "Unknown"
|
||||
return
|
||||
}
|
||||
const seconds = parseInt(output.split(" ")[0])
|
||||
const days = Math.floor(seconds / 86400)
|
||||
const hours = Math.floor((seconds % 86400) / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
|
||||
const parts = []
|
||||
if (days > 0) {
|
||||
parts.push(`${days} day${days === 1 ? "" : "s"}`)
|
||||
}
|
||||
if (hours > 0) {
|
||||
parts.push(`${hours} hour${hours === 1 ? "" : "s"}`)
|
||||
}
|
||||
if (minutes > 0) {
|
||||
parts.push(`${minutes} minute${minutes === 1 ? "" : "s"}`)
|
||||
}
|
||||
|
||||
if (parts.length > 0) {
|
||||
root.uptime = `up ${parts.join(", ")}`
|
||||
} else {
|
||||
root.uptime = `up ${seconds} seconds`
|
||||
}
|
||||
|
||||
let shortUptime = "up"
|
||||
if (days > 0) {
|
||||
shortUptime += ` ${days}d`
|
||||
}
|
||||
if (hours > 0) {
|
||||
shortUptime += ` ${hours}h`
|
||||
}
|
||||
if (minutes > 0) {
|
||||
shortUptime += ` ${minutes}m`
|
||||
}
|
||||
root.shortUptime = shortUptime
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function refreshUserInfo() {
|
||||
getUserInfo()
|
||||
getUptime()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
getUserInfo()
|
||||
getUptime()
|
||||
}
|
||||
Component.onCompleted: getUserInfo()
|
||||
}
|
||||
|
||||
@@ -27,39 +27,41 @@ PanelWindow {
|
||||
signal osdHidden
|
||||
|
||||
function show() {
|
||||
OSDManager.showOSD(root)
|
||||
closeTimer.stop()
|
||||
shouldBeVisible = true
|
||||
visible = true
|
||||
hideTimer.restart()
|
||||
osdShown()
|
||||
if (SessionData.suppressOSD)
|
||||
return;
|
||||
OSDManager.showOSD(root);
|
||||
closeTimer.stop();
|
||||
shouldBeVisible = true;
|
||||
visible = true;
|
||||
hideTimer.restart();
|
||||
osdShown();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
shouldBeVisible = false
|
||||
closeTimer.restart()
|
||||
shouldBeVisible = false;
|
||||
closeTimer.restart();
|
||||
}
|
||||
|
||||
function resetHideTimer() {
|
||||
if (shouldBeVisible) {
|
||||
hideTimer.restart()
|
||||
hideTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
function updateHoverState() {
|
||||
let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered
|
||||
let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered;
|
||||
if (enableMouseInteraction) {
|
||||
if (isHovered) {
|
||||
hideTimer.stop()
|
||||
hideTimer.stop();
|
||||
} else if (shouldBeVisible) {
|
||||
hideTimer.restart()
|
||||
hideTimer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setChildHovered(hovered) {
|
||||
osdContainer.childHovered = hovered
|
||||
updateHoverState()
|
||||
osdContainer.childHovered = hovered;
|
||||
updateHoverState();
|
||||
}
|
||||
|
||||
screen: modelData
|
||||
@@ -78,88 +80,92 @@ PanelWindow {
|
||||
readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter
|
||||
|
||||
readonly property real barThickness: {
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
||||
if (!defaultBar || !(defaultBar.visible ?? true)) return 0
|
||||
const innerPadding = defaultBar.innerPadding ?? 4
|
||||
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6)
|
||||
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding))
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||
if (!defaultBar || !(defaultBar.visible ?? true))
|
||||
return 0;
|
||||
const innerPadding = defaultBar.innerPadding ?? 4;
|
||||
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
|
||||
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
|
||||
}
|
||||
|
||||
readonly property real barOffset: {
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
||||
if (!defaultBar || !(defaultBar.visible ?? true)) return 0
|
||||
const spacing = defaultBar.spacing ?? 4
|
||||
const bottomGap = defaultBar.bottomGap ?? 0
|
||||
return barThickness + spacing + bottomGap
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||
if (!defaultBar || !(defaultBar.visible ?? true))
|
||||
return 0;
|
||||
const spacing = defaultBar.spacing ?? 4;
|
||||
const bottomGap = defaultBar.bottomGap ?? 0;
|
||||
return barThickness + spacing + bottomGap;
|
||||
}
|
||||
|
||||
readonly property real dockThickness: {
|
||||
if (!SettingsData.showDock) return 0
|
||||
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10
|
||||
if (!SettingsData.showDock)
|
||||
return 0;
|
||||
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10;
|
||||
}
|
||||
|
||||
readonly property real dockOffset: {
|
||||
if (!SettingsData.showDock || SettingsData.dockAutoHide) return 0
|
||||
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin
|
||||
if (!SettingsData.showDock || SettingsData.dockAutoHide)
|
||||
return 0;
|
||||
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin;
|
||||
}
|
||||
|
||||
readonly property real alignedX: {
|
||||
const margin = Theme.spacingM
|
||||
const centerX = (screenWidth - alignedWidth) / 2
|
||||
const margin = Theme.spacingM;
|
||||
const centerX = (screenWidth - alignedWidth) / 2;
|
||||
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
||||
|
||||
switch (SettingsData.osdPosition) {
|
||||
case SettingsData.Position.Left:
|
||||
case SettingsData.Position.Bottom:
|
||||
const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0
|
||||
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr)
|
||||
const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
|
||||
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
|
||||
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr);
|
||||
case SettingsData.Position.Top:
|
||||
case SettingsData.Position.Right:
|
||||
const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0
|
||||
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr)
|
||||
const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
|
||||
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr);
|
||||
case SettingsData.Position.LeftCenter:
|
||||
const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0
|
||||
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr)
|
||||
const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
|
||||
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
|
||||
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr);
|
||||
case SettingsData.Position.RightCenter:
|
||||
const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0
|
||||
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr)
|
||||
const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
|
||||
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr);
|
||||
case SettingsData.Position.TopCenter:
|
||||
case SettingsData.Position.BottomCenter:
|
||||
default:
|
||||
return Theme.snap(centerX, dpr)
|
||||
return Theme.snap(centerX, dpr);
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real alignedY: {
|
||||
const margin = Theme.spacingM
|
||||
const centerY = (screenHeight - alignedHeight) / 2
|
||||
const margin = Theme.spacingM;
|
||||
const centerY = (screenHeight - alignedHeight) / 2;
|
||||
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top
|
||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
||||
|
||||
switch (SettingsData.osdPosition) {
|
||||
case SettingsData.Position.Top:
|
||||
case SettingsData.Position.Left:
|
||||
case SettingsData.Position.TopCenter:
|
||||
const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0
|
||||
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr)
|
||||
const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0;
|
||||
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0;
|
||||
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr);
|
||||
case SettingsData.Position.Right:
|
||||
case SettingsData.Position.Bottom:
|
||||
case SettingsData.Position.BottomCenter:
|
||||
const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0
|
||||
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0
|
||||
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr)
|
||||
const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0;
|
||||
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0;
|
||||
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr);
|
||||
case SettingsData.Position.LeftCenter:
|
||||
case SettingsData.Position.RightCenter:
|
||||
default:
|
||||
return Theme.snap(centerY, dpr)
|
||||
return Theme.snap(centerY, dpr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +183,9 @@ PanelWindow {
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!enableMouseInteraction || !mouseArea.containsMouse) {
|
||||
hide()
|
||||
hide();
|
||||
} else {
|
||||
hideTimer.restart()
|
||||
hideTimer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,8 +195,8 @@ PanelWindow {
|
||||
interval: animationDuration + 50
|
||||
onTriggered: {
|
||||
if (!shouldBeVisible) {
|
||||
visible = false
|
||||
osdHidden()
|
||||
visible = false;
|
||||
osdHidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,8 +245,8 @@ PanelWindow {
|
||||
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax))
|
||||
shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest
|
||||
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha)
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user