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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
package:
|
package:
|
||||||
description: 'Package to upload (dms, dms-git, or all)'
|
description: 'Package to upload (dms, dms-git, dms-greeter, or all)'
|
||||||
required: false
|
required: false
|
||||||
default: 'dms-git'
|
default: 'dms-git'
|
||||||
rebuild_release:
|
rebuild_release:
|
||||||
@@ -83,6 +83,12 @@ jobs:
|
|||||||
echo "Uploading dms-git to PPA..."
|
echo "Uploading dms-git to PPA..."
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-git" dms-git questing
|
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
|
else
|
||||||
PPA_NAME="$PACKAGES"
|
PPA_NAME="$PACKAGES"
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
@@ -101,10 +107,13 @@ jobs:
|
|||||||
if [[ "$PACKAGES" == "all" ]]; then
|
if [[ "$PACKAGES" == "all" ]]; then
|
||||||
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
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 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
|
elif [[ "$PACKAGES" == "dms" ]]; then
|
||||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||||
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
||||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
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
|
fi
|
||||||
|
|
||||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||||
|
|||||||
@@ -182,8 +182,23 @@ case "$PACKAGE_NAME" in
|
|||||||
fi
|
fi
|
||||||
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)
|
||||||
# danksearch uses pre-built binary from releases, like dgop
|
# danksearch uses pre-built binary from releases
|
||||||
GIT_REPO="AvengeMedia/danksearch"
|
GIT_REPO="AvengeMedia/danksearch"
|
||||||
;;
|
;;
|
||||||
dgop)
|
dgop)
|
||||||
|
|||||||
@@ -224,6 +224,13 @@ if [ "$KEEP_BUILDS" = "false" ]; then
|
|||||||
REMOVED=$((REMOVED + 1))
|
REMOVED=$((REMOVED + 1))
|
||||||
fi
|
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
|
esac
|
||||||
|
|
||||||
if [ $REMOVED -gt 0 ]; then
|
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 isLightMode: false
|
||||||
property bool doNotDisturb: false
|
property bool doNotDisturb: false
|
||||||
property bool isSwitchingMode: 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 string wallpaperPath: ""
|
||||||
property bool perMonitorWallpaper: false
|
property bool perMonitorWallpaper: false
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ Item {
|
|||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
|
property bool animationsEnabled: true
|
||||||
readonly property bool useBackgroundWindow: true
|
readonly property bool useBackgroundWindow: true
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
@@ -75,6 +76,18 @@ Item {
|
|||||||
closeTimer.restart();
|
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() {
|
function toggle() {
|
||||||
shouldBeVisible ? close() : open();
|
shouldBeVisible ? close() : open();
|
||||||
}
|
}
|
||||||
@@ -180,6 +193,7 @@ Item {
|
|||||||
visible: root.showBackground && SettingsData.modalDarkenBackground
|
visible: root.showBackground && SettingsData.modalDarkenBackground
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -272,6 +286,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on animX {
|
Behavior on animX {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -280,6 +295,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on animY {
|
Behavior on animY {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -288,6 +304,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scaleValue {
|
Behavior on scaleValue {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -312,6 +329,7 @@ Item {
|
|||||||
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: animationDuration
|
duration: animationDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ DankModal {
|
|||||||
|
|
||||||
function hideInstant() {
|
function hideInstant() {
|
||||||
onColorSelectedCallback = null;
|
onColorSelectedCallback = null;
|
||||||
shouldBeVisible = false;
|
instantClose();
|
||||||
visible = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onColorSelected: color => {
|
onColorSelected: color => {
|
||||||
@@ -81,26 +80,57 @@ DankModal {
|
|||||||
selectedColor = currentColor;
|
selectedColor = currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickColorFromScreen() {
|
function applyPickedColor(colorStr) {
|
||||||
hideInstant();
|
if (colorStr.length < 7 || !colorStr.startsWith('#'))
|
||||||
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
return;
|
||||||
if (errorCode !== 0) {
|
const pickedColor = Qt.color(colorStr);
|
||||||
console.warn("hyprpicker exited with code:", errorCode);
|
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();
|
root.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const colorStr = output.trim();
|
const hexMatch = output.match(/Hex:\s*(#[0-9A-Fa-f]{6})/);
|
||||||
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
if (hexMatch) {
|
||||||
const pickedColor = Qt.color(colorStr);
|
applyPickedColor(hexMatch[1]);
|
||||||
root.selectedColor = pickedColor;
|
} else {
|
||||||
root.currentColor = pickedColor;
|
console.warn("Failed to parse niri pick-color output:", output);
|
||||||
root.updateFromColor(pickedColor);
|
|
||||||
copyColorToClipboard(colorStr);
|
|
||||||
root.show();
|
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
|
modalWidth: 680
|
||||||
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
|
||||||
backgroundColor: Theme.surfaceContainer
|
backgroundColor: Theme.surfaceContainer
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ FloatingWindow {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
visible = true;
|
visible = true;
|
||||||
UserInfoService.getUptime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ Rectangle {
|
|||||||
|
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
|
|
||||||
signal powerButtonClicked()
|
signal powerButtonClicked
|
||||||
signal lockRequested()
|
signal lockRequested
|
||||||
signal editModeToggled()
|
signal editModeToggled
|
||||||
signal settingsButtonClicked()
|
signal settingsButtonClicked
|
||||||
|
|
||||||
|
Component.onCompleted: DgopService.addRef("system")
|
||||||
|
Component.onDestruction: DgopService.removeRef("system")
|
||||||
|
|
||||||
implicitHeight: 70
|
implicitHeight: 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
Theme.outline.b, 0.08)
|
|
||||||
border.width: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -34,12 +36,12 @@ Rectangle {
|
|||||||
height: 60
|
height: 60
|
||||||
imageSource: {
|
imageSource: {
|
||||||
if (PortalService.profileImage === "")
|
if (PortalService.profileImage === "")
|
||||||
return ""
|
return "";
|
||||||
|
|
||||||
if (PortalService.profileImage.startsWith("/"))
|
if (PortalService.profileImage.startsWith("/"))
|
||||||
return "file://" + PortalService.profileImage
|
return "file://" + PortalService.profileImage;
|
||||||
|
|
||||||
return PortalService.profileImage
|
return PortalService.profileImage;
|
||||||
}
|
}
|
||||||
fallbackIcon: "person"
|
fallbackIcon: "person"
|
||||||
}
|
}
|
||||||
@@ -49,14 +51,13 @@ Rectangle {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: UserInfoService.fullName
|
text: UserInfoService.fullName || UserInfoService.username || "User"
|
||||||
|| UserInfoService.username || "User"
|
|
||||||
style: Typography.Style.Subtitle
|
style: Typography.Style.Subtitle
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: (UserInfoService.uptime || "Unknown")
|
text: DgopService.uptime || "Unknown"
|
||||||
style: Typography.Style.Caption
|
style: Typography.Style.Caption
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
@@ -77,7 +78,7 @@ Rectangle {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.lockRequested()
|
root.lockRequested();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +98,8 @@ Rectangle {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.settingsButtonClicked()
|
root.settingsButtonClicked();
|
||||||
settingsModal.show()
|
settingsModal.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,4 +112,4 @@ Rectangle {
|
|||||||
onClicked: root.editModeToggled()
|
onClicked: root.editModeToggled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,11 +84,8 @@ DankPopout {
|
|||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
collapseAll();
|
collapseAll();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (NetworkService.activeService) {
|
if (NetworkService.activeService)
|
||||||
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
|
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
|
||||||
}
|
|
||||||
if (UserInfoService)
|
|
||||||
UserInfoService.getUptime();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
@@ -7,6 +6,9 @@ import qs.Widgets
|
|||||||
Card {
|
Card {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
Component.onCompleted: DgopService.addRef("system")
|
||||||
|
Component.onDestruction: DgopService.removeRef("system")
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
@@ -21,12 +23,12 @@ Card {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
imageSource: {
|
imageSource: {
|
||||||
if (PortalService.profileImage === "")
|
if (PortalService.profileImage === "")
|
||||||
return ""
|
return "";
|
||||||
|
|
||||||
if (PortalService.profileImage.startsWith("/"))
|
if (PortalService.profileImage.startsWith("/"))
|
||||||
return "file://" + PortalService.profileImage
|
return "file://" + PortalService.profileImage;
|
||||||
|
|
||||||
return PortalService.profileImage
|
return PortalService.profileImage;
|
||||||
}
|
}
|
||||||
fallbackIcon: "person"
|
fallbackIcon: "person"
|
||||||
}
|
}
|
||||||
@@ -56,12 +58,16 @@ Card {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (CompositorService.isNiri) return "on niri"
|
if (CompositorService.isNiri)
|
||||||
if (CompositorService.isHyprland) return "on Hyprland"
|
return "on niri";
|
||||||
|
if (CompositorService.isHyprland)
|
||||||
|
return "on Hyprland";
|
||||||
// technically they might not be on mangowc, but its what we support in the docs
|
// technically they might not be on mangowc, but its what we support in the docs
|
||||||
if (CompositorService.isDwl) return "on MangoWC"
|
if (CompositorService.isDwl)
|
||||||
if (CompositorService.isSway) return "on Sway"
|
return "on MangoWC";
|
||||||
return ""
|
if (CompositorService.isSway)
|
||||||
|
return "on Sway";
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
||||||
@@ -82,29 +88,12 @@ Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: uptimeText
|
text: DgopService.shortUptime || "up"
|
||||||
|
|
||||||
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"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
elide: Text.ElideRight
|
|
||||||
width: availableWidth
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WeatherService.addRef();
|
WeatherService.addRef();
|
||||||
UserInfoService.refreshUserInfo();
|
UserInfoService.getUserInfo();
|
||||||
|
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
updateHyprlandLayout();
|
updateHyprlandLayout();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
@@ -69,7 +68,7 @@ DankFlickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: `${UserInfoService.uptime} • Boot: ${DgopService.bootTime}`
|
text: `${DgopService.uptime} • Boot: ${DgopService.bootTime}`
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.family: SettingsData.monoFontFamily
|
font.family: SettingsData.monoFontFamily
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
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)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -134,7 +131,6 @@ DankFlickable {
|
|||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -181,9 +177,7 @@ DankFlickable {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -275,7 +269,6 @@ DankFlickable {
|
|||||||
color: Theme.secondary
|
color: Theme.secondary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -365,22 +358,16 @@ DankFlickable {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -418,7 +405,6 @@ DankFlickable {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -495,7 +481,6 @@ DankFlickable {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
@@ -596,19 +581,11 @@ DankFlickable {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtCore
|
import QtCore
|
||||||
@@ -15,7 +14,6 @@ Singleton {
|
|||||||
readonly property PwNode sink: Pipewire.defaultAudioSink
|
readonly property PwNode sink: Pipewire.defaultAudioSink
|
||||||
readonly property PwNode source: Pipewire.defaultAudioSource
|
readonly property PwNode source: Pipewire.defaultAudioSource
|
||||||
|
|
||||||
property bool suppressOSD: true
|
|
||||||
property bool soundsAvailable: false
|
property bool soundsAvailable: false
|
||||||
property bool gsettingsAvailable: false
|
property bool gsettingsAvailable: false
|
||||||
property var availableSoundThemes: []
|
property var availableSoundThemes: []
|
||||||
@@ -33,12 +31,14 @@ Singleton {
|
|||||||
|
|
||||||
signal micMuteChanged
|
signal micMuteChanged
|
||||||
|
|
||||||
Timer {
|
Connections {
|
||||||
id: startupTimer
|
target: root.sink?.audio ?? null
|
||||||
interval: 500
|
|
||||||
repeat: false
|
function onVolumeChanged() {
|
||||||
running: true
|
if (SessionData.suppressOSD)
|
||||||
onTriggered: root.suppressOSD = false
|
return;
|
||||||
|
root.playVolumeChangeSoundIfEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectSoundsAvailability() {
|
function detectSoundsAvailability() {
|
||||||
@@ -47,35 +47,33 @@ Singleton {
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
Item {}
|
Item {}
|
||||||
`, root, "AudioService.TestComponent")
|
`, root, "AudioService.TestComponent");
|
||||||
if (testObj) {
|
if (testObj) {
|
||||||
testObj.destroy()
|
testObj.destroy();
|
||||||
}
|
}
|
||||||
soundsAvailable = true
|
soundsAvailable = true;
|
||||||
return true
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
soundsAvailable = false
|
soundsAvailable = false;
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkGsettings() {
|
function checkGsettings() {
|
||||||
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
|
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) {
|
if (gsettingsAvailable) {
|
||||||
scanSoundThemes()
|
scanSoundThemes();
|
||||||
getCurrentSoundTheme()
|
getCurrentSoundTheme();
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scanSoundThemes() {
|
function scanSoundThemes() {
|
||||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS")
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
|
||||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== ""
|
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))];
|
||||||
? 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 = `
|
const script = `
|
||||||
for base_dir in ${basePaths}; do
|
for base_dir in ${basePaths}; do
|
||||||
[ -d "$base_dir" ] || continue
|
[ -d "$base_dir" ] || continue
|
||||||
@@ -84,65 +82,63 @@ Singleton {
|
|||||||
basename "$theme_dir"
|
basename "$theme_dir"
|
||||||
done
|
done
|
||||||
done | sort -u
|
done | sort -u
|
||||||
`
|
`;
|
||||||
|
|
||||||
Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => {
|
Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => {
|
||||||
if (exitCode === 0 && output.trim()) {
|
if (exitCode === 0 && output.trim()) {
|
||||||
const themes = output.trim().split('\n').filter(t => t && t.length > 0)
|
const themes = output.trim().split('\n').filter(t => t && t.length > 0);
|
||||||
availableSoundThemes = themes
|
availableSoundThemes = themes;
|
||||||
} else {
|
} else {
|
||||||
availableSoundThemes = []
|
availableSoundThemes = [];
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentSoundTheme() {
|
function getCurrentSoundTheme() {
|
||||||
Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => {
|
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()) {
|
if (exitCode === 0 && output.trim()) {
|
||||||
currentSoundTheme = output.trim()
|
currentSoundTheme = output.trim();
|
||||||
console.log("AudioService: Current system sound theme:", currentSoundTheme)
|
console.log("AudioService: Current system sound theme:", currentSoundTheme);
|
||||||
if (SettingsData.useSystemSoundTheme) {
|
if (SettingsData.useSystemSoundTheme) {
|
||||||
discoverSoundFiles(currentSoundTheme)
|
discoverSoundFiles(currentSoundTheme);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentSoundTheme = ""
|
currentSoundTheme = "";
|
||||||
console.log("AudioService: No system sound theme found")
|
console.log("AudioService: No system sound theme found");
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSoundTheme(themeName) {
|
function setSoundTheme(themeName) {
|
||||||
if (!themeName || themeName === currentSoundTheme) {
|
if (!themeName || themeName === currentSoundTheme) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => {
|
Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
currentSoundTheme = themeName
|
currentSoundTheme = themeName;
|
||||||
if (SettingsData.useSystemSoundTheme) {
|
if (SettingsData.useSystemSoundTheme) {
|
||||||
discoverSoundFiles(themeName)
|
discoverSoundFiles(themeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function discoverSoundFiles(themeName) {
|
function discoverSoundFiles(themeName) {
|
||||||
if (!themeName) {
|
if (!themeName) {
|
||||||
soundFilePaths = {}
|
soundFilePaths = {};
|
||||||
if (soundsAvailable) {
|
if (soundsAvailable) {
|
||||||
destroySoundPlayers()
|
destroySoundPlayers();
|
||||||
createSoundPlayers()
|
createSoundPlayers();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS")
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
|
||||||
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== ""
|
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))];
|
||||||
? 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 extensions = ["oga", "ogg", "wav", "mp3", "flac"];
|
||||||
const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName
|
const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName;
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
for event_key in audio-volume-change power-plug power-unplug message message-new-instant; do
|
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
|
[ $found -eq 1 ] && break
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
`
|
`;
|
||||||
|
|
||||||
Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => {
|
Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => {
|
||||||
const paths = {}
|
const paths = {};
|
||||||
if (exitCode === 0 && output.trim()) {
|
if (exitCode === 0 && output.trim()) {
|
||||||
const lines = output.trim().split('\n')
|
const lines = output.trim().split('\n');
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
const parts = line.split('=')
|
const parts = line.split('=');
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
paths[parts[0]] = "file://" + parts[1]
|
paths[parts[0]] = "file://" + parts[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
soundFilePaths = paths
|
soundFilePaths = paths;
|
||||||
|
|
||||||
if (soundsAvailable) {
|
if (soundsAvailable) {
|
||||||
destroySoundPlayers()
|
destroySoundPlayers();
|
||||||
createSoundPlayers()
|
createSoundPlayers();
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSoundPath(soundEvent) {
|
function getSoundPath(soundEvent) {
|
||||||
@@ -208,45 +204,45 @@ Singleton {
|
|||||||
"power-unplug": "../assets/sounds/plasma/power-unplug.wav",
|
"power-unplug": "../assets/sounds/plasma/power-unplug.wav",
|
||||||
"message": "../assets/sounds/freedesktop/message.wav",
|
"message": "../assets/sounds/freedesktop/message.wav",
|
||||||
"message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav"
|
"message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav"
|
||||||
}
|
};
|
||||||
|
|
||||||
const specialConditions = {
|
const specialConditions = {
|
||||||
"smooth": ["audio-volume-change"]
|
"smooth": ["audio-volume-change"]
|
||||||
}
|
};
|
||||||
|
|
||||||
const themeLower = currentSoundTheme.toLowerCase()
|
const themeLower = currentSoundTheme.toLowerCase();
|
||||||
if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) {
|
if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) {
|
||||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav")
|
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
|
||||||
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath)
|
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath);
|
||||||
return bundledPath
|
return bundledPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) {
|
if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) {
|
||||||
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent])
|
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]);
|
||||||
return soundFilePaths[soundEvent]
|
return soundFilePaths[soundEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav")
|
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
|
||||||
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath)
|
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath);
|
||||||
return bundledPath
|
return bundledPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadSounds() {
|
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) {
|
if (SettingsData.useSystemSoundTheme && currentSoundTheme) {
|
||||||
discoverSoundFiles(currentSoundTheme)
|
discoverSoundFiles(currentSoundTheme);
|
||||||
} else {
|
} else {
|
||||||
soundFilePaths = {}
|
soundFilePaths = {};
|
||||||
if (soundsAvailable) {
|
if (soundsAvailable) {
|
||||||
destroySoundPlayers()
|
destroySoundPlayers();
|
||||||
createSoundPlayers()
|
createSoundPlayers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupMediaDevices() {
|
function setupMediaDevices() {
|
||||||
if (!soundsAvailable || mediaDevices) {
|
if (!soundsAvailable || mediaDevices) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -259,7 +255,7 @@ Singleton {
|
|||||||
console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description)
|
console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.MediaDevices")
|
`, root, "AudioService.MediaDevices");
|
||||||
|
|
||||||
if (mediaDevices) {
|
if (mediaDevices) {
|
||||||
mediaDevicesConnections = Qt.createQmlObject(`
|
mediaDevicesConnections = Qt.createQmlObject(`
|
||||||
@@ -272,48 +268,48 @@ Singleton {
|
|||||||
root.createSoundPlayers()
|
root.createSoundPlayers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.MediaDevicesConnections")
|
`, root, "AudioService.MediaDevicesConnections");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("AudioService: MediaDevices not available, using default audio output")
|
console.log("AudioService: MediaDevices not available, using default audio output");
|
||||||
mediaDevices = null
|
mediaDevices = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroySoundPlayers() {
|
function destroySoundPlayers() {
|
||||||
if (volumeChangeSound) {
|
if (volumeChangeSound) {
|
||||||
volumeChangeSound.destroy()
|
volumeChangeSound.destroy();
|
||||||
volumeChangeSound = null
|
volumeChangeSound = null;
|
||||||
}
|
}
|
||||||
if (powerPlugSound) {
|
if (powerPlugSound) {
|
||||||
powerPlugSound.destroy()
|
powerPlugSound.destroy();
|
||||||
powerPlugSound = null
|
powerPlugSound = null;
|
||||||
}
|
}
|
||||||
if (powerUnplugSound) {
|
if (powerUnplugSound) {
|
||||||
powerUnplugSound.destroy()
|
powerUnplugSound.destroy();
|
||||||
powerUnplugSound = null
|
powerUnplugSound = null;
|
||||||
}
|
}
|
||||||
if (normalNotificationSound) {
|
if (normalNotificationSound) {
|
||||||
normalNotificationSound.destroy()
|
normalNotificationSound.destroy();
|
||||||
normalNotificationSound = null
|
normalNotificationSound = null;
|
||||||
}
|
}
|
||||||
if (criticalNotificationSound) {
|
if (criticalNotificationSound) {
|
||||||
criticalNotificationSound.destroy()
|
criticalNotificationSound.destroy();
|
||||||
criticalNotificationSound = null
|
criticalNotificationSound = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSoundPlayers() {
|
function createSoundPlayers() {
|
||||||
if (!soundsAvailable) {
|
if (!soundsAvailable) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setupMediaDevices()
|
setupMediaDevices();
|
||||||
|
|
||||||
try {
|
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(`
|
volumeChangeSound = Qt.createQmlObject(`
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
@@ -323,9 +319,9 @@ Singleton {
|
|||||||
${deviceProperty}volume: 1.0
|
${deviceProperty}volume: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.VolumeChangeSound")
|
`, root, "AudioService.VolumeChangeSound");
|
||||||
|
|
||||||
const powerPlugPath = getSoundPath("power-plug")
|
const powerPlugPath = getSoundPath("power-plug");
|
||||||
powerPlugSound = Qt.createQmlObject(`
|
powerPlugSound = Qt.createQmlObject(`
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
@@ -335,9 +331,9 @@ Singleton {
|
|||||||
${deviceProperty}volume: 1.0
|
${deviceProperty}volume: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.PowerPlugSound")
|
`, root, "AudioService.PowerPlugSound");
|
||||||
|
|
||||||
const powerUnplugPath = getSoundPath("power-unplug")
|
const powerUnplugPath = getSoundPath("power-unplug");
|
||||||
powerUnplugSound = Qt.createQmlObject(`
|
powerUnplugSound = Qt.createQmlObject(`
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
@@ -347,9 +343,9 @@ Singleton {
|
|||||||
${deviceProperty}volume: 1.0
|
${deviceProperty}volume: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.PowerUnplugSound")
|
`, root, "AudioService.PowerUnplugSound");
|
||||||
|
|
||||||
const messagePath = getSoundPath("message")
|
const messagePath = getSoundPath("message");
|
||||||
normalNotificationSound = Qt.createQmlObject(`
|
normalNotificationSound = Qt.createQmlObject(`
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
@@ -359,9 +355,9 @@ Singleton {
|
|||||||
${deviceProperty}volume: 1.0
|
${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(`
|
criticalNotificationSound = Qt.createQmlObject(`
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
@@ -371,114 +367,114 @@ Singleton {
|
|||||||
${deviceProperty}volume: 1.0
|
${deviceProperty}volume: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root, "AudioService.CriticalNotificationSound")
|
`, root, "AudioService.CriticalNotificationSound");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("AudioService: Error creating sound players:", e)
|
console.warn("AudioService: Error creating sound players:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playVolumeChangeSound() {
|
function playVolumeChangeSound() {
|
||||||
if (soundsAvailable && volumeChangeSound) {
|
if (soundsAvailable && volumeChangeSound) {
|
||||||
volumeChangeSound.play()
|
volumeChangeSound.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playPowerPlugSound() {
|
function playPowerPlugSound() {
|
||||||
if (soundsAvailable && powerPlugSound) {
|
if (soundsAvailable && powerPlugSound) {
|
||||||
powerPlugSound.play()
|
powerPlugSound.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playPowerUnplugSound() {
|
function playPowerUnplugSound() {
|
||||||
if (soundsAvailable && powerUnplugSound) {
|
if (soundsAvailable && powerUnplugSound) {
|
||||||
powerUnplugSound.play()
|
powerUnplugSound.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playNormalNotificationSound() {
|
function playNormalNotificationSound() {
|
||||||
if (soundsAvailable && normalNotificationSound && !SessionData.doNotDisturb) {
|
if (soundsAvailable && normalNotificationSound && !SessionData.doNotDisturb) {
|
||||||
normalNotificationSound.play()
|
normalNotificationSound.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playCriticalNotificationSound() {
|
function playCriticalNotificationSound() {
|
||||||
if (soundsAvailable && criticalNotificationSound && !SessionData.doNotDisturb) {
|
if (soundsAvailable && criticalNotificationSound && !SessionData.doNotDisturb) {
|
||||||
criticalNotificationSound.play()
|
criticalNotificationSound.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playVolumeChangeSoundIfEnabled() {
|
function playVolumeChangeSoundIfEnabled() {
|
||||||
if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged) {
|
if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged) {
|
||||||
playVolumeChangeSound()
|
playVolumeChangeSound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayName(node) {
|
function displayName(node) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.properties && node.properties["device.description"]) {
|
if (node.properties && node.properties["device.description"]) {
|
||||||
return node.properties["device.description"]
|
return node.properties["device.description"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.description && node.description !== node.name) {
|
if (node.description && node.description !== node.name) {
|
||||||
return node.description
|
return node.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.nickname && node.nickname !== node.name) {
|
if (node.nickname && node.nickname !== node.name) {
|
||||||
return node.nickname
|
return node.nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.name.includes("analog-stereo")) {
|
if (node.name.includes("analog-stereo")) {
|
||||||
return "Built-in Speakers"
|
return "Built-in Speakers";
|
||||||
}
|
}
|
||||||
if (node.name.includes("bluez")) {
|
if (node.name.includes("bluez")) {
|
||||||
return "Bluetooth Audio"
|
return "Bluetooth Audio";
|
||||||
}
|
}
|
||||||
if (node.name.includes("usb")) {
|
if (node.name.includes("usb")) {
|
||||||
return "USB Audio"
|
return "USB Audio";
|
||||||
}
|
}
|
||||||
if (node.name.includes("hdmi")) {
|
if (node.name.includes("hdmi")) {
|
||||||
return "HDMI Audio"
|
return "HDMI Audio";
|
||||||
}
|
}
|
||||||
|
|
||||||
return node.name
|
return node.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function subtitle(name) {
|
function subtitle(name) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.includes('usb-')) {
|
if (name.includes('usb-')) {
|
||||||
if (name.includes('SteelSeries')) {
|
if (name.includes('SteelSeries')) {
|
||||||
return "USB Gaming Headset"
|
return "USB Gaming Headset";
|
||||||
}
|
}
|
||||||
if (name.includes('Generic')) {
|
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('pci-')) {
|
||||||
if (name.includes('01_00.1') || name.includes('01:00.1')) {
|
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')) {
|
if (name.includes('bluez')) {
|
||||||
return "Bluetooth Audio"
|
return "Bluetooth Audio";
|
||||||
}
|
}
|
||||||
if (name.includes('analog')) {
|
if (name.includes('analog')) {
|
||||||
return "Built-in Audio"
|
return "Built-in Audio";
|
||||||
}
|
}
|
||||||
if (name.includes('hdmi')) {
|
if (name.includes('hdmi')) {
|
||||||
return "HDMI Audio"
|
return "HDMI Audio";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
PwObjectTracker {
|
PwObjectTracker {
|
||||||
@@ -487,136 +483,134 @@ Singleton {
|
|||||||
|
|
||||||
function setVolume(percentage) {
|
function setVolume(percentage) {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio) {
|
||||||
return "No audio sink available"
|
return "No audio sink available";
|
||||||
}
|
}
|
||||||
|
|
||||||
const clampedVolume = Math.max(0, Math.min(100, percentage))
|
const clampedVolume = Math.max(0, Math.min(100, percentage));
|
||||||
root.sink.audio.volume = clampedVolume / 100
|
root.sink.audio.volume = clampedVolume / 100;
|
||||||
return `Volume set to ${clampedVolume}%`
|
return `Volume set to ${clampedVolume}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMute() {
|
function toggleMute() {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio) {
|
||||||
return "No audio sink available"
|
return "No audio sink available";
|
||||||
}
|
}
|
||||||
|
|
||||||
root.sink.audio.muted = !root.sink.audio.muted
|
root.sink.audio.muted = !root.sink.audio.muted;
|
||||||
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted"
|
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMicVolume(percentage) {
|
function setMicVolume(percentage) {
|
||||||
if (!root.source?.audio) {
|
if (!root.source?.audio) {
|
||||||
return "No audio source available"
|
return "No audio source available";
|
||||||
}
|
}
|
||||||
|
|
||||||
const clampedVolume = Math.max(0, Math.min(100, percentage))
|
const clampedVolume = Math.max(0, Math.min(100, percentage));
|
||||||
root.source.audio.volume = clampedVolume / 100
|
root.source.audio.volume = clampedVolume / 100;
|
||||||
return `Microphone volume set to ${clampedVolume}%`
|
return `Microphone volume set to ${clampedVolume}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMicMute() {
|
function toggleMicMute() {
|
||||||
if (!root.source?.audio) {
|
if (!root.source?.audio) {
|
||||||
return "No audio source available"
|
return "No audio source available";
|
||||||
}
|
}
|
||||||
|
|
||||||
root.source.audio.muted = !root.source.audio.muted
|
root.source.audio.muted = !root.source.audio.muted;
|
||||||
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted"
|
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted";
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "audio"
|
target: "audio"
|
||||||
|
|
||||||
function setvolume(percentage: string): string {
|
function setvolume(percentage: string): string {
|
||||||
return root.setVolume(parseInt(percentage))
|
return root.setVolume(parseInt(percentage));
|
||||||
}
|
}
|
||||||
|
|
||||||
function increment(step: string): string {
|
function increment(step: string): string {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio) {
|
||||||
return "No audio sink available"
|
return "No audio sink available";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.sink.audio.muted) {
|
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 currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||||
const stepValue = parseInt(step || "5")
|
const stepValue = parseInt(step || "5");
|
||||||
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue))
|
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue));
|
||||||
|
|
||||||
root.sink.audio.volume = newVolume / 100
|
root.sink.audio.volume = newVolume / 100;
|
||||||
root.playVolumeChangeSoundIfEnabled()
|
return `Volume increased to ${newVolume}%`;
|
||||||
return `Volume increased to ${newVolume}%`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decrement(step: string): string {
|
function decrement(step: string): string {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio) {
|
||||||
return "No audio sink available"
|
return "No audio sink available";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.sink.audio.muted) {
|
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 currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||||
const stepValue = parseInt(step || "5")
|
const stepValue = parseInt(step || "5");
|
||||||
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue))
|
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue));
|
||||||
|
|
||||||
root.sink.audio.volume = newVolume / 100
|
root.sink.audio.volume = newVolume / 100;
|
||||||
root.playVolumeChangeSoundIfEnabled()
|
return `Volume decreased to ${newVolume}%`;
|
||||||
return `Volume decreased to ${newVolume}%`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mute(): string {
|
function mute(): string {
|
||||||
return root.toggleMute()
|
return root.toggleMute();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setmic(percentage: string): string {
|
function setmic(percentage: string): string {
|
||||||
return root.setMicVolume(parseInt(percentage))
|
return root.setMicVolume(parseInt(percentage));
|
||||||
}
|
}
|
||||||
|
|
||||||
function micmute(): string {
|
function micmute(): string {
|
||||||
const result = root.toggleMicMute()
|
const result = root.toggleMicMute();
|
||||||
root.micMuteChanged()
|
root.micMuteChanged();
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function status(): string {
|
function status(): string {
|
||||||
let result = "Audio Status:\n"
|
let result = "Audio Status:\n";
|
||||||
|
|
||||||
if (root.sink?.audio) {
|
if (root.sink?.audio) {
|
||||||
const volume = Math.round(root.sink.audio.volume * 100)
|
const volume = Math.round(root.sink.audio.volume * 100);
|
||||||
const muteStatus = root.sink.audio.muted ? " (muted)" : ""
|
const muteStatus = root.sink.audio.muted ? " (muted)" : "";
|
||||||
result += `Output: ${volume}%${muteStatus}\n`
|
result += `Output: ${volume}%${muteStatus}\n`;
|
||||||
} else {
|
} else {
|
||||||
result += "Output: No sink available\n"
|
result += "Output: No sink available\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.source?.audio) {
|
if (root.source?.audio) {
|
||||||
const micVolume = Math.round(root.source.audio.volume * 100)
|
const micVolume = Math.round(root.source.audio.volume * 100);
|
||||||
const muteStatus = root.source.audio.muted ? " (muted)" : ""
|
const muteStatus = root.source.audio.muted ? " (muted)" : "";
|
||||||
result += `Input: ${micVolume}%${muteStatus}`
|
result += `Input: ${micVolume}%${muteStatus}`;
|
||||||
} else {
|
} else {
|
||||||
result += "Input: No source available"
|
result += "Input: No source available";
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onUseSystemSoundThemeChanged() {
|
function onUseSystemSoundThemeChanged() {
|
||||||
reloadSounds()
|
reloadSounds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!detectSoundsAvailability()) {
|
if (!detectSoundsAvailability()) {
|
||||||
console.warn("AudioService: QtMultimedia not available - sound effects disabled")
|
console.warn("AudioService: QtMultimedia not available - sound effects disabled");
|
||||||
} else {
|
} else {
|
||||||
console.info("AudioService: Sound effects enabled")
|
console.info("AudioService: Sound effects enabled");
|
||||||
checkGsettings()
|
checkGsettings();
|
||||||
Qt.callLater(createSoundPlayers)
|
Qt.callLater(createSoundPlayers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -72,504 +71,547 @@ Singleton {
|
|||||||
property string bootTime: ""
|
property string bootTime: ""
|
||||||
property string motherboard: ""
|
property string motherboard: ""
|
||||||
property string biosVersion: ""
|
property string biosVersion: ""
|
||||||
|
property string uptime: ""
|
||||||
|
property string shortUptime: ""
|
||||||
|
|
||||||
property int historySize: 60
|
property int historySize: 60
|
||||||
property var cpuHistory: []
|
property var cpuHistory: []
|
||||||
property var memoryHistory: []
|
property var memoryHistory: []
|
||||||
property var networkHistory: ({
|
property var networkHistory: ({
|
||||||
"rx": [],
|
"rx": [],
|
||||||
"tx": []
|
"tx": []
|
||||||
})
|
})
|
||||||
property var diskHistory: ({
|
property var diskHistory: ({
|
||||||
"read": [],
|
"read": [],
|
||||||
"write": []
|
"write": []
|
||||||
})
|
})
|
||||||
|
|
||||||
function addRef(modules = null) {
|
function addRef(modules = null) {
|
||||||
refCount++
|
refCount++;
|
||||||
let modulesChanged = false
|
let modulesChanged = false;
|
||||||
|
|
||||||
if (modules) {
|
if (modules) {
|
||||||
const modulesToAdd = Array.isArray(modules) ? modules : [modules]
|
const modulesToAdd = Array.isArray(modules) ? modules : [modules];
|
||||||
for (const module of modulesToAdd) {
|
for (const module of modulesToAdd) {
|
||||||
// Increment reference count for this module
|
// Increment reference count for this module
|
||||||
const currentCount = moduleRefCounts[module] || 0
|
const currentCount = moduleRefCounts[module] || 0;
|
||||||
moduleRefCounts[module] = currentCount + 1
|
moduleRefCounts[module] = currentCount + 1;
|
||||||
console.log("Adding ref for module:", module, "count:", moduleRefCounts[module])
|
console.log("Adding ref for module:", module, "count:", moduleRefCounts[module]);
|
||||||
|
|
||||||
// Add to enabled modules if not already there
|
// Add to enabled modules if not already there
|
||||||
if (enabledModules.indexOf(module) === -1) {
|
if (enabledModules.indexOf(module) === -1) {
|
||||||
enabledModules.push(module)
|
enabledModules.push(module);
|
||||||
modulesChanged = true
|
modulesChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modulesChanged || refCount === 1) {
|
if (modulesChanged || refCount === 1) {
|
||||||
enabledModules = enabledModules.slice() // Force property change
|
enabledModules = enabledModules.slice(); // Force property change
|
||||||
moduleRefCounts = Object.assign({}, moduleRefCounts) // Force property change
|
moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change
|
||||||
updateAllStats()
|
updateAllStats();
|
||||||
} else if (gpuPciIds.length > 0 && refCount > 0) {
|
} else if (gpuPciIds.length > 0 && refCount > 0) {
|
||||||
// If we have GPU PCI IDs and active modules, make sure to update
|
// 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
|
// This handles the case where PCI IDs were loaded after modules were added
|
||||||
updateAllStats()
|
updateAllStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeRef(modules = null) {
|
function removeRef(modules = null) {
|
||||||
refCount = Math.max(0, refCount - 1)
|
refCount = Math.max(0, refCount - 1);
|
||||||
let modulesChanged = false
|
let modulesChanged = false;
|
||||||
|
|
||||||
if (modules) {
|
if (modules) {
|
||||||
const modulesToRemove = Array.isArray(modules) ? modules : [modules]
|
const modulesToRemove = Array.isArray(modules) ? modules : [modules];
|
||||||
for (const module of modulesToRemove) {
|
for (const module of modulesToRemove) {
|
||||||
const currentCount = moduleRefCounts[module] || 0
|
const currentCount = moduleRefCounts[module] || 0;
|
||||||
if (currentCount > 1) {
|
if (currentCount > 1) {
|
||||||
// Decrement reference count
|
// Decrement reference count
|
||||||
moduleRefCounts[module] = currentCount - 1
|
moduleRefCounts[module] = currentCount - 1;
|
||||||
console.log("Removing ref for module:", module, "count:", moduleRefCounts[module])
|
console.log("Removing ref for module:", module, "count:", moduleRefCounts[module]);
|
||||||
} else if (currentCount === 1) {
|
} else if (currentCount === 1) {
|
||||||
// Remove completely when count reaches 0
|
// Remove completely when count reaches 0
|
||||||
delete moduleRefCounts[module]
|
delete moduleRefCounts[module];
|
||||||
const index = enabledModules.indexOf(module)
|
const index = enabledModules.indexOf(module);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
enabledModules.splice(index, 1)
|
enabledModules.splice(index, 1);
|
||||||
modulesChanged = true
|
modulesChanged = true;
|
||||||
console.log("Disabling module:", module, "(no more refs)")
|
console.log("Disabling module:", module, "(no more refs)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modulesChanged) {
|
if (modulesChanged) {
|
||||||
enabledModules = enabledModules.slice() // Force property change
|
enabledModules = enabledModules.slice(); // Force property change
|
||||||
moduleRefCounts = Object.assign({}, moduleRefCounts) // Force property change
|
moduleRefCounts = Object.assign({}, moduleRefCounts); // Force property change
|
||||||
|
|
||||||
// Clear cursor data when CPU or process modules are no longer active
|
// Clear cursor data when CPU or process modules are no longer active
|
||||||
if (!enabledModules.includes("cpu")) {
|
if (!enabledModules.includes("cpu")) {
|
||||||
cpuCursor = ""
|
cpuCursor = "";
|
||||||
cpuSampleCount = 0
|
cpuSampleCount = 0;
|
||||||
}
|
}
|
||||||
if (!enabledModules.includes("processes")) {
|
if (!enabledModules.includes("processes")) {
|
||||||
procCursor = ""
|
procCursor = "";
|
||||||
processSampleCount = 0
|
processSampleCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setGpuPciIds(pciIds) {
|
function setGpuPciIds(pciIds) {
|
||||||
gpuPciIds = Array.isArray(pciIds) ? pciIds : []
|
gpuPciIds = Array.isArray(pciIds) ? pciIds : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGpuPciId(pciId) {
|
function addGpuPciId(pciId) {
|
||||||
const currentCount = gpuPciIdRefCounts[pciId] || 0
|
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||||
gpuPciIdRefCounts[pciId] = currentCount + 1
|
gpuPciIdRefCounts[pciId] = currentCount + 1;
|
||||||
|
|
||||||
// Add to gpuPciIds array if not already there
|
// Add to gpuPciIds array if not already there
|
||||||
if (!gpuPciIds.includes(pciId)) {
|
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
|
// Force property change notification
|
||||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts)
|
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeGpuPciId(pciId) {
|
function removeGpuPciId(pciId) {
|
||||||
const currentCount = gpuPciIdRefCounts[pciId] || 0
|
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||||
if (currentCount > 1) {
|
if (currentCount > 1) {
|
||||||
// Decrement reference count
|
// Decrement reference count
|
||||||
gpuPciIdRefCounts[pciId] = currentCount - 1
|
gpuPciIdRefCounts[pciId] = currentCount - 1;
|
||||||
console.log("Removing GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId])
|
console.log("Removing GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId]);
|
||||||
} else if (currentCount === 1) {
|
} else if (currentCount === 1) {
|
||||||
// Remove completely when count reaches 0
|
// Remove completely when count reaches 0
|
||||||
delete gpuPciIdRefCounts[pciId]
|
delete gpuPciIdRefCounts[pciId];
|
||||||
const index = gpuPciIds.indexOf(pciId)
|
const index = gpuPciIds.indexOf(pciId);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
gpuPciIds = gpuPciIds.slice()
|
gpuPciIds = gpuPciIds.slice();
|
||||||
gpuPciIds.splice(index, 1)
|
gpuPciIds.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear temperature data for this GPU when no longer monitored
|
// Clear temperature data for this GPU when no longer monitored
|
||||||
if (availableGpus && availableGpus.length > 0) {
|
if (availableGpus && availableGpus.length > 0) {
|
||||||
const updatedGpus = availableGpus.slice()
|
const updatedGpus = availableGpus.slice();
|
||||||
for (var i = 0; i < updatedGpus.length; i++) {
|
for (var i = 0; i < updatedGpus.length; i++) {
|
||||||
if (updatedGpus[i].pciId === pciId) {
|
if (updatedGpus[i].pciId === pciId) {
|
||||||
updatedGpus[i] = Object.assign({}, updatedGpus[i], {
|
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
|
// Force property change notification
|
||||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts)
|
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setProcessOptions(limit = 20, sort = "cpu", disableCpu = false) {
|
function setProcessOptions(limit = 20, sort = "cpu", disableCpu = false) {
|
||||||
processLimit = limit
|
processLimit = limit;
|
||||||
processSort = sort
|
processSort = sort;
|
||||||
noCpu = disableCpu
|
noCpu = disableCpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAllStats() {
|
function updateAllStats() {
|
||||||
if (dgopAvailable && refCount > 0 && enabledModules.length > 0) {
|
if (dgopAvailable && refCount > 0 && enabledModules.length > 0) {
|
||||||
isUpdating = true
|
isUpdating = true;
|
||||||
dgopProcess.running = true
|
dgopProcess.running = true;
|
||||||
} else {
|
} else {
|
||||||
isUpdating = false
|
isUpdating = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeGpuMetadata() {
|
function initializeGpuMetadata() {
|
||||||
if (!dgopAvailable)
|
if (!dgopAvailable)
|
||||||
return
|
return;
|
||||||
gpuInitProcess.running = true
|
gpuInitProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeSystemMetadata() {
|
function initializeSystemMetadata() {
|
||||||
if (!dgopAvailable)
|
if (!dgopAvailable)
|
||||||
return
|
return;
|
||||||
systemInitProcess.running = true
|
systemInitProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDgopCommand() {
|
function buildDgopCommand() {
|
||||||
const cmd = ["dgop", "meta", "--json"]
|
const cmd = ["dgop", "meta", "--json"];
|
||||||
|
|
||||||
if (enabledModules.length === 0) {
|
if (enabledModules.length === 0) {
|
||||||
// Don't run if no modules are needed
|
// Don't run if no modules are needed
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace 'gpu' with 'gpu-temp' when we have PCI IDs to monitor
|
// Replace 'gpu' with 'gpu-temp' when we have PCI IDs to monitor
|
||||||
const finalModules = []
|
const finalModules = [];
|
||||||
for (const module of enabledModules) {
|
for (const module of enabledModules) {
|
||||||
if (module === "gpu" && gpuPciIds.length > 0) {
|
if (module === "gpu" && gpuPciIds.length > 0) {
|
||||||
finalModules.push("gpu-temp")
|
finalModules.push("gpu-temp");
|
||||||
} else if (module !== "gpu") {
|
} else if (module !== "gpu") {
|
||||||
finalModules.push(module)
|
finalModules.push(module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add gpu-temp module automatically when we have PCI IDs to monitor
|
// Add gpu-temp module automatically when we have PCI IDs to monitor
|
||||||
if (gpuPciIds.length > 0 && finalModules.indexOf("gpu-temp") === -1) {
|
if (gpuPciIds.length > 0 && finalModules.indexOf("gpu-temp") === -1) {
|
||||||
finalModules.push("gpu-temp")
|
finalModules.push("gpu-temp");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabledModules.indexOf("all") !== -1) {
|
if (enabledModules.indexOf("all") !== -1) {
|
||||||
cmd.push("--modules", "all")
|
cmd.push("--modules", "all");
|
||||||
} else if (finalModules.length > 0) {
|
} else if (finalModules.length > 0) {
|
||||||
const moduleList = finalModules.join(",")
|
const moduleList = finalModules.join(",");
|
||||||
cmd.push("--modules", moduleList)
|
cmd.push("--modules", moduleList);
|
||||||
} else {
|
} else {
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add cursor data if available for accurate CPU percentages
|
// Add cursor data if available for accurate CPU percentages
|
||||||
if ((enabledModules.includes("cpu") || enabledModules.includes("all")) && cpuCursor) {
|
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) {
|
if ((enabledModules.includes("processes") || enabledModules.includes("all")) && procCursor) {
|
||||||
cmd.push("--proc-cursor", procCursor)
|
cmd.push("--proc-cursor", procCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpuPciIds.length > 0) {
|
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) {
|
if (enabledModules.indexOf("processes") !== -1 || enabledModules.indexOf("all") !== -1) {
|
||||||
cmd.push("--limit", "100") // Get more data for client sorting
|
cmd.push("--limit", "100"); // Get more data for client sorting
|
||||||
cmd.push("--sort", "cpu") // Always get CPU sorted data
|
cmd.push("--sort", "cpu"); // Always get CPU sorted data
|
||||||
if (noCpu) {
|
if (noCpu) {
|
||||||
cmd.push("--no-cpu")
|
cmd.push("--no-cpu");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseData(data) {
|
function parseData(data) {
|
||||||
if (data.cpu) {
|
if (data.cpu) {
|
||||||
const cpu = data.cpu
|
const cpu = data.cpu;
|
||||||
cpuSampleCount++
|
cpuSampleCount++;
|
||||||
|
|
||||||
cpuUsage = cpu.usage || 0
|
cpuUsage = cpu.usage || 0;
|
||||||
cpuFrequency = cpu.frequency || 0
|
cpuFrequency = cpu.frequency || 0;
|
||||||
cpuTemperature = cpu.temperature || 0
|
cpuTemperature = cpu.temperature || 0;
|
||||||
cpuCores = cpu.count || 1
|
cpuCores = cpu.count || 1;
|
||||||
cpuModel = cpu.model || ""
|
cpuModel = cpu.model || "";
|
||||||
perCoreCpuUsage = cpu.coreUsage || []
|
perCoreCpuUsage = cpu.coreUsage || [];
|
||||||
addToHistory(cpuHistory, cpuUsage)
|
addToHistory(cpuHistory, cpuUsage);
|
||||||
|
|
||||||
if (cpu.cursor) {
|
if (cpu.cursor) {
|
||||||
cpuCursor = cpu.cursor
|
cpuCursor = cpu.cursor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.memory) {
|
if (data.memory) {
|
||||||
const mem = data.memory
|
const mem = data.memory;
|
||||||
const totalKB = mem.total || 0
|
const totalKB = mem.total || 0;
|
||||||
const availableKB = mem.available || 0
|
const availableKB = mem.available || 0;
|
||||||
const freeKB = mem.free || 0
|
const freeKB = mem.free || 0;
|
||||||
|
|
||||||
totalMemoryMB = totalKB / 1024
|
totalMemoryMB = totalKB / 1024;
|
||||||
availableMemoryMB = availableKB / 1024
|
availableMemoryMB = availableKB / 1024;
|
||||||
freeMemoryMB = freeKB / 1024
|
freeMemoryMB = freeKB / 1024;
|
||||||
usedMemoryMB = totalMemoryMB - availableMemoryMB
|
usedMemoryMB = totalMemoryMB - availableMemoryMB;
|
||||||
memoryUsage = totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0
|
memoryUsage = totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0;
|
||||||
|
|
||||||
totalMemoryKB = totalKB
|
totalMemoryKB = totalKB;
|
||||||
usedMemoryKB = totalKB - availableKB
|
usedMemoryKB = totalKB - availableKB;
|
||||||
totalSwapKB = mem.swaptotal || 0
|
totalSwapKB = mem.swaptotal || 0;
|
||||||
usedSwapKB = (mem.swaptotal || 0) - (mem.swapfree || 0)
|
usedSwapKB = (mem.swaptotal || 0) - (mem.swapfree || 0);
|
||||||
|
|
||||||
addToHistory(memoryHistory, memoryUsage)
|
addToHistory(memoryHistory, memoryUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.network && Array.isArray(data.network)) {
|
if (data.network && Array.isArray(data.network)) {
|
||||||
networkInterfaces = data.network
|
networkInterfaces = data.network;
|
||||||
|
|
||||||
let totalRx = 0
|
let totalRx = 0;
|
||||||
let totalTx = 0
|
let totalTx = 0;
|
||||||
for (const iface of data.network) {
|
for (const iface of data.network) {
|
||||||
totalRx += iface.rx || 0
|
totalRx += iface.rx || 0;
|
||||||
totalTx += iface.tx || 0
|
totalTx += iface.tx || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastNetworkStats) {
|
if (lastNetworkStats) {
|
||||||
const timeDiff = updateInterval / 1000
|
const timeDiff = updateInterval / 1000;
|
||||||
const rxDiff = totalRx - lastNetworkStats.rx
|
const rxDiff = totalRx - lastNetworkStats.rx;
|
||||||
const txDiff = totalTx - lastNetworkStats.tx
|
const txDiff = totalTx - lastNetworkStats.tx;
|
||||||
networkRxRate = Math.max(0, rxDiff / timeDiff)
|
networkRxRate = Math.max(0, rxDiff / timeDiff);
|
||||||
networkTxRate = Math.max(0, txDiff / timeDiff)
|
networkTxRate = Math.max(0, txDiff / timeDiff);
|
||||||
addToHistory(networkHistory.rx, networkRxRate / 1024)
|
addToHistory(networkHistory.rx, networkRxRate / 1024);
|
||||||
addToHistory(networkHistory.tx, networkTxRate / 1024)
|
addToHistory(networkHistory.tx, networkTxRate / 1024);
|
||||||
}
|
}
|
||||||
lastNetworkStats = {
|
lastNetworkStats = {
|
||||||
"rx": totalRx,
|
"rx": totalRx,
|
||||||
"tx": totalTx
|
"tx": totalTx
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.disk && Array.isArray(data.disk)) {
|
if (data.disk && Array.isArray(data.disk)) {
|
||||||
diskDevices = data.disk
|
diskDevices = data.disk;
|
||||||
|
|
||||||
let totalRead = 0
|
let totalRead = 0;
|
||||||
let totalWrite = 0
|
let totalWrite = 0;
|
||||||
for (const disk of data.disk) {
|
for (const disk of data.disk) {
|
||||||
totalRead += (disk.read || 0) * 512
|
totalRead += (disk.read || 0) * 512;
|
||||||
totalWrite += (disk.write || 0) * 512
|
totalWrite += (disk.write || 0) * 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastDiskStats) {
|
if (lastDiskStats) {
|
||||||
const timeDiff = updateInterval / 1000
|
const timeDiff = updateInterval / 1000;
|
||||||
const readDiff = totalRead - lastDiskStats.read
|
const readDiff = totalRead - lastDiskStats.read;
|
||||||
const writeDiff = totalWrite - lastDiskStats.write
|
const writeDiff = totalWrite - lastDiskStats.write;
|
||||||
diskReadRate = Math.max(0, readDiff / timeDiff)
|
diskReadRate = Math.max(0, readDiff / timeDiff);
|
||||||
diskWriteRate = Math.max(0, writeDiff / timeDiff)
|
diskWriteRate = Math.max(0, writeDiff / timeDiff);
|
||||||
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024))
|
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024));
|
||||||
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024))
|
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024));
|
||||||
}
|
}
|
||||||
lastDiskStats = {
|
lastDiskStats = {
|
||||||
"read": totalRead,
|
"read": totalRead,
|
||||||
"write": totalWrite
|
"write": totalWrite
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.diskmounts) {
|
if (data.diskmounts) {
|
||||||
diskMounts = data.diskmounts || []
|
diskMounts = data.diskmounts || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.processes && Array.isArray(data.processes)) {
|
if (data.processes && Array.isArray(data.processes)) {
|
||||||
const newProcesses = []
|
const newProcesses = [];
|
||||||
processSampleCount++
|
processSampleCount++;
|
||||||
|
|
||||||
for (const proc of data.processes) {
|
for (const proc of data.processes) {
|
||||||
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0
|
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0;
|
||||||
|
|
||||||
newProcesses.push({
|
newProcesses.push({
|
||||||
"pid": proc.pid || 0,
|
"pid": proc.pid || 0,
|
||||||
"ppid": proc.ppid || 0,
|
"ppid": proc.ppid || 0,
|
||||||
"cpu": cpuUsage,
|
"cpu": cpuUsage,
|
||||||
"memoryPercent": proc.memoryPercent || proc.pssPercent || 0,
|
"memoryPercent": proc.memoryPercent || proc.pssPercent || 0,
|
||||||
"memoryKB": proc.memoryKB || proc.pssKB || 0,
|
"memoryKB": proc.memoryKB || proc.pssKB || 0,
|
||||||
"command": proc.command || "",
|
"command": proc.command || "",
|
||||||
"fullCommand": proc.fullCommand || "",
|
"fullCommand": proc.fullCommand || "",
|
||||||
"displayName": (proc.command && proc.command.length > 15) ? proc.command.substring(0, 15) + "..." : (proc.command || "")
|
"displayName": (proc.command && proc.command.length > 15) ? proc.command.substring(0, 15) + "..." : (proc.command || "")
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
allProcesses = newProcesses
|
allProcesses = newProcesses;
|
||||||
applySorting()
|
applySorting();
|
||||||
|
|
||||||
if (data.cursor) {
|
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)) {
|
if (gpuData && Array.isArray(gpuData)) {
|
||||||
// Check if this is temperature update data (has PCI IDs being monitored)
|
// Check if this is temperature update data (has PCI IDs being monitored)
|
||||||
if (gpuPciIds.length > 0 && availableGpus && availableGpus.length > 0) {
|
if (gpuPciIds.length > 0 && availableGpus && availableGpus.length > 0) {
|
||||||
// This is temperature data - merge with existing GPU metadata
|
// 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++) {
|
for (var i = 0; i < updatedGpus.length; i++) {
|
||||||
const existingGpu = updatedGpus[i]
|
const existingGpu = updatedGpus[i];
|
||||||
const tempGpu = gpuData.find(g => g.pciId === existingGpu.pciId)
|
const tempGpu = gpuData.find(g => g.pciId === existingGpu.pciId);
|
||||||
// Only update temperature if this GPU's PCI ID is being monitored
|
// Only update temperature if this GPU's PCI ID is being monitored
|
||||||
if (tempGpu && gpuPciIds.includes(existingGpu.pciId)) {
|
if (tempGpu && gpuPciIds.includes(existingGpu.pciId)) {
|
||||||
updatedGpus[i] = Object.assign({}, existingGpu, {
|
updatedGpus[i] = Object.assign({}, existingGpu, {
|
||||||
"temperature": tempGpu.temperature || 0
|
"temperature": tempGpu.temperature || 0
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
availableGpus = updatedGpus
|
availableGpus = updatedGpus;
|
||||||
} else {
|
} else {
|
||||||
// This is initial GPU metadata - set the full list
|
// This is initial GPU metadata - set the full list
|
||||||
const gpuList = []
|
const gpuList = [];
|
||||||
for (const gpu of gpuData) {
|
for (const gpu of gpuData) {
|
||||||
let displayName = gpu.displayName || gpu.name || "Unknown GPU"
|
let displayName = gpu.displayName || gpu.name || "Unknown GPU";
|
||||||
let fullName = gpu.fullName || gpu.name || "Unknown GPU"
|
let fullName = gpu.fullName || gpu.name || "Unknown GPU";
|
||||||
|
|
||||||
gpuList.push({
|
gpuList.push({
|
||||||
"driver": gpu.driver || "",
|
"driver": gpu.driver || "",
|
||||||
"vendor": gpu.vendor || "",
|
"vendor": gpu.vendor || "",
|
||||||
"displayName": displayName,
|
"displayName": displayName,
|
||||||
"fullName": fullName,
|
"fullName": fullName,
|
||||||
"pciId": gpu.pciId || "",
|
"pciId": gpu.pciId || "",
|
||||||
"temperature": gpu.temperature || 0
|
"temperature": gpu.temperature || 0
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
availableGpus = gpuList
|
availableGpus = gpuList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.system) {
|
if (data.system) {
|
||||||
const sys = data.system
|
const sys = data.system;
|
||||||
loadAverage = sys.loadavg || ""
|
loadAverage = sys.loadavg || "";
|
||||||
processCount = sys.processes || 0
|
processCount = sys.processes || 0;
|
||||||
threadCount = sys.threads || 0
|
threadCount = sys.threads || 0;
|
||||||
bootTime = sys.boottime || ""
|
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) {
|
if (hwData) {
|
||||||
hostname = hwData.hostname || ""
|
hostname = hwData.hostname || "";
|
||||||
kernelVersion = hwData.kernel || ""
|
kernelVersion = hwData.kernel || "";
|
||||||
distribution = hwData.distro || ""
|
distribution = hwData.distro || "";
|
||||||
architecture = hwData.arch || ""
|
architecture = hwData.arch || "";
|
||||||
motherboard = (hwData.bios && hwData.bios.motherboard) || ""
|
motherboard = (hwData.bios && hwData.bios.motherboard) || "";
|
||||||
biosVersion = (hwData.bios && hwData.bios.version) || ""
|
biosVersion = (hwData.bios && hwData.bios.version) || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
isUpdating = false
|
isUpdating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToHistory(array, value) {
|
function addToHistory(array, value) {
|
||||||
array.push(value)
|
array.push(value);
|
||||||
if (array.length > historySize) {
|
if (array.length > historySize) {
|
||||||
array.shift()
|
array.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProcessIcon(command) {
|
function getProcessIcon(command) {
|
||||||
const cmd = command.toLowerCase()
|
const cmd = command.toLowerCase();
|
||||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser") || cmd.includes("chromium")) {
|
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")) {
|
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) {
|
||||||
return "code"
|
return "code";
|
||||||
}
|
}
|
||||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) {
|
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) {
|
||||||
return "terminal"
|
return "terminal";
|
||||||
}
|
}
|
||||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify")) {
|
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")) {
|
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")) {
|
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) {
|
function formatCpuUsage(cpu) {
|
||||||
return (cpu || 0).toFixed(1) + "%"
|
return (cpu || 0).toFixed(1) + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMemoryUsage(memoryKB) {
|
function formatMemoryUsage(memoryKB) {
|
||||||
const mem = memoryKB || 0
|
const mem = memoryKB || 0;
|
||||||
if (mem < 1024) {
|
if (mem < 1024) {
|
||||||
return mem.toFixed(0) + " KB"
|
return mem.toFixed(0) + " KB";
|
||||||
} else if (mem < 1024 * 1024) {
|
} else if (mem < 1024 * 1024) {
|
||||||
return (mem / 1024).toFixed(1) + " MB"
|
return (mem / 1024).toFixed(1) + " MB";
|
||||||
} else {
|
} else {
|
||||||
return (mem / (1024 * 1024)).toFixed(1) + " GB"
|
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSystemMemory(memoryKB) {
|
function formatSystemMemory(memoryKB) {
|
||||||
const mem = memoryKB || 0
|
const mem = memoryKB || 0;
|
||||||
if (mem === 0) {
|
if (mem === 0) {
|
||||||
return "--"
|
return "--";
|
||||||
}
|
}
|
||||||
if (mem < 1024 * 1024) {
|
if (mem < 1024 * 1024) {
|
||||||
return (mem / 1024).toFixed(0) + " MB"
|
return (mem / 1024).toFixed(0) + " MB";
|
||||||
} else {
|
} else {
|
||||||
return (mem / (1024 * 1024)).toFixed(1) + " GB"
|
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function killProcess(pid) {
|
function killProcess(pid) {
|
||||||
if (pid > 0) {
|
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) {
|
function setSortBy(newSortBy) {
|
||||||
if (newSortBy !== currentSort) {
|
if (newSortBy !== currentSort) {
|
||||||
currentSort = newSortBy
|
currentSort = newSortBy;
|
||||||
applySorting()
|
applySorting();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySorting() {
|
function applySorting() {
|
||||||
if (!allProcesses || allProcesses.length === 0) {
|
if (!allProcesses || allProcesses.length === 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sorted = allProcesses.slice()
|
const sorted = allProcesses.slice();
|
||||||
sorted.sort((a, b) => {
|
sorted.sort((a, b) => {
|
||||||
let valueA, valueB
|
let valueA, valueB;
|
||||||
|
|
||||||
switch (currentSort) {
|
switch (currentSort) {
|
||||||
case "cpu":
|
case "cpu":
|
||||||
valueA = a.cpu || 0
|
valueA = a.cpu || 0;
|
||||||
valueB = b.cpu || 0
|
valueB = b.cpu || 0;
|
||||||
return valueB - valueA
|
return valueB - valueA;
|
||||||
case "memory":
|
case "memory":
|
||||||
valueA = a.memoryKB || 0
|
valueA = a.memoryKB || 0;
|
||||||
valueB = b.memoryKB || 0
|
valueB = b.memoryKB || 0;
|
||||||
return valueB - valueA
|
return valueB - valueA;
|
||||||
case "name":
|
case "name":
|
||||||
valueA = (a.command || "").toLowerCase()
|
valueA = (a.command || "").toLowerCase();
|
||||||
valueB = (b.command || "").toLowerCase()
|
valueB = (b.command || "").toLowerCase();
|
||||||
return valueA.localeCompare(valueB)
|
return valueA.localeCompare(valueB);
|
||||||
case "pid":
|
case "pid":
|
||||||
valueA = a.pid || 0
|
valueA = a.pid || 0;
|
||||||
valueB = b.pid || 0
|
valueB = b.pid || 0;
|
||||||
return valueA - valueB
|
return valueA - valueB;
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
processes = sorted.slice(0, processLimit)
|
processes = sorted.slice(0, processLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -585,26 +627,26 @@ Singleton {
|
|||||||
id: dgopProcess
|
id: dgopProcess
|
||||||
command: root.buildDgopCommand()
|
command: root.buildDgopCommand()
|
||||||
running: false
|
running: false
|
||||||
onCommandChanged: {
|
onCommandChanged:
|
||||||
|
|
||||||
//console.log("DgopService command:", JSON.stringify(command))
|
//console.log("DgopService command:", JSON.stringify(command))
|
||||||
}
|
{}
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Dgop process failed with exit code:", exitCode)
|
console.warn("Dgop process failed with exit code:", exitCode);
|
||||||
isUpdating = false
|
isUpdating = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (text.trim()) {
|
if (text.trim()) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(text.trim())
|
const data = JSON.parse(text.trim());
|
||||||
parseData(data)
|
parseData(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Failed to parse dgop JSON:", e)
|
console.warn("Failed to parse dgop JSON:", e);
|
||||||
console.warn("Raw text was:", text.substring(0, 200))
|
console.warn("Raw text was:", text.substring(0, 200));
|
||||||
isUpdating = false
|
isUpdating = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,17 +659,17 @@ Singleton {
|
|||||||
running: false
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
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 {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (text.trim()) {
|
if (text.trim()) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(text.trim())
|
const data = JSON.parse(text.trim());
|
||||||
parseData(data)
|
parseData(data);
|
||||||
} catch (e) {
|
} 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
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
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 {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (text.trim()) {
|
if (text.trim()) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(text.trim())
|
const data = JSON.parse(text.trim());
|
||||||
parseData(data)
|
parseData(data);
|
||||||
} catch (e) {
|
} 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"]
|
command: ["which", "dgop"]
|
||||||
running: false
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
dgopAvailable = (exitCode === 0)
|
dgopAvailable = (exitCode === 0);
|
||||||
if (dgopAvailable) {
|
if (dgopAvailable) {
|
||||||
initializeGpuMetadata()
|
initializeGpuMetadata();
|
||||||
initializeSystemMetadata()
|
initializeSystemMetadata();
|
||||||
if (SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.length > 0) {
|
if (SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.length > 0) {
|
||||||
for (const pciId of SessionData.enabledGpuPciIds) {
|
for (const pciId of SessionData.enabledGpuPciIds) {
|
||||||
addGpuPciId(pciId)
|
addGpuPciId(pciId);
|
||||||
}
|
}
|
||||||
if (refCount > 0 && enabledModules.length > 0) {
|
if (refCount > 0 && enabledModules.length > 0) {
|
||||||
updateAllStats()
|
updateAllStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to read /etc/os-release")
|
console.warn("Failed to read /etc/os-release");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (text.trim()) {
|
if (text.trim()) {
|
||||||
try {
|
try {
|
||||||
const lines = text.trim().split('\n')
|
const lines = text.trim().split('\n');
|
||||||
let prettyName = ""
|
let prettyName = "";
|
||||||
let name = ""
|
let name = "";
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmedLine = line.trim()
|
const trimmedLine = line.trim();
|
||||||
if (trimmedLine.startsWith('PRETTY_NAME=')) {
|
if (trimmedLine.startsWith('PRETTY_NAME=')) {
|
||||||
prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, '')
|
prettyName = trimmedLine.substring(12).replace(/^["']|["']$/g, '');
|
||||||
} else if (trimmedLine.startsWith('NAME=')) {
|
} else if (trimmedLine.startsWith('NAME=')) {
|
||||||
name = trimmedLine.substring(5).replace(/^["']|["']$/g, '')
|
name = trimmedLine.substring(5).replace(/^["']|["']$/g, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer PRETTY_NAME, fallback to NAME
|
// Prefer PRETTY_NAME, fallback to NAME
|
||||||
const distroName = prettyName || name || "Linux"
|
const distroName = prettyName || name || "Linux";
|
||||||
distribution = distroName
|
distribution = distroName;
|
||||||
console.info("Detected distribution:", distroName)
|
console.info("Detected distribution:", distroName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Failed to parse /etc/os-release:", e)
|
console.warn("Failed to parse /etc/os-release:", e);
|
||||||
distribution = "Linux"
|
distribution = "Linux";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -720,7 +762,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
dgopCheckProcess.running = true
|
dgopCheckProcess.running = true;
|
||||||
osReleaseProcess.running = true
|
osReleaseProcess.running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -23,9 +22,9 @@ Singleton {
|
|||||||
|
|
||||||
readonly property bool nativeInhibitorAvailable: {
|
readonly property bool nativeInhibitorAvailable: {
|
||||||
try {
|
try {
|
||||||
return typeof IdleInhibitor !== "undefined"
|
return typeof IdleInhibitor !== "undefined";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +41,10 @@ Singleton {
|
|||||||
property string seat: ""
|
property string seat: ""
|
||||||
property string display: ""
|
property string display: ""
|
||||||
|
|
||||||
signal sessionLocked()
|
signal sessionLocked
|
||||||
signal sessionUnlocked()
|
signal sessionUnlocked
|
||||||
signal prepareForSleep()
|
signal sessionResumed
|
||||||
signal loginctlStateChanged()
|
signal loginctlStateChanged
|
||||||
|
|
||||||
property bool stateInitialized: false
|
property bool stateInitialized: false
|
||||||
|
|
||||||
@@ -57,30 +56,29 @@ Singleton {
|
|||||||
running: true
|
running: true
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
detectElogindProcess.running = true
|
detectElogindProcess.running = true;
|
||||||
detectHibernateProcess.running = true
|
detectHibernateProcess.running = true;
|
||||||
detectPrimeRunProcess.running = true
|
detectPrimeRunProcess.running = true;
|
||||||
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable)
|
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable);
|
||||||
if (!SettingsData.loginctlLockIntegration) {
|
if (!SettingsData.loginctlLockIntegration) {
|
||||||
console.log("SessionService: loginctl lock integration disabled by user")
|
console.log("SessionService: loginctl lock integration disabled by user");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (socketPath && socketPath.length > 0) {
|
if (socketPath && socketPath.length > 0) {
|
||||||
checkDMSCapabilities()
|
checkDMSCapabilities();
|
||||||
} else {
|
} else {
|
||||||
console.log("SessionService: DMS_SOCKET not set")
|
console.log("SessionService: DMS_SOCKET not set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: detectUwsmProcess
|
id: detectUwsmProcess
|
||||||
running: false
|
running: false
|
||||||
command: ["which", "uwsm"]
|
command: ["which", "uwsm"]
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
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)$'"]
|
command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"]
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
console.log("SessionService: Elogind detection exited with code", exitCode)
|
console.log("SessionService: Elogind detection exited with code", exitCode);
|
||||||
isElogind = (exitCode === 0)
|
isElogind = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +99,7 @@ Singleton {
|
|||||||
command: ["grep", "-q", "disk", "/sys/power/state"]
|
command: ["grep", "-q", "disk", "/sys/power/state"]
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
hibernateSupported = (exitCode === 0)
|
hibernateSupported = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +109,7 @@ Singleton {
|
|||||||
command: ["which", "prime-run"]
|
command: ["which", "prime-run"]
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
hasPrimeRun = (exitCode === 0)
|
hasPrimeRun = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,164 +122,167 @@ Singleton {
|
|||||||
splitMarker: "\n"
|
splitMarker: "\n"
|
||||||
onRead: data => {
|
onRead: data => {
|
||||||
if (data.trim().toLowerCase().includes("not running")) {
|
if (data.trim().toLowerCase().includes("not running")) {
|
||||||
_logout()
|
_logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
_logout()
|
_logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeShellArg(arg) {
|
function escapeShellArg(arg) {
|
||||||
return "'" + arg.replace(/'/g, "'\\''") + "'"
|
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
function needsShellExecution(prefix) {
|
function needsShellExecution(prefix) {
|
||||||
if (!prefix || prefix.length === 0) return false
|
if (!prefix || prefix.length === 0)
|
||||||
return /[;&|<>()$`\\"']/.test(prefix)
|
return false;
|
||||||
|
return /[;&|<>()$`\\"']/.test(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchDesktopEntry(desktopEntry, usePrimeRun) {
|
function launchDesktopEntry(desktopEntry, usePrimeRun) {
|
||||||
let cmd = desktopEntry.command
|
let cmd = desktopEntry.command;
|
||||||
if (usePrimeRun && hasPrimeRun) {
|
if (usePrimeRun && hasPrimeRun) {
|
||||||
cmd = ["prime-run"].concat(cmd)
|
cmd = ["prime-run"].concat(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPrefix = SettingsData.launchPrefix?.trim() || ""
|
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""
|
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix
|
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||||
|
|
||||||
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
||||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ")
|
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||||
const shellCmd = `${prefix} ${escapedCmd}`
|
const shellCmd = `${prefix} ${escapedCmd}`;
|
||||||
|
|
||||||
Quickshell.execDetached({
|
Quickshell.execDetached({
|
||||||
command: ["sh", "-c", shellCmd],
|
command: ["sh", "-c", shellCmd],
|
||||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
if (prefix.length > 0) {
|
if (prefix.length > 0) {
|
||||||
const launchPrefix = prefix.split(" ")
|
const launchPrefix = prefix.split(" ");
|
||||||
cmd = launchPrefix.concat(cmd)
|
cmd = launchPrefix.concat(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quickshell.execDetached({
|
Quickshell.execDetached({
|
||||||
command: cmd,
|
command: cmd,
|
||||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchDesktopAction(desktopEntry, action, usePrimeRun) {
|
function launchDesktopAction(desktopEntry, action, usePrimeRun) {
|
||||||
let cmd = action.command
|
let cmd = action.command;
|
||||||
if (usePrimeRun && hasPrimeRun) {
|
if (usePrimeRun && hasPrimeRun) {
|
||||||
cmd = ["prime-run"].concat(cmd)
|
cmd = ["prime-run"].concat(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPrefix = SettingsData.launchPrefix?.trim() || ""
|
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || ""
|
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix
|
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||||
|
|
||||||
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
||||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ")
|
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||||
const shellCmd = `${prefix} ${escapedCmd}`
|
const shellCmd = `${prefix} ${escapedCmd}`;
|
||||||
|
|
||||||
Quickshell.execDetached({
|
Quickshell.execDetached({
|
||||||
command: ["sh", "-c", shellCmd],
|
command: ["sh", "-c", shellCmd],
|
||||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
if (prefix.length > 0) {
|
if (prefix.length > 0) {
|
||||||
const launchPrefix = prefix.split(" ")
|
const launchPrefix = prefix.split(" ");
|
||||||
cmd = launchPrefix.concat(cmd)
|
cmd = launchPrefix.concat(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quickshell.execDetached({
|
Quickshell.execDetached({
|
||||||
command: cmd,
|
command: cmd,
|
||||||
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"),
|
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Session management
|
// * Session management
|
||||||
function logout() {
|
function logout() {
|
||||||
if (hasUwsm) {
|
if (hasUwsm) {
|
||||||
uwsmLogout.running = true
|
uwsmLogout.running = true;
|
||||||
}
|
}
|
||||||
_logout()
|
_logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _logout() {
|
function _logout() {
|
||||||
if (SettingsData.customPowerActionLogout.length === 0) {
|
if (SettingsData.customPowerActionLogout.length === 0) {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
NiriService.quit()
|
NiriService.quit();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CompositorService.isDwl) {
|
if (CompositorService.isDwl) {
|
||||||
DwlService.quit()
|
DwlService.quit();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CompositorService.isSway) {
|
if (CompositorService.isSway) {
|
||||||
try { I3.dispatch("exit") } catch(_){}
|
try {
|
||||||
return
|
I3.dispatch("exit");
|
||||||
|
} catch (_) {}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hyprland.dispatch("exit")
|
Hyprland.dispatch("exit");
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function suspend() {
|
function suspend() {
|
||||||
if (SettingsData.customPowerActionSuspend.length === 0) {
|
if (SettingsData.customPowerActionSuspend.length === 0) {
|
||||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"])
|
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"]);
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hibernate() {
|
function hibernate() {
|
||||||
if (SettingsData.customPowerActionHibernate.length === 0) {
|
if (SettingsData.customPowerActionHibernate.length === 0) {
|
||||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"])
|
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"]);
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function suspendThenHibernate() {
|
function suspendThenHibernate() {
|
||||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"])
|
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function suspendWithBehavior(behavior) {
|
function suspendWithBehavior(behavior) {
|
||||||
if (behavior === SettingsData.SuspendBehavior.Hibernate) {
|
if (behavior === SettingsData.SuspendBehavior.Hibernate) {
|
||||||
hibernate()
|
hibernate();
|
||||||
} else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) {
|
} else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) {
|
||||||
suspendThenHibernate()
|
suspendThenHibernate();
|
||||||
} else {
|
} else {
|
||||||
suspend()
|
suspend();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reboot() {
|
function reboot() {
|
||||||
if (SettingsData.customPowerActionReboot.length === 0) {
|
if (SettingsData.customPowerActionReboot.length === 0) {
|
||||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"])
|
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"]);
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function poweroff() {
|
function poweroff() {
|
||||||
if (SettingsData.customPowerActionPowerOff.length === 0) {
|
if (SettingsData.customPowerActionPowerOff.length === 0) {
|
||||||
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"])
|
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"]);
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,42 +291,42 @@ Singleton {
|
|||||||
|
|
||||||
function enableIdleInhibit() {
|
function enableIdleInhibit() {
|
||||||
if (idleInhibited) {
|
if (idleInhibited) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")")
|
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
|
||||||
idleInhibited = true
|
idleInhibited = true;
|
||||||
inhibitorChanged()
|
inhibitorChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableIdleInhibit() {
|
function disableIdleInhibit() {
|
||||||
if (!idleInhibited) {
|
if (!idleInhibited) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")")
|
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
|
||||||
idleInhibited = false
|
idleInhibited = false;
|
||||||
inhibitorChanged()
|
inhibitorChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleIdleInhibit() {
|
function toggleIdleInhibit() {
|
||||||
if (idleInhibited) {
|
if (idleInhibited) {
|
||||||
disableIdleInhibit()
|
disableIdleInhibit();
|
||||||
} else {
|
} else {
|
||||||
enableIdleInhibit()
|
enableIdleInhibit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInhibitReason(reason) {
|
function setInhibitReason(reason) {
|
||||||
inhibitReason = reason
|
inhibitReason = reason;
|
||||||
|
|
||||||
if (idleInhibited && !nativeInhibitorAvailable) {
|
if (idleInhibited && !nativeInhibitorAvailable) {
|
||||||
const wasActive = idleInhibited
|
const wasActive = idleInhibited;
|
||||||
idleInhibited = false
|
idleInhibited = false;
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (wasActive) {
|
if (wasActive) {
|
||||||
idleInhibited = true
|
idleInhibited = true;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,24 +335,24 @@ Singleton {
|
|||||||
|
|
||||||
command: {
|
command: {
|
||||||
if (!idleInhibited || nativeInhibitorAvailable) {
|
if (!idleInhibited || nativeInhibitorAvailable) {
|
||||||
return ["true"]
|
return ["true"];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("SessionService: Starting systemd/elogind inhibit process")
|
console.log("SessionService: Starting systemd/elogind inhibit process");
|
||||||
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"]
|
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"];
|
||||||
}
|
}
|
||||||
|
|
||||||
running: idleInhibited && !nativeInhibitorAvailable
|
running: idleInhibited && !nativeInhibitorAvailable
|
||||||
|
|
||||||
onRunningChanged: {
|
onRunningChanged: {
|
||||||
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")")
|
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
|
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
|
||||||
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode)
|
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode);
|
||||||
idleInhibited = false
|
idleInhibited = false;
|
||||||
ToastService.showWarning("Idle inhibitor failed")
|
ToastService.showWarning("Idle inhibitor failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,12 +362,12 @@ Singleton {
|
|||||||
|
|
||||||
function onConnectionStateChanged() {
|
function onConnectionStateChanged() {
|
||||||
if (DMSService.isConnected) {
|
if (DMSService.isConnected) {
|
||||||
checkDMSCapabilities()
|
checkDMSCapabilities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCapabilitiesReceived() {
|
function onCapabilitiesReceived() {
|
||||||
syncSleepInhibitor()
|
syncSleepInhibitor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +376,7 @@ Singleton {
|
|||||||
enabled: DMSService.isConnected
|
enabled: DMSService.isConnected
|
||||||
|
|
||||||
function onCapabilitiesChanged() {
|
function onCapabilitiesChanged() {
|
||||||
checkDMSCapabilities()
|
checkDMSCapabilities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,22 +387,22 @@ Singleton {
|
|||||||
if (SettingsData.loginctlLockIntegration) {
|
if (SettingsData.loginctlLockIntegration) {
|
||||||
if (socketPath && socketPath.length > 0 && loginctlAvailable) {
|
if (socketPath && socketPath.length > 0 && loginctlAvailable) {
|
||||||
if (!stateInitialized) {
|
if (!stateInitialized) {
|
||||||
stateInitialized = true
|
stateInitialized = true;
|
||||||
getLoginctlState()
|
getLoginctlState();
|
||||||
syncLockBeforeSuspend()
|
syncLockBeforeSuspend();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stateInitialized = false
|
stateInitialized = false;
|
||||||
}
|
}
|
||||||
syncSleepInhibitor()
|
syncSleepInhibitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLockBeforeSuspendChanged() {
|
function onLockBeforeSuspendChanged() {
|
||||||
if (SettingsData.loginctlLockIntegration) {
|
if (SettingsData.loginctlLockIntegration) {
|
||||||
syncLockBeforeSuspend()
|
syncLockBeforeSuspend();
|
||||||
}
|
}
|
||||||
syncSleepInhibitor()
|
syncSleepInhibitor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,109 +411,114 @@ Singleton {
|
|||||||
enabled: SettingsData.loginctlLockIntegration
|
enabled: SettingsData.loginctlLockIntegration
|
||||||
|
|
||||||
function onLoginctlStateUpdate(data) {
|
function onLoginctlStateUpdate(data) {
|
||||||
updateLoginctlState(data)
|
updateLoginctlState(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoginctlEvent(event) {
|
function onLoginctlEvent(event) {
|
||||||
handleLoginctlEvent(event)
|
handleLoginctlEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDMSCapabilities() {
|
function checkDMSCapabilities() {
|
||||||
if (!DMSService.isConnected) {
|
if (!DMSService.isConnected) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DMSService.capabilities.length === 0) {
|
if (DMSService.capabilities.length === 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DMSService.capabilities.includes("loginctl")) {
|
if (DMSService.capabilities.includes("loginctl")) {
|
||||||
loginctlAvailable = true
|
loginctlAvailable = true;
|
||||||
if (SettingsData.loginctlLockIntegration && !stateInitialized) {
|
if (SettingsData.loginctlLockIntegration && !stateInitialized) {
|
||||||
stateInitialized = true
|
stateInitialized = true;
|
||||||
getLoginctlState()
|
getLoginctlState();
|
||||||
syncLockBeforeSuspend()
|
syncLockBeforeSuspend();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loginctlAvailable = false
|
loginctlAvailable = false;
|
||||||
console.log("SessionService: loginctl capability not available in DMS")
|
console.log("SessionService: loginctl capability not available in DMS");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLoginctlState() {
|
function getLoginctlState() {
|
||||||
if (!loginctlAvailable) return
|
if (!loginctlAvailable)
|
||||||
|
return;
|
||||||
DMSService.sendRequest("loginctl.getState", null, response => {
|
DMSService.sendRequest("loginctl.getState", null, response => {
|
||||||
if (response.result) {
|
if (response.result) {
|
||||||
updateLoginctlState(response.result)
|
updateLoginctlState(response.result);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncLockBeforeSuspend() {
|
function syncLockBeforeSuspend() {
|
||||||
if (!loginctlAvailable) return
|
if (!loginctlAvailable)
|
||||||
|
return;
|
||||||
DMSService.sendRequest("loginctl.setLockBeforeSuspend", {
|
DMSService.sendRequest("loginctl.setLockBeforeSuspend", {
|
||||||
enabled: SettingsData.lockBeforeSuspend
|
enabled: SettingsData.lockBeforeSuspend
|
||||||
}, response => {
|
}, response => {
|
||||||
if (response.error) {
|
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 {
|
} else {
|
||||||
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend)
|
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncSleepInhibitor() {
|
function syncSleepInhibitor() {
|
||||||
if (!loginctlAvailable) return
|
if (!loginctlAvailable)
|
||||||
|
return;
|
||||||
if (!DMSService.apiVersion || DMSService.apiVersion < 4) return
|
if (!DMSService.apiVersion || DMSService.apiVersion < 4)
|
||||||
|
return;
|
||||||
DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", {
|
DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", {
|
||||||
enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend
|
enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend
|
||||||
}, response => {
|
}, response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error)
|
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error);
|
||||||
} else {
|
} else {
|
||||||
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration)
|
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLoginctlState(state) {
|
function updateLoginctlState(state) {
|
||||||
const wasLocked = locked
|
const wasLocked = locked;
|
||||||
|
const wasSleeping = preparingForSleep;
|
||||||
|
|
||||||
sessionId = state.sessionId || ""
|
sessionId = state.sessionId || "";
|
||||||
sessionPath = state.sessionPath || ""
|
sessionPath = state.sessionPath || "";
|
||||||
locked = state.locked || false
|
locked = state.locked || false;
|
||||||
active = state.active || false
|
active = state.active || false;
|
||||||
idleHint = state.idleHint || false
|
idleHint = state.idleHint || false;
|
||||||
lockedHint = state.lockedHint || false
|
lockedHint = state.lockedHint || false;
|
||||||
sessionType = state.sessionType || ""
|
preparingForSleep = state.preparingForSleep || false;
|
||||||
userName = state.userName || ""
|
sessionType = state.sessionType || "";
|
||||||
seat = state.seat || ""
|
userName = state.userName || "";
|
||||||
display = state.display || ""
|
seat = state.seat || "";
|
||||||
|
display = state.display || "";
|
||||||
|
|
||||||
if (locked && !wasLocked) {
|
if (locked && !wasLocked) {
|
||||||
sessionLocked()
|
sessionLocked();
|
||||||
} else if (!locked && wasLocked) {
|
} else if (!locked && wasLocked) {
|
||||||
sessionUnlocked()
|
sessionUnlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
loginctlStateChanged()
|
if (wasSleeping && !preparingForSleep) {
|
||||||
|
sessionResumed();
|
||||||
|
}
|
||||||
|
|
||||||
|
loginctlStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLoginctlEvent(event) {
|
function handleLoginctlEvent(event) {
|
||||||
if (event.event === "Lock") {
|
if (event.event === "Lock") {
|
||||||
locked = true
|
locked = true;
|
||||||
lockedHint = true
|
lockedHint = true;
|
||||||
sessionLocked()
|
sessionLocked();
|
||||||
} else if (event.event === "Unlock") {
|
} else if (event.event === "Unlock") {
|
||||||
locked = false
|
locked = false;
|
||||||
lockedHint = false
|
lockedHint = false;
|
||||||
sessionUnlocked()
|
sessionUnlocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -12,77 +11,25 @@ Singleton {
|
|||||||
property string username: ""
|
property string username: ""
|
||||||
property string fullName: ""
|
property string fullName: ""
|
||||||
property string profilePicture: ""
|
property string profilePicture: ""
|
||||||
property string uptime: ""
|
|
||||||
property string shortUptime: ""
|
|
||||||
property string hostname: ""
|
property string hostname: ""
|
||||||
property bool profileAvailable: false
|
property bool profileAvailable: false
|
||||||
|
|
||||||
function getUserInfo() {
|
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) {
|
if (exitCode !== 0) {
|
||||||
root.username = "User"
|
root.username = "User";
|
||||||
root.fullName = "User"
|
root.fullName = "User";
|
||||||
root.hostname = "System"
|
root.hostname = "System";
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const parts = output.trim().split("|")
|
const parts = output.trim().split("|");
|
||||||
if (parts.length >= 3) {
|
if (parts.length >= 3) {
|
||||||
root.username = parts[0] || ""
|
root.username = parts[0] || "";
|
||||||
root.fullName = parts[1] || parts[0] || ""
|
root.fullName = parts[1] || parts[0] || "";
|
||||||
root.hostname = parts[2] || ""
|
root.hostname = parts[2] || "";
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUptime() {
|
Component.onCompleted: getUserInfo()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,39 +27,41 @@ PanelWindow {
|
|||||||
signal osdHidden
|
signal osdHidden
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
OSDManager.showOSD(root)
|
if (SessionData.suppressOSD)
|
||||||
closeTimer.stop()
|
return;
|
||||||
shouldBeVisible = true
|
OSDManager.showOSD(root);
|
||||||
visible = true
|
closeTimer.stop();
|
||||||
hideTimer.restart()
|
shouldBeVisible = true;
|
||||||
osdShown()
|
visible = true;
|
||||||
|
hideTimer.restart();
|
||||||
|
osdShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
shouldBeVisible = false
|
shouldBeVisible = false;
|
||||||
closeTimer.restart()
|
closeTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetHideTimer() {
|
function resetHideTimer() {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
hideTimer.restart()
|
hideTimer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHoverState() {
|
function updateHoverState() {
|
||||||
let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered
|
let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered;
|
||||||
if (enableMouseInteraction) {
|
if (enableMouseInteraction) {
|
||||||
if (isHovered) {
|
if (isHovered) {
|
||||||
hideTimer.stop()
|
hideTimer.stop();
|
||||||
} else if (shouldBeVisible) {
|
} else if (shouldBeVisible) {
|
||||||
hideTimer.restart()
|
hideTimer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setChildHovered(hovered) {
|
function setChildHovered(hovered) {
|
||||||
osdContainer.childHovered = hovered
|
osdContainer.childHovered = hovered;
|
||||||
updateHoverState()
|
updateHoverState();
|
||||||
}
|
}
|
||||||
|
|
||||||
screen: modelData
|
screen: modelData
|
||||||
@@ -78,88 +80,92 @@ PanelWindow {
|
|||||||
readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter
|
readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter
|
||||||
|
|
||||||
readonly property real barThickness: {
|
readonly property real barThickness: {
|
||||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||||
if (!defaultBar || !(defaultBar.visible ?? true)) return 0
|
if (!defaultBar || !(defaultBar.visible ?? true))
|
||||||
const innerPadding = defaultBar.innerPadding ?? 4
|
return 0;
|
||||||
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6)
|
const innerPadding = defaultBar.innerPadding ?? 4;
|
||||||
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding))
|
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
|
||||||
|
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real barOffset: {
|
readonly property real barOffset: {
|
||||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||||
if (!defaultBar || !(defaultBar.visible ?? true)) return 0
|
if (!defaultBar || !(defaultBar.visible ?? true))
|
||||||
const spacing = defaultBar.spacing ?? 4
|
return 0;
|
||||||
const bottomGap = defaultBar.bottomGap ?? 0
|
const spacing = defaultBar.spacing ?? 4;
|
||||||
return barThickness + spacing + bottomGap
|
const bottomGap = defaultBar.bottomGap ?? 0;
|
||||||
|
return barThickness + spacing + bottomGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real dockThickness: {
|
readonly property real dockThickness: {
|
||||||
if (!SettingsData.showDock) return 0
|
if (!SettingsData.showDock)
|
||||||
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10
|
return 0;
|
||||||
|
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real dockOffset: {
|
readonly property real dockOffset: {
|
||||||
if (!SettingsData.showDock || SettingsData.dockAutoHide) return 0
|
if (!SettingsData.showDock || SettingsData.dockAutoHide)
|
||||||
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin
|
return 0;
|
||||||
|
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real alignedX: {
|
readonly property real alignedX: {
|
||||||
const margin = Theme.spacingM
|
const margin = Theme.spacingM;
|
||||||
const centerX = (screenWidth - alignedWidth) / 2
|
const centerX = (screenWidth - alignedWidth) / 2;
|
||||||
|
|
||||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top
|
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
||||||
|
|
||||||
switch (SettingsData.osdPosition) {
|
switch (SettingsData.osdPosition) {
|
||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0
|
const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
|
||||||
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
|
||||||
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr)
|
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr);
|
||||||
case SettingsData.Position.Top:
|
case SettingsData.Position.Top:
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0
|
const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
|
||||||
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
|
||||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr)
|
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr);
|
||||||
case SettingsData.Position.LeftCenter:
|
case SettingsData.Position.LeftCenter:
|
||||||
const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0
|
const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
|
||||||
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
|
||||||
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr)
|
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr);
|
||||||
case SettingsData.Position.RightCenter:
|
case SettingsData.Position.RightCenter:
|
||||||
const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0
|
const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
|
||||||
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
|
||||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr)
|
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr);
|
||||||
case SettingsData.Position.TopCenter:
|
case SettingsData.Position.TopCenter:
|
||||||
case SettingsData.Position.BottomCenter:
|
case SettingsData.Position.BottomCenter:
|
||||||
default:
|
default:
|
||||||
return Theme.snap(centerX, dpr)
|
return Theme.snap(centerX, dpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real alignedY: {
|
readonly property real alignedY: {
|
||||||
const margin = Theme.spacingM
|
const margin = Theme.spacingM;
|
||||||
const centerY = (screenHeight - alignedHeight) / 2
|
const centerY = (screenHeight - alignedHeight) / 2;
|
||||||
|
|
||||||
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default")
|
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
|
||||||
const barPos = defaultBar?.position ?? SettingsData.Position.Top
|
const barPos = defaultBar?.position ?? SettingsData.Position.Top;
|
||||||
|
|
||||||
switch (SettingsData.osdPosition) {
|
switch (SettingsData.osdPosition) {
|
||||||
case SettingsData.Position.Top:
|
case SettingsData.Position.Top:
|
||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
case SettingsData.Position.TopCenter:
|
case SettingsData.Position.TopCenter:
|
||||||
const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0
|
const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0;
|
||||||
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0
|
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0;
|
||||||
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr)
|
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr);
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
case SettingsData.Position.BottomCenter:
|
case SettingsData.Position.BottomCenter:
|
||||||
const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0
|
const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0;
|
||||||
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0
|
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0;
|
||||||
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr)
|
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr);
|
||||||
case SettingsData.Position.LeftCenter:
|
case SettingsData.Position.LeftCenter:
|
||||||
case SettingsData.Position.RightCenter:
|
case SettingsData.Position.RightCenter:
|
||||||
default:
|
default:
|
||||||
return Theme.snap(centerY, dpr)
|
return Theme.snap(centerY, dpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,9 +183,9 @@ PanelWindow {
|
|||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!enableMouseInteraction || !mouseArea.containsMouse) {
|
if (!enableMouseInteraction || !mouseArea.containsMouse) {
|
||||||
hide()
|
hide();
|
||||||
} else {
|
} else {
|
||||||
hideTimer.restart()
|
hideTimer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,8 +195,8 @@ PanelWindow {
|
|||||||
interval: animationDuration + 50
|
interval: animationDuration + 50
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
visible = false
|
visible = false;
|
||||||
osdHidden()
|
osdHidden();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,8 +245,8 @@ PanelWindow {
|
|||||||
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax))
|
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))
|
shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
|
||||||
shadowColor: {
|
shadowColor: {
|
||||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest
|
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||||
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha)
|
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user