ro-Control, kullanıcıların NVIDIA GPU'ları tespit etmesine,
sürücüleri DNF ile kurup güncellemesine ve sistem metriklerini Qt tabanlı
bir masaüstü arayüzünden izlemesine yardımcı olur.
@@ -38,12 +36,12 @@
- ro-control.desktop
+ io.github.projectroasd.rocontrol.desktopro-control
-
+ Project Ro ASD
diff --git a/packaging/rpm/ro-control.spec b/packaging/rpm/ro-control.spec
index 2a552c1..d1d3336 100644
--- a/packaging/rpm/ro-control.spec
+++ b/packaging/rpm/ro-control.spec
@@ -1,8 +1,6 @@
-%global _rpmfilename %{NAME}-%{VERSION}.%{ARCH}.rpm
-
Name: ro-control
Version: 0.1.0
-Release: 1
+Release: 2%{?dist}
Summary: Smart NVIDIA driver manager and system monitor
License: GPL-3.0-or-later
@@ -26,6 +24,8 @@ Requires: qt6-qtdeclarative
Requires: qt6-qtwayland
Requires: kf6-qqc2-desktop-style
Requires: polkit
+Requires: dnf
+Requires: /usr/bin/pkexec
%description
ro-Control is a Qt6/KDE Plasma desktop application that helps users
@@ -51,9 +51,9 @@ tar -xzf %{SOURCE0} --strip-components=1
%license LICENSE
%doc README.md README.tr.md CHANGELOG.md
%{_bindir}/ro-control
-%{_datadir}/applications/ro-control.desktop
+%{_datadir}/applications/io.github.projectroasd.rocontrol.desktop
%{_datadir}/man/man1/ro-control.1*
-%{_datadir}/metainfo/ro-control.metainfo.xml
+%{_datadir}/metainfo/io.github.projectroasd.rocontrol.metainfo.xml
%{_datadir}/icons/hicolor/256x256/apps/ro-control.png
%{_datadir}/icons/hicolor/scalable/apps/ro-control.svg
%{_datadir}/bash-completion/completions/ro-control
@@ -63,5 +63,9 @@ tar -xzf %{SOURCE0} --strip-components=1
%{_datadir}/polkit-1/actions/io.github.ProjectRoASD.rocontrol.policy
%changelog
+* Mon Mar 16 2026 ro-Control Maintainers - 0.1.0-2
+- Fix Fedora runtime dependencies for DNF and pkexec
+- Restore standard RPM artifact naming to avoid output collisions
+
* Fri Mar 06 2026 ro-Control Maintainers - 0.1.0-1
- Initial RPM packaging spec
diff --git a/tests/test_metadata.cpp b/tests/test_metadata.cpp
index 9df85ba..fc0ff70 100644
--- a/tests/test_metadata.cpp
+++ b/tests/test_metadata.cpp
@@ -21,7 +21,8 @@ class TestMetadata : public QObject {
private slots:
void testDesktopEntryContainsCoreFields() {
- const QString desktop = readFile(QStringLiteral("data/icons/ro-control.desktop"));
+ const QString desktop = readFile(
+ QStringLiteral("data/icons/io.github.projectroasd.rocontrol.desktop"));
QVERIFY(!desktop.isEmpty());
QVERIFY(desktop.contains(QStringLiteral("[Desktop Entry]")));
QVERIFY(desktop.contains(QStringLiteral("Exec=ro-control")));
@@ -31,10 +32,13 @@ private slots:
}
void testAppStreamContainsExpectedIdsAndUrls() {
- const QString metainfo = readFile(QStringLiteral("data/icons/ro-control.metainfo.xml"));
+ const QString metainfo = readFile(QStringLiteral(
+ "data/icons/io.github.projectroasd.rocontrol.metainfo.xml"));
QVERIFY(!metainfo.isEmpty());
- QVERIFY(metainfo.contains(QStringLiteral("ro-control.desktop")));
- QVERIFY(metainfo.contains(QStringLiteral("ro-control.desktop")));
+ QVERIFY(metainfo.contains(
+ QStringLiteral("io.github.projectroasd.rocontrol.desktop")));
+ QVERIFY(metainfo.contains(QStringLiteral(
+ "io.github.projectroasd.rocontrol.desktop")));
QVERIFY(metainfo.contains(QStringLiteral("ro-control")));
QVERIFY(metainfo.contains(QStringLiteral("https://github.com/Project-Ro-ASD/ro-Control")));
QVERIFY(metainfo.contains(QStringLiteral("https://github.com/Project-Ro-ASD/ro-Control/issues")));
@@ -49,14 +53,17 @@ private slots:
}
void testDesktopAndAppStreamIdsStayAligned() {
- const QString desktop = readFile(QStringLiteral("data/icons/ro-control.desktop"));
- const QString metainfo = readFile(QStringLiteral("data/icons/ro-control.metainfo.xml"));
+ const QString desktop = readFile(
+ QStringLiteral("data/icons/io.github.projectroasd.rocontrol.desktop"));
+ const QString metainfo = readFile(QStringLiteral(
+ "data/icons/io.github.projectroasd.rocontrol.metainfo.xml"));
QVERIFY(!desktop.isEmpty());
QVERIFY(!metainfo.isEmpty());
QVERIFY(desktop.contains(QStringLiteral("Exec=ro-control")));
- QVERIFY(metainfo.contains(QStringLiteral("ro-control.desktop")));
+ QVERIFY(metainfo.contains(
+ QStringLiteral("io.github.projectroasd.rocontrol.desktop")));
const QRegularExpression screenshotRe(
QStringLiteral(R"(https://raw\.githubusercontent\.com/.+/docs/screenshots/.+)"));
From 7822dabe31666646e169b5968425010eae2a2fa1 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 18:53:24 +0300
Subject: [PATCH 03/22] docs: add Fedora bootstrap and runtime guidance
---
README.md | 8 ++++
README.tr.md | 8 ++++
docs/BUILDING.md | 78 ++++++++++++++++++-----------------
docs/FEDORA.md | 74 +++++++++++++++++++++++++++++++++
packaging/rpm/ro-control.spec | 3 ++
scripts/fedora-bootstrap.sh | 68 ++++++++++++++++++++++++++++++
6 files changed, 202 insertions(+), 37 deletions(-)
create mode 100644 docs/FEDORA.md
create mode 100755 scripts/fedora-bootstrap.sh
diff --git a/README.md b/README.md
index 04cbadd..b9397b7 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,14 @@ sudo dnf install ./ro-control-*.rpm
See [docs/BUILDING.md](docs/BUILDING.md) for full instructions.
+Fedora quick bootstrap:
+
+```bash
+./scripts/fedora-bootstrap.sh
+```
+
+For Fedora-specific runtime notes, see [docs/FEDORA.md](docs/FEDORA.md).
+
### CLI Quick Examples
```bash
diff --git a/README.tr.md b/README.tr.md
index 72973a1..2aac2b4 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -71,6 +71,14 @@ sudo dnf install ./ro-control-*.rpm
Tam talimatlar için [docs/BUILDING.md](docs/BUILDING.md) dosyasına bakın.
+Fedora hızlı kurulum:
+
+```bash
+./scripts/fedora-bootstrap.sh
+```
+
+Fedora çalışma notları için [docs/FEDORA.md](docs/FEDORA.md) dosyasına bakın.
+
### CLI Hızlı Örnekler
```bash
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index abc8a54..afc4110 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -16,6 +16,17 @@ This guide covers building ro-Control from source on Linux systems with Qt 6 and
---
+## Fedora Quick Bootstrap
+
+```bash
+./scripts/fedora-bootstrap.sh
+```
+
+The script installs Fedora dependencies, builds the app, and runs tests by default.
+For Fedora-specific runtime notes, see [FEDORA.md](FEDORA.md).
+
+---
+
## Install Dependencies
```bash
@@ -32,6 +43,12 @@ sudo dnf install \
polkit-devel
```
+Runtime tools used by diagnostics and driver operations:
+
+```bash
+sudo dnf install dnf polkit pciutils mokutil kmod
+```
+
---
## Clone the Repository
@@ -48,25 +65,15 @@ cd ro-Control
### Debug Build (for development)
```bash
-mkdir build && cd build
-cmake .. -DCMAKE_BUILD_TYPE=Debug
-make -j$(nproc)
+cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
+cmake --build build -j$(nproc)
```
### Release Build
```bash
-mkdir build && cd build
-cmake .. -DCMAKE_BUILD_TYPE=Release
-make -j$(nproc)
-```
-
-### Faster builds with Ninja (optional)
-
-```bash
-mkdir build && cd build
-cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug
-ninja
+cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
+cmake --build build -j$(nproc)
```
### Refresh translations (recommended before release)
@@ -81,19 +88,19 @@ cmake --build build
## Run
```bash
-# From the build directory
-./ro-control
+# From the repository root
+./build/ro-control
```
CLI examples:
```bash
-./ro-control help
-./ro-control version
-./ro-control status
-./ro-control diagnostics --json
-./ro-control driver install --proprietary --accept-license
-./ro-control driver update
+./build/ro-control help
+./build/ro-control version
+./build/ro-control status
+./build/ro-control diagnostics --json
+./build/ro-control driver install --proprietary --accept-license
+./build/ro-control driver update
```
> **Note:** Driver install/remove operations require PolicyKit authentication. The UI will prompt you automatically.
@@ -110,27 +117,25 @@ After `cmake --install`, the CLI integration also installs:
## Install System-Wide
```bash
-cd build
-sudo make install
+sudo cmake --install build
```
This installs:
-- Binary → `/usr/local/bin/ro-control`
-- Privileged helper → `/usr/local/libexec/ro-control-helper`
-- Desktop entry → `/usr/local/share/applications/`
-- Icons → `/usr/local/share/icons/`
-- AppStream metadata → `/usr/local/share/metainfo/`
-- PolicyKit policy → `/usr/local/share/polkit-1/actions/`
+- Binary -> `/usr/local/bin/ro-control`
+- Privileged helper -> `/usr/local/libexec/ro-control-helper`
+- Desktop entry -> `/usr/local/share/applications/`
+- Icons -> `/usr/local/share/icons/`
+- AppStream metadata -> `/usr/local/share/metainfo/`
+- PolicyKit policy -> `/usr/local/share/polkit-1/actions/`
---
## Build with Tests
```bash
-mkdir build && cd build
-cmake .. -DBUILD_TESTS=ON
-make -j$(nproc)
-ctest --output-on-failure
+cmake -S . -B build -G Ninja -DBUILD_TESTS=ON
+cmake --build build -j$(nproc)
+ctest --test-dir build --output-on-failure
```
---
@@ -177,9 +182,8 @@ cmake --build build
After making changes, always verify the build passes before submitting a PR:
```bash
-cd build
-make -j$(nproc)
-ctest --output-on-failure
+cmake --build build -j$(nproc)
+ctest --test-dir build --output-on-failure
```
See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full contribution guide.
diff --git a/docs/FEDORA.md b/docs/FEDORA.md
new file mode 100644
index 0000000..b07d5e0
--- /dev/null
+++ b/docs/FEDORA.md
@@ -0,0 +1,74 @@
+# Fedora Run Guide
+
+This guide is focused on running `ro-control` on Fedora Workstation/Spin systems.
+
+## 1) Bootstrap (recommended)
+
+From the repository root:
+
+```bash
+./scripts/fedora-bootstrap.sh
+```
+
+Optional flags via environment variables:
+
+```bash
+ENABLE_TESTS=0 BUILD_TYPE=Debug ./scripts/fedora-bootstrap.sh
+INSTALL_AFTER_BUILD=1 INSTALL_PREFIX=/usr ./scripts/fedora-bootstrap.sh
+```
+
+## 2) Manual dependency install (equivalent)
+
+Build dependencies:
+
+```bash
+sudo dnf install -y \
+ cmake \
+ extra-cmake-modules \
+ gcc-c++ \
+ ninja-build \
+ qt6-qtbase-devel \
+ qt6-qtdeclarative-devel \
+ qt6-qttools-devel \
+ qt6-qtwayland-devel \
+ kf6-qqc2-desktop-style \
+ polkit-devel
+```
+
+Runtime tools used by diagnostics and driver workflows:
+
+```bash
+sudo dnf install -y dnf polkit pciutils mokutil kmod
+```
+
+## 3) Build and run
+
+```bash
+cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
+cmake --build build -j"$(nproc)"
+ctest --test-dir build --output-on-failure
+./build/ro-control
+```
+
+## 4) Install (optional)
+
+```bash
+sudo cmake --install build
+```
+
+## 5) Driver management prerequisites
+
+- `ro-control` invokes privileged operations through `pkexec`.
+- For proprietary NVIDIA flow, the app enables RPM Fusion and installs
+ `akmod-nvidia` using `dnf`.
+- A reboot is required after install/update/remove flows.
+- On Secure Boot systems, kernel module signing policy may still require manual steps.
+
+## 6) Quick verification
+
+```bash
+./build/ro-control diagnostics --json
+./build/ro-control status
+```
+
+If GPU telemetry is unavailable, verify `nvidia-smi` is present and working.
diff --git a/packaging/rpm/ro-control.spec b/packaging/rpm/ro-control.spec
index d1d3336..ff71cd3 100644
--- a/packaging/rpm/ro-control.spec
+++ b/packaging/rpm/ro-control.spec
@@ -26,6 +26,9 @@ Requires: kf6-qqc2-desktop-style
Requires: polkit
Requires: dnf
Requires: /usr/bin/pkexec
+Requires: pciutils
+Recommends: mokutil
+Recommends: kmod
%description
ro-Control is a Qt6/KDE Plasma desktop application that helps users
diff --git a/scripts/fedora-bootstrap.sh b/scripts/fedora-bootstrap.sh
new file mode 100755
index 0000000..28aa179
--- /dev/null
+++ b/scripts/fedora-bootstrap.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+BUILD_DIR="${BUILD_DIR:-$ROOT_DIR/build}"
+BUILD_TYPE="${BUILD_TYPE:-Release}"
+GENERATOR="${GENERATOR:-Ninja}"
+ENABLE_TESTS="${ENABLE_TESTS:-1}"
+INSTALL_AFTER_BUILD="${INSTALL_AFTER_BUILD:-0}"
+INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}"
+
+build_reqs=(
+ cmake
+ extra-cmake-modules
+ gcc-c++
+ ninja-build
+ qt6-qtbase-devel
+ qt6-qtdeclarative-devel
+ qt6-qttools-devel
+ qt6-qtwayland-devel
+ kf6-qqc2-desktop-style
+ polkit-devel
+)
+
+runtime_tools=(
+ dnf
+ polkit
+ pciutils
+ mokutil
+ kmod
+)
+
+echo "[1/4] Installing Fedora build dependencies..."
+sudo dnf install -y "${build_reqs[@]}"
+
+echo "[2/4] Installing runtime utilities used by diagnostics/driver workflows..."
+sudo dnf install -y "${runtime_tools[@]}"
+
+cmake_args=(
+ -S "$ROOT_DIR"
+ -B "$BUILD_DIR"
+ -G "$GENERATOR"
+ -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
+ -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX"
+)
+
+if [[ "$ENABLE_TESTS" == "1" ]]; then
+ cmake_args+=( -DBUILD_TESTS=ON )
+else
+ cmake_args+=( -DBUILD_TESTS=OFF )
+fi
+
+echo "[3/4] Configuring and building ($BUILD_TYPE)..."
+cmake "${cmake_args[@]}"
+cmake --build "$BUILD_DIR" -j"$(nproc)"
+
+if [[ "$ENABLE_TESTS" == "1" ]]; then
+ echo "[3.1/4] Running test suite..."
+ ctest --test-dir "$BUILD_DIR" --output-on-failure
+fi
+
+echo "[4/4] Done."
+echo "Run from build dir: $BUILD_DIR/ro-control"
+
+if [[ "$INSTALL_AFTER_BUILD" == "1" ]]; then
+ echo "Installing to $INSTALL_PREFIX..."
+ sudo cmake --install "$BUILD_DIR"
+fi
From 7bf8d6a02b888e1414ea7377c8c7774e96eae6f5 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:06:08 +0300
Subject: [PATCH 04/22] chore: Add dev-watch.sh script for continuous
development monitoring.
---
scripts/dev-watch.sh | 103 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
create mode 100755 scripts/dev-watch.sh
diff --git a/scripts/dev-watch.sh b/scripts/dev-watch.sh
new file mode 100755
index 0000000..081fd67
--- /dev/null
+++ b/scripts/dev-watch.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+# dev-watch.sh — Kaynak değişikliklerini izler, otomatik build alır ve uygulamayı yeniden başlatır.
+# Kullanım: ./scripts/dev-watch.sh
+# Gereksinim: sudo dnf install inotify-tools
+
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}"]/.." && pwd)"
+BUILD_DIR="${BUILD_DIR:-$ROOT_DIR/build}"
+BINARY="$BUILD_DIR/ro-control"
+APP_PID=""
+
+# ─── Renk tanımları ──────────────────────────────────────────────────────────
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+RESET='\033[0m'
+
+log() { echo -e "${CYAN}[dev-watch]${RESET} $*"; }
+ok() { echo -e "${GREEN}[dev-watch]${RESET} $*"; }
+warn() { echo -e "${YELLOW}[dev-watch]${RESET} $*"; }
+err() { echo -e "${RED}[dev-watch]${RESET} $*"; }
+
+# ─── Bağımlılık kontrolü ─────────────────────────────────────────────────────
+if ! command -v inotifywait &>/dev/null; then
+ err "inotify-tools bulunamadı. Kurmak için:"
+ err " sudo dnf install inotify-tools"
+ exit 1
+fi
+
+if [[ ! -d "$BUILD_DIR" || ! -f "$BUILD_DIR/CMakeCache.txt" ]]; then
+ warn "Build dizini yok veya cmake yapılandırılmamış."
+ warn "Önce şunu çalıştır: ./scripts/fedora-bootstrap.sh"
+ exit 1
+fi
+
+# ─── Uygulama durdurma ───────────────────────────────────────────────────────
+stop_app() {
+ if [[ -n "$APP_PID" ]] && kill -0 "$APP_PID" 2>/dev/null; then
+ log "Uygulama durduruluyor (PID: $APP_PID)..."
+ kill "$APP_PID" 2>/dev/null || true
+ wait "$APP_PID" 2>/dev/null || true
+ APP_PID=""
+ fi
+}
+
+# ─── Build + başlat ──────────────────────────────────────────────────────────
+build_and_run() {
+ echo ""
+ log "${BOLD}Incremental build başlıyor...${RESET}"
+ if cmake --build "$BUILD_DIR" -j"$(nproc)" 2>&1; then
+ ok "✓ Build başarılı"
+ stop_app
+ log "Uygulama başlatılıyor: $BINARY"
+ "$BINARY" &
+ APP_PID=$!
+ ok "✓ ro-control çalışıyor (PID: $APP_PID)"
+ else
+ err "✗ Build hatası — bekleniyor..."
+ fi
+ echo ""
+}
+
+# ─── Temiz çıkış ─────────────────────────────────────────────────────────────
+cleanup() {
+ echo ""
+ warn "Çıkış sinyali alındı."
+ stop_app
+ exit 0
+}
+trap cleanup SIGINT SIGTERM
+
+# ─── İlk build ve başlangıç ──────────────────────────────────────────────────
+echo -e "${BOLD}${CYAN}"
+echo "╔══════════════════════════════════════════╗"
+echo "║ ro-Control dev-watch modu ║"
+echo "╚══════════════════════════════════════════╝"
+echo -e "${RESET}"
+log "İzlenen dizin: $ROOT_DIR/src"
+log "Build dizini: $BUILD_DIR"
+log "Çıkmak için: Ctrl+C"
+echo ""
+
+build_and_run
+
+# ─── Değişiklik izleme döngüsü ───────────────────────────────────────────────
+inotifywait -m -r \
+ --include '\.(cpp|h|qml|js|ts)$' \
+ -e modify,create,delete,moved_to \
+ --format "%w%f [%e]" \
+ "$ROOT_DIR/src" "$ROOT_DIR/i18n" 2>/dev/null \
+| while IFS= read -r line; do
+ # Birden fazla hızlı değişikliği birleştir (debounce 800ms)
+ log "Değişiklik algılandı: $line"
+ sleep 0.8
+
+ # Kuyruktaki diğer olayları boşalt
+ while IFS= read -t 0.1 -r _extra; do :; done <&0 2>/dev/null || true
+
+ build_and_run
+done
From e405784e847d7feba661bc942d47342fe58d4872 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:08:34 +0300
Subject: [PATCH 05/22] refactor: Normalize Turkish characters in comments and
log messages and fix a path concatenation.
---
scripts/dev-watch.sh | 55 ++++++++++++++++++++------------------------
1 file changed, 25 insertions(+), 30 deletions(-)
diff --git a/scripts/dev-watch.sh b/scripts/dev-watch.sh
index 081fd67..eb285bb 100755
--- a/scripts/dev-watch.sh
+++ b/scripts/dev-watch.sh
@@ -1,16 +1,15 @@
#!/usr/bin/env bash
-# dev-watch.sh — Kaynak değişikliklerini izler, otomatik build alır ve uygulamayı yeniden başlatır.
-# Kullanım: ./scripts/dev-watch.sh
+# dev-watch.sh — Kaynak degisikliklerini izler, otomatik build alir ve uygulamayi yeniden baslatir.
+# Kullanim: ./scripts/dev-watch.sh
# Gereksinim: sudo dnf install inotify-tools
set -euo pipefail
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}"]/.." && pwd)"
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BUILD_DIR="${BUILD_DIR:-$ROOT_DIR/build}"
BINARY="$BUILD_DIR/ro-control"
APP_PID=""
-# ─── Renk tanımları ──────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
@@ -23,20 +22,20 @@ ok() { echo -e "${GREEN}[dev-watch]${RESET} $*"; }
warn() { echo -e "${YELLOW}[dev-watch]${RESET} $*"; }
err() { echo -e "${RED}[dev-watch]${RESET} $*"; }
-# ─── Bağımlılık kontrolü ─────────────────────────────────────────────────────
+# --- Bagimlilik kontrolu ---
if ! command -v inotifywait &>/dev/null; then
- err "inotify-tools bulunamadı. Kurmak için:"
+ err "inotify-tools bulunamadi. Kurmak icin:"
err " sudo dnf install inotify-tools"
exit 1
fi
if [[ ! -d "$BUILD_DIR" || ! -f "$BUILD_DIR/CMakeCache.txt" ]]; then
- warn "Build dizini yok veya cmake yapılandırılmamış."
- warn "Önce şunu çalıştır: ./scripts/fedora-bootstrap.sh"
+ warn "Build dizini yok veya cmake yapilandirilmamis."
+ warn "Once sunu calistir: ./scripts/fedora-bootstrap.sh"
exit 1
fi
-# ─── Uygulama durdurma ───────────────────────────────────────────────────────
+# --- Uygulamayi durdur ---
stop_app() {
if [[ -n "$APP_PID" ]] && kill -0 "$APP_PID" 2>/dev/null; then
log "Uygulama durduruluyor (PID: $APP_PID)..."
@@ -46,57 +45,53 @@ stop_app() {
fi
}
-# ─── Build + başlat ──────────────────────────────────────────────────────────
+# --- Incremental build + yeniden basla ---
build_and_run() {
echo ""
- log "${BOLD}Incremental build başlıyor...${RESET}"
+ log "Incremental build basliyor..."
if cmake --build "$BUILD_DIR" -j"$(nproc)" 2>&1; then
- ok "✓ Build başarılı"
+ ok "Build basarili"
stop_app
- log "Uygulama başlatılıyor: $BINARY"
+ log "Uygulama baslatiliyor: $BINARY"
"$BINARY" &
APP_PID=$!
- ok "✓ ro-control çalışıyor (PID: $APP_PID)"
+ ok "ro-control calisiyor (PID: $APP_PID)"
else
- err "✗ Build hatası — bekleniyor..."
+ err "Build hatasi -- degisiklikleri kontrol et."
fi
echo ""
}
-# ─── Temiz çıkış ─────────────────────────────────────────────────────────────
+# --- Temiz cikis ---
cleanup() {
echo ""
- warn "Çıkış sinyali alındı."
+ warn "Cikis sinyali alindi."
stop_app
exit 0
}
trap cleanup SIGINT SIGTERM
-# ─── İlk build ve başlangıç ──────────────────────────────────────────────────
-echo -e "${BOLD}${CYAN}"
-echo "╔══════════════════════════════════════════╗"
-echo "║ ro-Control dev-watch modu ║"
-echo "╚══════════════════════════════════════════╝"
-echo -e "${RESET}"
-log "İzlenen dizin: $ROOT_DIR/src"
-log "Build dizini: $BUILD_DIR"
-log "Çıkmak için: Ctrl+C"
+# --- Baslangic ---
+echo ""
+log "ro-Control dev-watch modu"
+log "Izlenen dizin : $ROOT_DIR/src"
+log "Build dizini : $BUILD_DIR"
+log "Cikmak icin : Ctrl+C"
echo ""
build_and_run
-# ─── Değişiklik izleme döngüsü ───────────────────────────────────────────────
+# --- Degisiklik izleme dongusu ---
inotifywait -m -r \
--include '\.(cpp|h|qml|js|ts)$' \
-e modify,create,delete,moved_to \
--format "%w%f [%e]" \
"$ROOT_DIR/src" "$ROOT_DIR/i18n" 2>/dev/null \
| while IFS= read -r line; do
- # Birden fazla hızlı değişikliği birleştir (debounce 800ms)
- log "Değişiklik algılandı: $line"
+ log "Degisiklik algilandi: $line"
sleep 0.8
- # Kuyruktaki diğer olayları boşalt
+ # Kuyruktaki diger olaylari bosalt (debounce)
while IFS= read -t 0.1 -r _extra; do :; done <&0 2>/dev/null || true
build_and_run
From 906d10c0e928a5d4493a142e3474e1c314a9acf7 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:13:45 +0300
Subject: [PATCH 06/22] feat: Automatically configure the Qt render backend in
the dev-watch script and refactor QML context property assignment in
main.cpp.
---
scripts/dev-watch.sh | 40 ++++++++++++++++++++++++++++++++++++----
src/main.cpp | 17 +++++++++--------
2 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/scripts/dev-watch.sh b/scripts/dev-watch.sh
index eb285bb..d4a908a 100755
--- a/scripts/dev-watch.sh
+++ b/scripts/dev-watch.sh
@@ -14,7 +14,6 @@ RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
-BOLD='\033[1m'
RESET='\033[0m'
log() { echo -e "${CYAN}[dev-watch]${RESET} $*"; }
@@ -22,6 +21,29 @@ ok() { echo -e "${GREEN}[dev-watch]${RESET} $*"; }
warn() { echo -e "${YELLOW}[dev-watch]${RESET} $*"; }
err() { echo -e "${RED}[dev-watch]${RESET} $*"; }
+# --- Qt render backend otomatik sec ---
+# GPU olmadan calisan sistemlerde (NVIDIA surucusu kurulu degil, VM, vb.)
+# Qt'un EGL hatasi vermemesi icin fallback backend ayarla
+setup_qt_env() {
+ # Eger kullanici zaten bir backend secmisse dokunma
+ if [[ -n "${QSG_RHI_BACKEND:-}" || -n "${QT_XCB_GL_INTEGRATION:-}" ]]; then
+ return
+ fi
+
+ # EGL/DRI2 kullanilabilir mi kontrol et
+ if command -v glxinfo &>/dev/null && glxinfo 2>/dev/null | grep -q "direct rendering: Yes"; then
+ # Donanim hizlandirma var, varsayilan backend kullan
+ log "OpenGL donanim hizlandirma mevcut, varsayilan renderer kullaniliyor."
+ else
+ # Yazilim renderer'a gec - GPU olmayan / surucusuz ortam
+ warn "GPU/EGL hizlandirma bulunamadi, yazilim renderer'a geciliyor."
+ warn "NVIDIA surucu kurulduktan sonra bu uyari kaybolacak."
+ export QT_XCB_GL_INTEGRATION=none
+ export LIBGL_ALWAYS_SOFTWARE=0
+ export QSG_RENDERER_DEBUG=""
+ fi
+}
+
# --- Bagimlilik kontrolu ---
if ! command -v inotifywait &>/dev/null; then
err "inotify-tools bulunamadi. Kurmak icin:"
@@ -35,6 +57,12 @@ if [[ ! -d "$BUILD_DIR" || ! -f "$BUILD_DIR/CMakeCache.txt" ]]; then
exit 1
fi
+if [[ ! -f "$BINARY" ]]; then
+ warn "Binary bulunamadi: $BINARY"
+ warn "Once sunu calistir: ./scripts/fedora-bootstrap.sh"
+ exit 1
+fi
+
# --- Uygulamayi durdur ---
stop_app() {
if [[ -n "$APP_PID" ]] && kill -0 "$APP_PID" 2>/dev/null; then
@@ -52,8 +80,8 @@ build_and_run() {
if cmake --build "$BUILD_DIR" -j"$(nproc)" 2>&1; then
ok "Build basarili"
stop_app
- log "Uygulama baslatiliyor: $BINARY"
- "$BINARY" &
+ log "Uygulama baslatiliyor..."
+ "$BINARY" 2>/dev/null &
APP_PID=$!
ok "ro-control calisiyor (PID: $APP_PID)"
else
@@ -71,11 +99,15 @@ cleanup() {
}
trap cleanup SIGINT SIGTERM
+# --- Qt ortam degiskenlerini ayarla ---
+setup_qt_env
+
# --- Baslangic ---
echo ""
log "ro-Control dev-watch modu"
-log "Izlenen dizin : $ROOT_DIR/src"
+log "Proje dizini : $ROOT_DIR"
log "Build dizini : $BUILD_DIR"
+log "Izlenen dizin : $ROOT_DIR/src"
log "Cikmak icin : Ctrl+C"
echo ""
diff --git a/src/main.cpp b/src/main.cpp
index a94bf16..409447f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -235,14 +236,14 @@ int main(int argc, char *argv[]) {
RamMonitor ramMonitor;
QQmlApplicationEngine engine;
- engine.setInitialProperties({
- {"nvidiaDetector", QVariant::fromValue(&detector)},
- {"nvidiaInstaller", QVariant::fromValue(&installer)},
- {"nvidiaUpdater", QVariant::fromValue(&updater)},
- {"cpuMonitor", QVariant::fromValue(&cpuMonitor)},
- {"gpuMonitor", QVariant::fromValue(&gpuMonitor)},
- {"ramMonitor", QVariant::fromValue(&ramMonitor)},
- });
+
+ // Backend nesnelerini tüm QML dosyalarına global olarak aç
+ engine.rootContext()->setContextProperty("nvidiaDetector", &detector);
+ engine.rootContext()->setContextProperty("nvidiaInstaller", &installer);
+ engine.rootContext()->setContextProperty("nvidiaUpdater", &updater);
+ engine.rootContext()->setContextProperty("cpuMonitor", &cpuMonitor);
+ engine.rootContext()->setContextProperty("gpuMonitor", &gpuMonitor);
+ engine.rootContext()->setContextProperty("ramMonitor", &ramMonitor);
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
From ca069c18d166adb7d39bc9cd443e9b3f28ad54d9 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:18:05 +0300
Subject: [PATCH 07/22] Refactor QML layouts to utilize QtQuick.Layouts for
improved responsiveness and dynamic content sizing across pages and the
sidebar.
---
src/qml/components/SectionPanel.qml | 8 +++-
src/qml/components/SidebarMenu.qml | 58 +++++++++++++++++------------
src/qml/pages/DriverPage.qml | 7 +---
src/qml/pages/MonitorPage.qml | 7 +---
src/qml/pages/SettingsPage.qml | 7 +---
5 files changed, 48 insertions(+), 39 deletions(-)
diff --git a/src/qml/components/SectionPanel.qml b/src/qml/components/SectionPanel.qml
index a755a05..f93ed12 100644
--- a/src/qml/components/SectionPanel.qml
+++ b/src/qml/components/SectionPanel.qml
@@ -14,9 +14,13 @@ Rectangle {
color: theme.card
border.width: 1
border.color: theme.border
+ implicitHeight: innerColumn.implicitHeight + 36
ColumnLayout {
- anchors.fill: parent
+ id: innerColumn
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
anchors.margins: 18
spacing: 14
@@ -30,6 +34,8 @@ Rectangle {
font.bold: true
color: panel.theme.text
visible: text.length > 0
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
}
Label {
diff --git a/src/qml/components/SidebarMenu.qml b/src/qml/components/SidebarMenu.qml
index 583b8bf..55a9e5a 100644
--- a/src/qml/components/SidebarMenu.qml
+++ b/src/qml/components/SidebarMenu.qml
@@ -1,11 +1,13 @@
import QtQuick
import QtQuick.Controls
+import QtQuick.Layouts
Rectangle {
id: sidebar
width: 220
required property var theme
color: theme.sidebarBg
+ clip: true
property int currentIndex: 0
readonly property var menuItems: [
@@ -14,14 +16,27 @@ Rectangle {
qsTr("Settings")
]
- Column {
- anchors.fill: parent
+ // Versiyon — alt köşe
+ Label {
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottomMargin: 16
+ text: "v" + Qt.application.version
+ font.pixelSize: 11
+ color: theme.sidebarHint
+ z: 1
+ }
+
+ ColumnLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
spacing: 0
// Başlık
Item {
- width: parent.width
- height: 70
+ Layout.fillWidth: true
+ implicitHeight: 70
Label {
anchors.centerIn: parent
@@ -33,15 +48,16 @@ Rectangle {
}
Rectangle {
- width: parent.width - 32
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
height: 1
- anchors.horizontalCenter: parent.horizontalCenter
color: theme.sidebarBorder
}
Item {
- width: 1
- height: 12
+ Layout.fillWidth: true
+ implicitHeight: 12
}
Repeater {
@@ -50,10 +66,12 @@ Rectangle {
delegate: Rectangle {
id: menuItem
required property int index
+ required property string modelData
- width: sidebar.width - 16
- height: 44
- x: 8
+ Layout.fillWidth: true
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+ implicitHeight: 44
radius: 8
color: sidebar.currentIndex === menuItem.index ? theme.sidebarActive
: mouseArea.containsMouse ? theme.sidebarHover
@@ -61,10 +79,14 @@ Rectangle {
Label {
anchors.verticalCenter: parent.verticalCenter
- leftPadding: 16
- text: modelData
+ anchors.left: parent.left
+ anchors.leftMargin: 16
+ anchors.right: parent.right
+ anchors.rightMargin: 8
+ text: menuItem.modelData
font.pixelSize: 14
color: sidebar.currentIndex === menuItem.index ? theme.sidebarAccent : theme.sidebarMuted
+ elide: Text.ElideRight
}
MouseArea {
@@ -77,14 +99,4 @@ Rectangle {
}
}
}
-
- // Versiyon — alt köşe
- Label {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottomMargin: 16
- text: "v" + Qt.application.version
- font.pixelSize: 11
- color: theme.sidebarHint
- }
}
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 10a29c8..c2e86f8 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -16,9 +16,10 @@ Item {
ScrollView {
anchors.fill: parent
clip: true
+ contentWidth: availableWidth
ColumnLayout {
- width: Math.max(page.width - 12, 760)
+ width: parent.availableWidth
spacing: page.compactMode ? 12 : 16
StatusBanner {
@@ -83,7 +84,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Verification")
subtitle: qsTr("Review driver prerequisites before changing packages.")
@@ -148,7 +148,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Driver Actions")
subtitle: qsTr("Use guided actions to install, switch or remove the current stack.")
@@ -227,7 +226,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Update Center")
subtitle: qsTr("Check the repository version and pin a specific build when required.")
@@ -296,7 +294,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Activity Log")
subtitle: qsTr("Operation output is streamed here in real time.")
diff --git a/src/qml/pages/MonitorPage.qml b/src/qml/pages/MonitorPage.qml
index f62e21c..5e77233 100644
--- a/src/qml/pages/MonitorPage.qml
+++ b/src/qml/pages/MonitorPage.qml
@@ -13,9 +13,10 @@ Item {
ScrollView {
anchors.fill: parent
clip: true
+ contentWidth: availableWidth
ColumnLayout {
- width: Math.max(page.width - 12, 760)
+ width: parent.availableWidth
spacing: page.compactMode ? 12 : 16
GridLayout {
@@ -69,7 +70,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Live Resource Curves")
subtitle: qsTr("Quick pulse view for the most important machine resources.")
@@ -122,7 +122,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Health Summary")
subtitle: qsTr("Fast interpretation of the raw telemetry values.")
@@ -170,7 +169,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Detailed Signals")
subtitle: qsTr("Expanded raw values for support and diagnostics.")
@@ -226,7 +224,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: page.theme
title: qsTr("Actions")
subtitle: qsTr("Trigger a manual refresh when you need a fresh sample.")
diff --git a/src/qml/pages/SettingsPage.qml b/src/qml/pages/SettingsPage.qml
index ba0961a..e61ab21 100644
--- a/src/qml/pages/SettingsPage.qml
+++ b/src/qml/pages/SettingsPage.qml
@@ -13,9 +13,10 @@ Item {
ScrollView {
anchors.fill: parent
clip: true
+ contentWidth: availableWidth
ColumnLayout {
- width: Math.max(settingsPage.width - 12, 760)
+ width: parent.availableWidth
spacing: settingsPage.compactMode ? 12 : 16
GridLayout {
@@ -26,7 +27,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: settingsPage.theme
title: qsTr("Interface")
subtitle: qsTr("Tune the shell density and how much operational detail the app exposes.")
@@ -105,7 +105,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: settingsPage.theme
title: qsTr("Diagnostics")
subtitle: qsTr("Useful runtime context before filing issues or performing support work.")
@@ -167,7 +166,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: settingsPage.theme
title: qsTr("Workflow Guidance")
subtitle: qsTr("Recommended order of operations when changing drivers.")
@@ -191,7 +189,6 @@ Item {
SectionPanel {
Layout.fillWidth: true
- Layout.fillHeight: true
theme: settingsPage.theme
title: qsTr("About")
subtitle: qsTr("Project identity and current shell mode.")
From ed21c75739d2b20b4a58390181eaf1ae04ad9dca Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:29:56 +0300
Subject: [PATCH 08/22] Refactor: Centralize session type detection into a new
`SessionUtil` module and introduce internationalization for updater messages.
---
.gitignore | 1 +
CMakeLists.txt | 1 +
src/backend/nvidia/detector.cpp | 23 +-------
src/backend/nvidia/detector.h | 1 -
src/backend/nvidia/installer.cpp | 23 +-------
src/backend/nvidia/installer.h | 1 -
src/backend/nvidia/updater.cpp | 91 ++++++++++++------------------
src/backend/system/sessionutil.cpp | 31 ++++++++++
src/backend/system/sessionutil.h | 12 ++++
src/qml/components/StatCard.qml | 6 +-
10 files changed, 87 insertions(+), 103 deletions(-)
create mode 100644 src/backend/system/sessionutil.cpp
create mode 100644 src/backend/system/sessionutil.h
diff --git a/.gitignore b/.gitignore
index d56e801..fc40183 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# Build directories
build/
build-*/
+src/build-*/
cmake-build-*/
out/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d346e9..1311434 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,6 +70,7 @@ set(BACKEND_SOURCES
src/backend/system/polkit.cpp
src/backend/system/dnfmanager.cpp
src/backend/system/commandrunner.cpp
+ src/backend/system/sessionutil.cpp
)
set(APP_SOURCES
diff --git a/src/backend/nvidia/detector.cpp b/src/backend/nvidia/detector.cpp
index 40bbb52..92ec1d0 100644
--- a/src/backend/nvidia/detector.cpp
+++ b/src/backend/nvidia/detector.cpp
@@ -1,6 +1,7 @@
#include "detector.h"
#include "system/commandrunner.h"
+#include "system/sessionutil.h"
#include
#include
@@ -18,7 +19,7 @@ NvidiaDetector::GpuInfo NvidiaDetector::detect() const {
info.driverLoaded = isModuleLoaded(QStringLiteral("nvidia"));
info.nouveauActive = isModuleLoaded(QStringLiteral("nouveau"));
info.secureBootEnabled = detectSecureBoot(&info.secureBootKnown);
- info.sessionType = detectSessionType();
+ info.sessionType = SessionUtil::detectSessionType();
return info;
}
@@ -151,24 +152,4 @@ bool NvidiaDetector::detectSecureBoot(bool *known) const {
return false;
}
-QString NvidiaDetector::detectSessionType() const {
- const QString envType =
- qEnvironmentVariable("XDG_SESSION_TYPE").trimmed().toLower();
- if (!envType.isEmpty())
- return envType;
- CommandRunner runner;
- const auto loginctl =
- runner.run(QStringLiteral("loginctl"),
- {QStringLiteral("show-session"),
- qEnvironmentVariable("XDG_SESSION_ID"), QStringLiteral("-p"),
- QStringLiteral("Type"), QStringLiteral("--value")});
-
- if (loginctl.success()) {
- const QString type = loginctl.stdout.trimmed().toLower();
- if (!type.isEmpty())
- return type;
- }
-
- return QStringLiteral("unknown");
-}
diff --git a/src/backend/nvidia/detector.h b/src/backend/nvidia/detector.h
index 83b6517..d0ffd97 100644
--- a/src/backend/nvidia/detector.h
+++ b/src/backend/nvidia/detector.h
@@ -65,7 +65,6 @@ class NvidiaDetector : public QObject {
QString detectDriverVersion() const;
bool isModuleLoaded(const QString &moduleName) const;
bool detectSecureBoot(bool *known = nullptr) const;
- QString detectSessionType() const;
GpuInfo m_info;
};
diff --git a/src/backend/nvidia/installer.cpp b/src/backend/nvidia/installer.cpp
index eec3a23..9978c3a 100644
--- a/src/backend/nvidia/installer.cpp
+++ b/src/backend/nvidia/installer.cpp
@@ -1,6 +1,7 @@
#include "installer.h"
#include "system/commandrunner.h"
+#include "system/sessionutil.h"
#include
#include
@@ -212,7 +213,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
Qt::QueuedConnection);
runner.runAsRoot(QStringLiteral("akmods"), {QStringLiteral("--force")});
- const QString sessionType = guard->detectSessionType();
+ const QString sessionType = SessionUtil::detectSessionType();
QString sessionError;
if (!guard->applySessionSpecificSetup(runner, sessionType, &sessionError)) {
QMetaObject::invokeMethod(
@@ -466,27 +467,7 @@ void NvidiaInstaller::deepClean() {
});
}
-QString NvidiaInstaller::detectSessionType() const {
- const QString envType =
- qEnvironmentVariable("XDG_SESSION_TYPE").trimmed().toLower();
- if (!envType.isEmpty())
- return envType;
- CommandRunner runner;
- const auto loginctl =
- runner.run(QStringLiteral("loginctl"),
- {QStringLiteral("show-session"),
- qEnvironmentVariable("XDG_SESSION_ID"), QStringLiteral("-p"),
- QStringLiteral("Type"), QStringLiteral("--value")});
-
- if (loginctl.success()) {
- const QString type = loginctl.stdout.trimmed().toLower();
- if (!type.isEmpty())
- return type;
- }
-
- return QStringLiteral("unknown");
-}
bool NvidiaInstaller::applySessionSpecificSetup(CommandRunner &runner,
const QString &sessionType,
diff --git a/src/backend/nvidia/installer.h b/src/backend/nvidia/installer.h
index 09c9385..fc564a4 100644
--- a/src/backend/nvidia/installer.h
+++ b/src/backend/nvidia/installer.h
@@ -61,7 +61,6 @@ class NvidiaInstaller : public QObject {
void setBusy(bool busy);
void runAsyncTask(const std::function &task);
void setProprietaryAgreement(bool required, const QString &text);
- QString detectSessionType() const;
bool applySessionSpecificSetup(CommandRunner &runner,
const QString &sessionType,
QString *errorMessage);
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index 6302332..3eab02d 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -1,6 +1,7 @@
#include "updater.h"
#include "detector.h"
#include "system/commandrunner.h"
+#include "system/sessionutil.h"
#include "versionparser.h"
#include
@@ -49,29 +50,7 @@ struct UpdateStatusSnapshot {
QString message;
};
-QString detectSessionTypeImpl() {
- const QString envType =
- qEnvironmentVariable("XDG_SESSION_TYPE").trimmed().toLower();
- if (!envType.isEmpty()) {
- return envType;
- }
- CommandRunner runner;
- const auto loginctl =
- runner.run(QStringLiteral("loginctl"),
- {QStringLiteral("show-session"),
- qEnvironmentVariable("XDG_SESSION_ID"), QStringLiteral("-p"),
- QStringLiteral("Type"), QStringLiteral("--value")});
-
- if (loginctl.success()) {
- const QString type = loginctl.stdout.trimmed().toLower();
- if (!type.isEmpty()) {
- return type;
- }
- }
-
- return QStringLiteral("unknown");
-}
UpdateStatusSnapshot collectUpdateStatus() {
UpdateStatusSnapshot snapshot;
@@ -79,7 +58,7 @@ UpdateStatusSnapshot collectUpdateStatus() {
snapshot.currentVersion = detector.installedDriverVersion();
if (QStandardPaths::findExecutable(QStringLiteral("dnf")).isEmpty()) {
- snapshot.message = QStringLiteral("dnf bulunamadi.");
+ snapshot.message = NvidiaUpdater::tr("dnf not found.");
return snapshot;
}
@@ -96,7 +75,7 @@ UpdateStatusSnapshot collectUpdateStatus() {
}
if (snapshot.currentVersion.isEmpty()) {
- snapshot.message = QStringLiteral("Kurulu NVIDIA surucusu bulunamadi.");
+ snapshot.message = NvidiaUpdater::tr("No installed NVIDIA driver found.");
return snapshot;
}
@@ -110,13 +89,13 @@ UpdateStatusSnapshot collectUpdateStatus() {
snapshot.updateAvailable = true;
snapshot.message =
snapshot.latestVersion.isEmpty()
- ? QStringLiteral("Guncelleme bulundu (surum ayrintisi alinamadi).")
- : QStringLiteral("Guncelleme bulundu: %1")
+ ? NvidiaUpdater::tr("Update found (version details unavailable).")
+ : NvidiaUpdater::tr("Update found: %1")
.arg(snapshot.latestVersion);
} else if (checkResult.exitCode == 0) {
- snapshot.message = QStringLiteral("Surucu guncel. Yeni surum bulunamadi.");
+ snapshot.message = NvidiaUpdater::tr("Driver is up to date. No new version found.");
} else {
- snapshot.message = QStringLiteral("Guncelleme kontrolu basarisiz: %1")
+ snapshot.message = NvidiaUpdater::tr("Update check failed: %1")
.arg(checkResult.stderr.trimmed().isEmpty()
? checkResult.stdout.trimmed()
: checkResult.stderr.trimmed());
@@ -141,7 +120,7 @@ void NvidiaUpdater::setBusy(bool busy) {
void NvidiaUpdater::runAsyncTask(const std::function &task) {
if (m_busy) {
emit progressMessage(
- QStringLiteral("Baska bir surucu islemi zaten calisiyor."));
+ tr("Another driver operation is already running."));
return;
}
@@ -174,7 +153,7 @@ void NvidiaUpdater::setAvailableVersions(const QStringList &versions) {
}
QString NvidiaUpdater::detectSessionType() const {
- return detectSessionTypeImpl();
+ return SessionUtil::detectSessionType();
}
QStringList
@@ -196,23 +175,23 @@ bool NvidiaUpdater::finalizeDriverChange(CommandRunner &runner,
runner.runAsRoot(QStringLiteral("akmods"), {QStringLiteral("--force")});
if (!result.success()) {
if (errorMessage != nullptr) {
- *errorMessage = QStringLiteral("Kernel modulu derlenemedi: ") +
- commandError(result, QStringLiteral("bilinmeyen hata"));
+ *errorMessage = tr("Kernel module build failed: ") +
+ commandError(result, tr("unknown error"));
}
return false;
}
if (sessionType == QStringLiteral("wayland")) {
- emit progressMessage(QStringLiteral(
- "Wayland tespit edildi: nvidia-drm.modeset=1 ayari guncelleniyor..."));
+ emit progressMessage(
+ tr("Wayland detected: applying nvidia-drm.modeset=1..."));
result = runner.runAsRoot(QStringLiteral("grubby"),
{QStringLiteral("--update-kernel=ALL"),
QStringLiteral("--args=nvidia-drm.modeset=1")});
if (!result.success()) {
if (errorMessage != nullptr) {
*errorMessage =
- QStringLiteral("Wayland kernel parametresi guncellenemedi: ") +
- commandError(result, QStringLiteral("bilinmeyen hata"));
+ tr("Failed to update the Wayland kernel parameter: ") +
+ commandError(result, tr("unknown error"));
}
return false;
}
@@ -239,8 +218,8 @@ void NvidiaUpdater::refreshAvailableVersions() {
guard->setAvailableVersions(snapshot.availableVersions);
emit guard->progressMessage(
snapshot.availableVersions.isEmpty()
- ? QStringLiteral("Kullanilabilir surum bulunamadi.")
- : QStringLiteral("Kullanilabilir surum sayisi: %1")
+ ? guard->tr("No available versions found.")
+ : guard->tr("Available versions: %1")
.arg(snapshot.availableVersions.size()));
},
Qt::QueuedConnection);
@@ -250,7 +229,7 @@ void NvidiaUpdater::refreshAvailableVersions() {
void NvidiaUpdater::checkForUpdate() {
// TR: Her kontrol denemesinde UI'ye gorunur bir baslangic mesaji gonder.
// EN: Always emit a visible start message for each check request.
- emit progressMessage(QStringLiteral("Guncelleme kontrolu baslatildi..."));
+ emit progressMessage(tr("Starting update check..."));
QPointer guard(this);
runAsyncTask([guard]() {
@@ -318,7 +297,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
[guard]() {
if (guard) {
emit guard->updateFinished(false,
- QStringLiteral("dnf bulunamadi."));
+ guard->tr("dnf not found."));
}
},
Qt::QueuedConnection);
@@ -327,7 +306,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
NvidiaDetector detector;
const QString installedVersion = detector.installedDriverVersion();
- const QString sessionType = detectSessionTypeImpl();
+ const QString sessionType = SessionUtil::detectSessionType();
if (!trimmedVersion.isEmpty() && !knownVersions.contains(trimmedVersion)) {
QMetaObject::invokeMethod(
@@ -336,7 +315,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
if (guard) {
emit guard->updateFinished(
false,
- QStringLiteral("Secilen surum repo listesinde bulunamadi."));
+ guard->tr("Selected version not found in the repository."));
}
},
Qt::QueuedConnection);
@@ -352,10 +331,10 @@ void NvidiaUpdater::applyVersion(const QString &version) {
emit guard->progressMessage(
trimmedVersion.isEmpty()
- ? QStringLiteral(
- "NVIDIA surucusu en son surume guncelleniyor...")
- : QStringLiteral(
- "NVIDIA surucusu secilen surume geciriliyor: %1")
+ ? guard->tr(
+ "Updating NVIDIA driver to the latest version...")
+ : guard->tr(
+ "Switching NVIDIA driver to selected version: %1")
.arg(trimmedVersion));
},
Qt::QueuedConnection);
@@ -374,8 +353,8 @@ void NvidiaUpdater::applyVersion(const QString &version) {
auto result = runner.runAsRoot(QStringLiteral("dnf"), args);
if (!result.success()) {
const QString error =
- QStringLiteral("Guncelleme basarisiz: ") +
- commandError(result, QStringLiteral("bilinmeyen hata"));
+ guard->tr("Update failed: ") +
+ commandError(result, guard->tr("unknown error"));
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -392,7 +371,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
[guard]() {
if (guard) {
emit guard->progressMessage(
- QStringLiteral("Kernel modulu yeniden derleniyor..."));
+ guard->tr("Rebuilding kernel module..."));
}
},
Qt::QueuedConnection);
@@ -414,13 +393,13 @@ void NvidiaUpdater::applyVersion(const QString &version) {
const QString successMessage =
trimmedVersion.isEmpty()
? (installedVersion.isEmpty()
- ? QStringLiteral("En son surum basariyla kuruldu. Lutfen "
- "sistemi yeniden baslatin.")
- : QStringLiteral("Surucu basariyla guncellendi. Lutfen "
- "sistemi yeniden baslatin."))
- : QStringLiteral(
- "Secilen surum basariyla uygulandi. Lutfen sistemi "
- "yeniden baslatin.");
+ ? guard->tr("Latest version installed successfully. "
+ "Please restart the system.")
+ : guard->tr("Driver updated successfully. "
+ "Please restart the system."))
+ : guard->tr(
+ "Selected version applied successfully. "
+ "Please restart the system.");
QMetaObject::invokeMethod(
guard,
diff --git a/src/backend/system/sessionutil.cpp b/src/backend/system/sessionutil.cpp
new file mode 100644
index 0000000..f590f9d
--- /dev/null
+++ b/src/backend/system/sessionutil.cpp
@@ -0,0 +1,31 @@
+#include "sessionutil.h"
+
+#include "commandrunner.h"
+
+#include
+
+namespace SessionUtil {
+
+QString detectSessionType() {
+ const QString envType =
+ qEnvironmentVariable("XDG_SESSION_TYPE").trimmed().toLower();
+ if (!envType.isEmpty())
+ return envType;
+
+ CommandRunner runner;
+ const auto loginctl =
+ runner.run(QStringLiteral("loginctl"),
+ {QStringLiteral("show-session"),
+ qEnvironmentVariable("XDG_SESSION_ID"), QStringLiteral("-p"),
+ QStringLiteral("Type"), QStringLiteral("--value")});
+
+ if (loginctl.success()) {
+ const QString type = loginctl.stdout.trimmed().toLower();
+ if (!type.isEmpty())
+ return type;
+ }
+
+ return QStringLiteral("unknown");
+}
+
+} // namespace SessionUtil
diff --git a/src/backend/system/sessionutil.h b/src/backend/system/sessionutil.h
new file mode 100644
index 0000000..8592ab5
--- /dev/null
+++ b/src/backend/system/sessionutil.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include
+
+namespace SessionUtil {
+
+// XDG_SESSION_TYPE ortam degiskeni veya loginctl
+// uzerinden geçerli oturum turunu tespit eder.
+// "wayland", "x11" veya "unknown" doner.
+QString detectSessionType();
+
+} // namespace SessionUtil
diff --git a/src/qml/components/StatCard.qml b/src/qml/components/StatCard.qml
index 0a5fa77..1ac1d4a 100644
--- a/src/qml/components/StatCard.qml
+++ b/src/qml/components/StatCard.qml
@@ -1,6 +1,6 @@
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
Rectangle {
id: card
From aaaf73a3d8397b35cae7142a82f60255de1d40b2 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:32:00 +0300
Subject: [PATCH 09/22] build: update test build configuration.
---
tests/CMakeLists.txt | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index af17a3b..73fbb19 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -11,6 +11,7 @@ qt_add_executable(test_detector
test_detector.cpp
${CMAKE_SOURCE_DIR}/src/backend/nvidia/detector.cpp
${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
target_include_directories(test_detector PRIVATE
@@ -32,7 +33,11 @@ add_test(NAME test_detector COMMAND test_detector)
# ─── Updater/Version Parser Tests ────────────────────────────────────────────
qt_add_executable(test_updater
test_updater.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/nvidia/updater.cpp
${CMAKE_SOURCE_DIR}/src/backend/nvidia/versionparser.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/nvidia/detector.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
target_include_directories(test_updater PRIVATE
@@ -54,6 +59,7 @@ qt_add_executable(test_monitor
${CMAKE_SOURCE_DIR}/src/backend/monitor/gpumonitor.cpp
${CMAKE_SOURCE_DIR}/src/backend/monitor/rammonitor.cpp
${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
target_include_directories(test_monitor PRIVATE
@@ -124,6 +130,7 @@ qt_add_executable(test_cli
${CMAKE_SOURCE_DIR}/src/backend/monitor/gpumonitor.cpp
${CMAKE_SOURCE_DIR}/src/backend/monitor/rammonitor.cpp
${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
+ ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
target_include_directories(test_cli PRIVATE
From b04a2f5e8bff3cc4906b4aaba0df9ed03565698e Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:36:08 +0300
Subject: [PATCH 10/22] docs: Add development setup instructions to README and
update architecture documentation.
---
README.md | 13 +++++++++++++
docs/ARCHITECTURE.md | 19 +++++++++++++------
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index b9397b7..d71027e 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,19 @@ ro-Control is a native KDE Plasma desktop application built with **C++20** and *
- **Hybrid graphics** — Switch between NVIDIA, Intel, and On-Demand modes
- **PolicyKit integration** — Secure privilege escalation without running as root
+## Development
+
+The easiest way to develop ro-Control rapidly on Fedora is using the provided `dev-watch.sh` script, which automatically rebuilds and restarts the application on file save:
+```bash
+# Setup Fedora dependencies
+./scripts/fedora-bootstrap.sh
+
+# Start the live development watcher
+./scripts/dev-watch.sh
+```
+
+> **Note:** If you launch the tool on a system without a working NVIDIA driver or encounter `libEGL` errors, UI elements may overlap. The `dev-watch.sh` script detects this and automatically exports `QT_XCB_GL_INTEGRATION=none` to force software rendering as a fallback.
+
### 🌍 Internationalization
- Runtime locale loading with Qt translations (`.ts` / `.qm`)
- English source strings with Turkish translation included
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 9e56940..cf9b412 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -66,7 +66,7 @@ Divided into three modules:
## C++ ↔ QML Communication
-Qt's `QObject` system is the bridge. Backend objects are injected at startup from `main.cpp`, and their `Q_PROPERTY` values are then consumed by QML:
+Qt's `QObject` system is the bridge. Backend objects are injected at startup from `main.cpp` into the root QML context, and their `Q_PROPERTY` values are then consumed by QML:
```cpp
// C++ side — gpumonitor.h
@@ -141,22 +141,29 @@ ro-Control/
│ │ ├── nvidia/
│ │ │ ├── detector.h / detector.cpp
│ │ │ ├── installer.h / installer.cpp
-│ │ │ └── updater.h / updater.cpp
+│ │ │ ├── updater.h / updater.cpp
+│ │ │ └── versionparser.h / versionparser.cpp
│ │ ├── monitor/
│ │ │ ├── gpumonitor.h / gpumonitor.cpp
│ │ │ ├── cpumonitor.h / cpumonitor.cpp
│ │ │ └── rammonitor.h / rammonitor.cpp
-│ │ └── system/
-│ │ ├── commandrunner.h / commandrunner.cpp
-│ │ ├── dnfmanager.h / dnfmanager.cpp
-│ │ └── polkit.h / polkit.cpp
+│ │ ├── system/
+│ │ │ ├── commandrunner.h / commandrunner.cpp
+│ │ │ ├── dnfmanager.h / dnfmanager.cpp
+│ │ │ ├── polkit.h / polkit.cpp
+│ │ │ └── sessionutil.h / sessionutil.cpp
+│ │ └── cli/
+│ │ └── cli.h / cli.cpp
│ ├── qml/
│ │ ├── assets/
│ │ │ ├── ro-control-logo.png
│ │ │ └── ro-control-logo.svg
│ │ ├── components/
+│ │ │ ├── InfoBadge.qml
+│ │ │ ├── SectionPanel.qml
│ │ │ ├── SidebarMenu.qml
│ │ │ ├── StatCard.qml
+│ │ │ ├── StatusBanner.qml
│ │ │ └── qmldir
│ │ ├── pages/
│ │ │ ├── DriverPage.qml
From a0a06b53dc9d42bbf25954a470dfb3c7fecdea3c Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:44:11 +0300
Subject: [PATCH 11/22] chore: update test build configuration.
---
tests/CMakeLists.txt | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 73fbb19..90d447b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,10 +1,5 @@
find_package(Qt6 REQUIRED COMPONENTS Test)
-set(RO_CONTROL_TEST_COMPILE_DEFINITIONS
- RO_CONTROL_POLICY_ID="${RO_CONTROL_POLICY_ID}"
- RO_CONTROL_HELPER_BUILD_PATH="${RO_CONTROL_HELPER_BUILD_PATH}"
- RO_CONTROL_HELPER_INSTALL_PATH="${RO_CONTROL_HELPER_INSTALL_PATH}"
-)
# ─── Detector Tests ──────────────────────────────────────────────────────────
qt_add_executable(test_detector
@@ -20,7 +15,9 @@ target_include_directories(test_detector PRIVATE
)
target_compile_definitions(test_detector PRIVATE
- ${RO_CONTROL_TEST_COMPILE_DEFINITIONS}
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
)
target_link_libraries(test_detector PRIVATE
@@ -45,6 +42,12 @@ target_include_directories(test_updater PRIVATE
${CMAKE_SOURCE_DIR}/src/backend
)
+target_compile_definitions(test_updater PRIVATE
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
+)
+
target_link_libraries(test_updater PRIVATE
Qt6::Core
Qt6::Test
@@ -68,7 +71,9 @@ target_include_directories(test_monitor PRIVATE
)
target_compile_definitions(test_monitor PRIVATE
- ${RO_CONTROL_TEST_COMPILE_DEFINITIONS}
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
)
target_link_libraries(test_monitor PRIVATE
@@ -92,7 +97,9 @@ target_include_directories(test_system_integration PRIVATE
)
target_compile_definitions(test_system_integration PRIVATE
- ${RO_CONTROL_TEST_COMPILE_DEFINITIONS}
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
)
target_link_libraries(test_system_integration PRIVATE
@@ -108,8 +115,10 @@ qt_add_executable(test_metadata
)
target_compile_definitions(test_metadata PRIVATE
- ${RO_CONTROL_TEST_COMPILE_DEFINITIONS}
- RO_CONTROL_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
+ "RO_CONTROL_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\""
)
target_link_libraries(test_metadata PRIVATE
@@ -139,7 +148,9 @@ target_include_directories(test_cli PRIVATE
)
target_compile_definitions(test_cli PRIVATE
- ${RO_CONTROL_TEST_COMPILE_DEFINITIONS}
+ "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
+ "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
+ "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
)
target_link_libraries(test_cli PRIVATE
From a774304ec96781463430765d47f2abc6a764b799 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:47:05 +0300
Subject: [PATCH 12/22] refactor: Adjust component layouts by refining implicit
heights, anchors, margins, and spacing.
---
src/qml/Main.qml | 6 ++++--
src/qml/components/StatCard.qml | 8 +++++---
src/qml/components/StatusBanner.qml | 9 +++++++--
src/qml/pages/DriverPage.qml | 4 +++-
4 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/src/qml/Main.qml b/src/qml/Main.qml
index ba299e3..15352a6 100644
--- a/src/qml/Main.qml
+++ b/src/qml/Main.qml
@@ -129,11 +129,13 @@ ApplicationWindow {
color: root.theme.card
border.width: 1
border.color: root.theme.border
- implicitHeight: heroLayout.implicitHeight + 32
+ implicitHeight: heroLayout.implicitHeight + 40
RowLayout {
id: heroLayout
- anchors.fill: parent
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
anchors.margins: 20
spacing: 18
diff --git a/src/qml/components/StatCard.qml b/src/qml/components/StatCard.qml
index 1ac1d4a..a610236 100644
--- a/src/qml/components/StatCard.qml
+++ b/src/qml/components/StatCard.qml
@@ -16,7 +16,7 @@ Rectangle {
color: emphasized ? Qt.tint(theme.cardStrong, "#15000000") : theme.card
border.width: 1
border.color: theme.border
- implicitHeight: cardLayout.implicitHeight + 30
+ implicitHeight: cardLayout.implicitHeight + 36
Rectangle {
anchors.top: parent.top
@@ -30,9 +30,11 @@ Rectangle {
ColumnLayout {
id: cardLayout
- anchors.fill: parent
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
anchors.margins: 18
- spacing: 10
+ spacing: 8
Label {
text: card.title
diff --git a/src/qml/components/StatusBanner.qml b/src/qml/components/StatusBanner.qml
index 4ee9781..4bace54 100644
--- a/src/qml/components/StatusBanner.qml
+++ b/src/qml/components/StatusBanner.qml
@@ -28,10 +28,15 @@ Rectangle {
border.color: borderTone
visible: text.length > 0
+ implicitHeight: bannerLayout.implicitHeight + 24
+
RowLayout {
- anchors.fill: parent
+ id: bannerLayout
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
anchors.margins: 12
- spacing: 10
+ spacing: 12
Rectangle {
width: 10
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index c2e86f8..4aff5b9 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -137,7 +137,9 @@ Item {
Label {
id: verificationText
- anchors.fill: parent
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
anchors.margins: 12
text: nvidiaDetector.verificationReport
wrapMode: Text.Wrap
From 037d44e4a495389dfbfb9e140cd08a9a22998bb6 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:51:25 +0300
Subject: [PATCH 13/22] refactor: Update QML layouts by replacing anchor-based
positioning with explicit x, y, width properties and correcting ScrollView
content width references.
---
src/qml/Main.qml | 7 +++----
src/qml/components/SectionPanel.qml | 7 +++----
src/qml/pages/DriverPage.qml | 10 +++++-----
src/qml/pages/MonitorPage.qml | 3 ++-
src/qml/pages/SettingsPage.qml | 3 ++-
5 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/qml/Main.qml b/src/qml/Main.qml
index 15352a6..9df0974 100644
--- a/src/qml/Main.qml
+++ b/src/qml/Main.qml
@@ -133,10 +133,9 @@ ApplicationWindow {
RowLayout {
id: heroLayout
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: 20
+ x: 20
+ y: 20
+ width: parent.width - 40
spacing: 18
ColumnLayout {
diff --git a/src/qml/components/SectionPanel.qml b/src/qml/components/SectionPanel.qml
index f93ed12..6a16e12 100644
--- a/src/qml/components/SectionPanel.qml
+++ b/src/qml/components/SectionPanel.qml
@@ -18,10 +18,9 @@ Rectangle {
ColumnLayout {
id: innerColumn
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.margins: 18
+ x: 18
+ y: 18
+ width: parent.width - 36
spacing: 14
ColumnLayout {
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 4aff5b9..79a715d 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -14,12 +14,13 @@ Item {
property string bannerTone: "info"
ScrollView {
+ id: pageScroll
anchors.fill: parent
clip: true
contentWidth: availableWidth
ColumnLayout {
- width: parent.availableWidth
+ width: pageScroll.availableWidth
spacing: page.compactMode ? 12 : 16
StatusBanner {
@@ -137,10 +138,9 @@ Item {
Label {
id: verificationText
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: 12
+ x: 12
+ y: 12
+ width: parent.width - 24
text: nvidiaDetector.verificationReport
wrapMode: Text.Wrap
color: page.theme.text
diff --git a/src/qml/pages/MonitorPage.qml b/src/qml/pages/MonitorPage.qml
index 5e77233..a7fb3aa 100644
--- a/src/qml/pages/MonitorPage.qml
+++ b/src/qml/pages/MonitorPage.qml
@@ -11,12 +11,13 @@ Item {
property bool showAdvancedInfo: true
ScrollView {
+ id: pageScroll
anchors.fill: parent
clip: true
contentWidth: availableWidth
ColumnLayout {
- width: parent.availableWidth
+ width: pageScroll.availableWidth
spacing: page.compactMode ? 12 : 16
GridLayout {
diff --git a/src/qml/pages/SettingsPage.qml b/src/qml/pages/SettingsPage.qml
index e61ab21..c4fe16e 100644
--- a/src/qml/pages/SettingsPage.qml
+++ b/src/qml/pages/SettingsPage.qml
@@ -11,12 +11,13 @@ Item {
property bool showAdvancedInfo: true
ScrollView {
+ id: pageScroll
anchors.fill: parent
clip: true
contentWidth: availableWidth
ColumnLayout {
- width: parent.availableWidth
+ width: pageScroll.availableWidth
spacing: settingsPage.compactMode ? 12 : 16
GridLayout {
From 35311ead6d0d54663dfd99a6cce2279a1fcc7e3c Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:08:08 +0300
Subject: [PATCH 14/22] feat: Introduce system capability probing and enhance
command runner with path resolution and environment overrides, plus improve
release workflow to build RPMs.
---
.github/workflows/ci.yml | 10 +-
.github/workflows/release.yml | 81 +-
CMakeLists.txt | 33 +-
i18n/ro-control_en.ts | 1239 +++++++++++++++++++++--
i18n/ro-control_tr.ts | 1245 ++++++++++++++++++++++--
src/backend/nvidia/detector.cpp | 30 +-
src/backend/nvidia/installer.cpp | 38 +-
src/backend/nvidia/updater.cpp | 24 +-
src/backend/system/capabilityprobe.cpp | 41 +
src/backend/system/capabilityprobe.h | 19 +
src/backend/system/commandrunner.cpp | 21 +-
src/backend/system/commandrunner.h | 2 +
src/backend/system/dnfmanager.cpp | 5 +-
src/backend/system/polkit.cpp | 4 +-
src/qml/pages/DriverPage.qml | 107 +-
tests/CMakeLists.txt | 98 +-
tests/test_system_integration.cpp | 50 +
17 files changed, 2720 insertions(+), 327 deletions(-)
create mode 100644 src/backend/system/capabilityprobe.cpp
create mode 100644 src/backend/system/capabilityprobe.h
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 30cf445..122a4ec 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,8 +17,12 @@ jobs:
build:
name: Build & Test
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ fedora: [41, 42]
container:
- image: fedora:41
+ image: fedora:${{ matrix.fedora }}
steps:
- name: Checkout repository
@@ -52,7 +56,7 @@ jobs:
run: cmake --build build --parallel
- name: Run tests
- run: cd build && ctest --output-on-failure
+ run: ctest --test-dir build --output-on-failure
- name: Check formatting (clang-format)
run: |
@@ -63,7 +67,7 @@ jobs:
name: RPM Build Check
runs-on: ubuntu-latest
container:
- image: fedora:41
+ image: fedora:42
steps:
- name: Checkout repository
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e23f597..43e09cf 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,17 +7,36 @@ on:
workflow_dispatch:
jobs:
- release:
- name: Create GitHub Release
+ artifacts:
+ name: Build Release Artifacts
runs-on: ubuntu-latest
+ container:
+ image: fedora:42
permissions:
- contents: write
+ contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
+ - name: Install packaging dependencies
+ run: |
+ dnf install -y \
+ git \
+ rpm-build \
+ cmake \
+ extra-cmake-modules \
+ gcc-c++ \
+ ninja-build \
+ qt6-qtbase-devel \
+ qt6-qtbase-private-devel \
+ qt6-qtdeclarative-devel \
+ qt6-qttools-devel \
+ qt6-qtwayland-devel \
+ kf6-qqc2-desktop-style \
+ polkit-devel
+
- name: Derive release version
run: |
VERSION="${GITHUB_REF_NAME#v}"
@@ -29,10 +48,62 @@ jobs:
git archive --format=tar.gz --prefix="${PREFIX}/" --output="${PREFIX}.tar.gz" "${GITHUB_SHA}"
git archive --format=zip --prefix="${PREFIX}/" --output="${PREFIX}.zip" "${GITHUB_SHA}"
+ - name: Build RPM artifacts
+ run: |
+ mkdir -p ~/rpmbuild/SOURCES ~/rpmbuild/SPECS
+ cp "ro-control-${VERSION}.tar.gz" "${HOME}/rpmbuild/SOURCES/"
+ cp packaging/rpm/ro-control.spec "${HOME}/rpmbuild/SPECS/ro-control.spec"
+ rpmbuild -ba "${HOME}/rpmbuild/SPECS/ro-control.spec" \
+ --define "_topdir ${HOME}/rpmbuild"
+ cp ~/rpmbuild/SRPMS/*.src.rpm .
+ cp ~/rpmbuild/RPMS/*/*.rpm .
+
+ - name: Generate checksums
+ run: |
+ sha256sum \
+ "ro-control-${VERSION}.tar.gz" \
+ "ro-control-${VERSION}.zip" \
+ *.rpm \
+ *.src.rpm > "ro-control-${VERSION}-SHA256SUMS.txt"
+
+ - name: Upload release artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ro-control-release-${{ env.VERSION }}
+ path: |
+ ro-control-${{ env.VERSION }}.tar.gz
+ ro-control-${{ env.VERSION }}.zip
+ *.rpm
+ *.src.rpm
+ ro-control-${{ env.VERSION }}-SHA256SUMS.txt
+
+ release:
+ name: Create GitHub Release
+ runs-on: ubuntu-latest
+ needs: artifacts
+
+ permissions:
+ contents: write
+
+ steps:
+ - name: Derive release version
+ run: |
+ VERSION="${GITHUB_REF_NAME#v}"
+ echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
+
+ - name: Download release artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: ro-control-release-${{ env.VERSION }}
+ path: dist
+
- name: Publish release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
- ro-control-${{ env.VERSION }}.tar.gz
- ro-control-${{ env.VERSION }}.zip
+ dist/ro-control-${{ env.VERSION }}.tar.gz
+ dist/ro-control-${{ env.VERSION }}.zip
+ dist/*.rpm
+ dist/*.src.rpm
+ dist/ro-control-${{ env.VERSION }}-SHA256SUMS.txt
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1311434..ad9d8b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,15 +71,41 @@ set(BACKEND_SOURCES
src/backend/system/dnfmanager.cpp
src/backend/system/commandrunner.cpp
src/backend/system/sessionutil.cpp
+ src/backend/system/capabilityprobe.cpp
)
set(APP_SOURCES
src/main.cpp
src/cli/cli.cpp
- ${BACKEND_SOURCES}
)
# ─── QML Resources ────────────────────────────────────────────────────────────
+add_library(ro-control-backend STATIC ${BACKEND_SOURCES})
+
+target_link_libraries(ro-control-backend PUBLIC
+ Qt6::Core
+ Qt6::DBus
+)
+
+target_compile_options(ro-control-backend PRIVATE
+ -Wall
+ -Wextra
+ -Wpedantic
+ $<$:-g -O0>
+ $<$:-O2>
+)
+
+target_include_directories(ro-control-backend PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/backend
+)
+
+target_compile_definitions(ro-control-backend PUBLIC
+ RO_CONTROL_POLICY_ID="${RO_CONTROL_POLICY_ID}"
+ RO_CONTROL_HELPER_BUILD_PATH="${RO_CONTROL_HELPER_BUILD_PATH}"
+ RO_CONTROL_HELPER_INSTALL_PATH="${RO_CONTROL_HELPER_INSTALL_PATH}"
+)
+
qt_add_executable(ro-control ${APP_SOURCES})
set_source_files_properties(src/qml/assets/ro-control-logo.svg PROPERTIES
@@ -122,11 +148,10 @@ endif()
# ─── Link Qt Libraries ────────────────────────────────────────────────────────
target_link_libraries(ro-control PRIVATE
- Qt6::Core
Qt6::Quick
Qt6::QuickControls2
Qt6::Widgets
- Qt6::DBus
+ ro-control-backend
)
# ─── Compiler Warnings ────────────────────────────────────────────────────────
@@ -156,8 +181,6 @@ target_include_directories(ro-control PRIVATE
target_compile_definitions(ro-control PRIVATE
RO_CONTROL_POLICY_ID="${RO_CONTROL_POLICY_ID}"
- RO_CONTROL_HELPER_BUILD_PATH="${RO_CONTROL_HELPER_BUILD_PATH}"
- RO_CONTROL_HELPER_INSTALL_PATH="${RO_CONTROL_HELPER_INSTALL_PATH}"
)
configure_file(
diff --git a/i18n/ro-control_en.ts b/i18n/ro-control_en.ts
index ebcc060..30d4826 100644
--- a/i18n/ro-control_en.ts
+++ b/i18n/ro-control_en.ts
@@ -1,117 +1,1186 @@
+
DriverPage
- Driver ManagementDriver Management
- GPU: GPU:
- Not detectedNot detected
- Active driver: Active driver:
- Driver version: Driver version:
- NoneNone
- Secure Boot: Secure Boot:
- EnabledEnabled
- Disabled / UnknownDisabled / Unknown
- Session type: Session type:
- For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
- For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
- I accept the license / agreement termsI accept the license / agreement terms
- Install Proprietary DriverInstall Proprietary Driver
- Install Open-Source Driver (Nouveau)Install Open-Source Driver (Nouveau)
- Deep CleanDeep Clean
- Check for UpdatesCheck for Updates
- Apply UpdateApply Update
- Latest version: Latest version:
- RescanRescan
- Installed NVIDIA version: Installed NVIDIA version:
+
+ Driver Management
+ Driver Management
+
+
+ GPU:
+ GPU:
+
+
+ Not detected
+ Not detected
+
+
+ Active driver:
+ Active driver:
+
+
+ Driver version:
+ Driver version:
+
+
+
+
+ None
+ None
+
+
+ Secure Boot:
+ Secure Boot:
+
+
+
+ GPU Detection
+
+
+
+
+ Detected
+
+
+
+
+ Missing
+
+
+
+
+ No NVIDIA GPU was detected on this system.
+
+
+
+
+ Active Driver
+
+
+
+
+ Session:
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Installed Version
+
+
+
+
+ Latest available:
+
+
+
+
+ No pending package update detected.
+
+
+
+
+ Secure Boot
+
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unsigned kernel modules may require manual signing.
+
+
+
+
+ No Secure Boot blocker is currently detected.
+
+
+
+
+ Verification
+
+
+
+
+ Review driver prerequisites before changing packages.
+
+
+
+
+ GPU Ready
+
+
+
+
+ GPU Missing
+
+
+
+
+ Wayland Session
+
+
+
+
+ X11 / Other Session
+
+
+
+
+ Nouveau Active
+
+
+
+
+ Nouveau Inactive
+
+
+
+
+ Kernel Module Loaded
+
+
+
+
+ Kernel Module Missing
+
+
+
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
+
+
+
+
+ X11 sessions require matching userspace packages after install or update.
+
+
+
+
+ Driver Actions
+
+
+
+
+ Use guided actions to install, switch or remove the current stack.
+
+
+
+
+ I accept the detected license / agreement terms
+
+
+
+
+ Install Proprietary
+
+
+
+
+ Install Nouveau
+
+
+
+
+ Remove Driver
+
+
+
+
+ Apply Latest
+
+
+
+
+ Apply Selected
+
+
+
+
+ Repository versions loaded:
+
+
+
+
+ No repository version list has been loaded yet.
+
+
+
+
+ Activity Log
+
+
+
+
+ Operation output is streamed here in real time.
+
+
+
+
+ Clear Log
+
+
+
+ Session type:
+ Session type:
+
+
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+
+
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+
+
+ I accept the license / agreement terms
+ I accept the license / agreement terms
+
+
+ Install Proprietary Driver
+ Install Proprietary Driver
+
+
+ Install Open-Source Driver (Nouveau)
+ Install Open-Source Driver (Nouveau)
+
+
+
+ Deep Clean
+ Deep Clean
+
+
+
+ Rescan System
+
+
+
+
+ Update Center
+
+
+
+
+ Check the repository version and pin a specific build when required.
+
+
+
+
+ Installed:
+
+
+
+
+ Update Available
+
+
+
+
+ Up to Date
+
+
+
+
+ Check for Updates
+ Check for Updates
+
+
+ Apply Update
+ Apply Update
+
+
+ Latest version:
+ Latest version:
+
+
+ Rescan
+ Rescan
+
+
+ Installed NVIDIA version:
+ Installed NVIDIA version:
+ Main
- ro-Controlro-Control
- Theme: System (Dark)Theme: System (Dark)
- Theme: System (Light)Theme: System (Light)
- DriverDriver
- MonitorMonitor
- SettingsSettings
+
+
+ ro-Control
+ ro-Control
+
+
+
+ Driver Control Center
+
+
+
+
+ System Monitor
+
+
+
+
+ Preferences
+
+
+
+
+ Install, verify and update NVIDIA drivers with guided system checks.
+
+
+
+
+ Track live CPU, GPU and memory telemetry in one place.
+
+
+
+
+ Tune the interface and review diagnostic context before support work.
+
+
+
+
+ System Dark
+ System Dark
+
+
+
+ System Light
+ System Light
+
+
+
+ Compact Layout
+
+
+
+
+ Comfort Layout
+
+
+
+ Theme: System (Dark)
+ Theme: System (Dark)
+
+
+ Theme: System (Light)
+ Theme: System (Light)
+
+
+ Driver
+ Driver
+
+
+ Monitor
+ Monitor
+
+
+ Settings
+ Settings
+ MonitorPage
- System MonitoringSystem Monitoring
- CPUCPU
- Usage: Usage:
- CPU data unavailableCPU data unavailable
- Temperature: Temperature:
- GPU (NVIDIA)GPU (NVIDIA)
- NVIDIA GPUNVIDIA GPU
- Failed to read data via nvidia-smiFailed to read data via nvidia-smi
- Load: Load:
- VRAM: VRAM:
- RAMRAM
- RAM data unavailableRAM data unavailable
- RefreshRefresh
- Refresh interval: Refresh interval:
+
+ System Monitoring
+ System Monitoring
+
+
+
+ CPU Load
+
+
+
+
+
+
+ Unavailable
+
+
+
+
+ CPU telemetry is currently unavailable.
+
+
+
+
+ GPU Load
+
+
+
+
+ nvidia-smi did not return live GPU telemetry.
+
+
+
+
+ Memory Usage
+
+
+
+
+ Used:
+
+
+
+
+ RAM telemetry is currently unavailable.
+
+
+
+
+ Live Resource Curves
+
+
+
+
+ Quick pulse view for the most important machine resources.
+
+
+
+
+ CPU
+ CPU
+
+
+
+ GPU
+
+
+
+
+ Health Summary
+
+
+
+
+ Fast interpretation of the raw telemetry values.
+
+
+
+
+ CPU Busy
+
+
+
+
+ CPU Stable
+
+
+
+
+ GPU Online
+
+
+
+
+ GPU Telemetry Missing
+
+
+
+
+ Memory Pressure
+
+
+
+
+ Memory Stable
+
+
+
+
+ GPU temperature:
+
+
+
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.
+
+
+
+
+ Detailed Signals
+
+
+
+
+ Expanded raw values for support and diagnostics.
+
+
+
+
+ CPU Temperature
+
+
+
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ GPU Temperature
+
+
+
+
+ VRAM
+
+
+
+
+ RAM Footprint
+
+
+
+
+ Actions
+
+
+
+
+ Trigger a manual refresh when you need a fresh sample.
+
+
+
+
+ Refresh Telemetry
+
+
+
+
+ NVIDIA Path OK
+
+
+
+
+ Check NVIDIA Path
+
+
+
+ Usage:
+ Usage:
+
+
+ CPU data unavailable
+ CPU data unavailable
+
+
+
+ Temperature:
+ Temperature:
+
+
+ GPU (NVIDIA)
+ GPU (NVIDIA)
+
+
+
+ NVIDIA GPU
+ NVIDIA GPU
+
+
+ Failed to read data via nvidia-smi
+ Failed to read data via nvidia-smi
+
+
+ Load:
+ Load:
+
+
+ VRAM:
+ VRAM:
+
+
+
+ RAM
+ RAM
+
+
+ RAM data unavailable
+ RAM data unavailable
+
+
+ Refresh
+ Refresh
+
+
+
+ Refresh interval:
+ Refresh interval:
+ NvidiaDetector
- Proprietary (NVIDIA)Proprietary (NVIDIA)
- Open Source (Nouveau)Open Source (Nouveau)
- Not Installed / UnknownNot Installed / Unknown
- GPU: %1
+
+
+ Proprietary (NVIDIA)
+ Proprietary (NVIDIA)
+
+
+
+ Open Source (Nouveau)
+ Open Source (Nouveau)
+
+
+
+ Not Installed / Unknown
+ Not Installed / Unknown
+
+
+
+
+ None
+ None
+
+
+
+ GPU: %1
Driver Version: %2
Secure Boot: %3
Session: %4
NVIDIA Module: %5
-Nouveau: %6GPU: %1
+Nouveau: %6
+ GPU: %1
Driver Version: %2
Secure Boot: %3
Session: %4
NVIDIA Module: %5
-Nouveau: %6
- UnknownUnknown
- LoadedLoaded
- Not loadedNot loaded
- ActiveActive
- InactiveInactive
+Nouveau: %6
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled
+
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Loaded
+ Loaded
+
+
+
+ Not loaded
+ Not loaded
+
+
+
+ Active
+ Active
+
+
+
+ Inactive
+ Inactive
+ NvidiaInstaller
- You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
- License agreement acceptance is required before installation.License agreement acceptance is required before installation.
- Checking RPM Fusion repositories...Checking RPM Fusion repositories...
- Platform version could not be detected.Platform version could not be detected.
- Failed to enable RPM Fusion repositories: Failed to enable RPM Fusion repositories:
- Installing the proprietary NVIDIA driver (akmod-nvidia)...Installing the proprietary NVIDIA driver (akmod-nvidia)...
- Installation failed: Installation failed:
- Building the kernel module (akmods --force)...Building the kernel module (akmods --force)...
- The proprietary NVIDIA driver was installed successfully. Please restart the system.The proprietary NVIDIA driver was installed successfully. Please restart the system.
- Switching to the open-source driver...Switching to the open-source driver...
- Failed to remove proprietary packages: Failed to remove proprietary packages:
- Open-source driver installation failed: Open-source driver installation failed:
- The open-source driver (Nouveau) was installed. Please restart the system.The open-source driver (Nouveau) was installed. Please restart the system.
- Removing the NVIDIA driver...Removing the NVIDIA driver...
- Driver removed successfully.Driver removed successfully.
- Removal failed: Removal failed:
- Cleaning legacy driver leftovers...Cleaning legacy driver leftovers...
- Deep clean completed.Deep clean completed.
- Wayland detected: applying nvidia-drm.modeset=1...Wayland detected: applying nvidia-drm.modeset=1...
- Failed to apply the Wayland kernel parameter: Failed to apply the Wayland kernel parameter:
- X11 detected: checking NVIDIA userspace packages...X11 detected: checking NVIDIA userspace packages...
- Failed to install the X11 NVIDIA package: Failed to install the X11 NVIDIA package:
+
+
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+
+
+
+ License agreement acceptance is required before installation.
+ License agreement acceptance is required before installation.
+
+
+
+ Checking RPM Fusion repositories...
+ Checking RPM Fusion repositories...
+
+
+
+ Platform version could not be detected.
+ Platform version could not be detected.
+
+
+
+ Failed to enable RPM Fusion repositories:
+ Failed to enable RPM Fusion repositories:
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+
+ Installation failed:
+ Installation failed:
+
+
+
+ Building the kernel module (akmods --force)...
+ Building the kernel module (akmods --force)...
+
+
+
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+
+ Failed to remove proprietary packages:
+ Failed to remove proprietary packages:
+
+
+
+ Open-source driver installation failed:
+ Open-source driver installation failed:
+
+
+
+ The open-source driver (Nouveau) was installed. Please restart the system.
+ The open-source driver (Nouveau) was installed. Please restart the system.
+
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+ Driver removed successfully.
+ Driver removed successfully.
+
+
+
+ Removal failed:
+ Removal failed:
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+ Deep clean failed:
+
+
+
+
+ DNF cache cleanup failed:
+
+
+
+
+ Deep clean completed.
+ Deep clean completed.
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Another driver operation is already running.
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Legacy NVIDIA cleanup completed.
+
+
+
+
+ Failed to apply the Wayland kernel parameter:
+ Failed to apply the Wayland kernel parameter:
+
+
+
+ X11 detected: checking NVIDIA userspace packages...
+ X11 detected: checking NVIDIA userspace packages...
+
+
+
+ Failed to install the X11 NVIDIA package:
+ Failed to install the X11 NVIDIA package:
+ NvidiaUpdater
- Updating the NVIDIA driver...Updating the NVIDIA driver...
- Update failed: Update failed:
- Rebuilding the kernel module...Rebuilding the kernel module...
- Wayland detected: refreshing nvidia-drm.modeset=1...Wayland detected: refreshing nvidia-drm.modeset=1...
- Driver updated successfully. Please restart the system.Driver updated successfully. Please restart the system.
+
+ Updating the NVIDIA driver...
+ Updating the NVIDIA driver...
+
+
+
+ Update failed:
+ Update failed:
+
+
+ Rebuilding the kernel module...
+ Rebuilding the kernel module...
+
+
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+
+
+
+ Driver updated successfully. Please restart the system.
+ Driver updated successfully. Please restart the system.
+
+
+
+
+ dnf not found.
+
+
+
+
+ No installed NVIDIA driver found.
+
+
+
+
+ Update found (version details unavailable).
+
+
+
+
+ Update found: %1
+
+
+
+
+ Driver is up to date. No new version found.
+
+
+
+
+ Update check failed: %1
+
+
+
+
+ Another driver operation is already running.
+
+
+
+
+ Kernel module build failed:
+
+
+
+
+
+
+ unknown error
+
+
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Failed to update the Wayland kernel parameter:
+
+
+
+
+ No available versions found.
+
+
+
+
+ Available versions: %1
+
+
+
+
+ Starting update check...
+
+
+
+
+ Selected version not found in the repository.
+
+
+
+
+ Updating NVIDIA driver to the latest version...
+
+
+
+
+ Switching NVIDIA driver to selected version: %1
+
+
+
+
+ Rebuilding kernel module...
+
+
+
+
+ Latest version installed successfully. Please restart the system.
+
+
+
+
+ Selected version applied successfully. Please restart the system.
+
+ SettingsPage
- SettingsSettings
- AboutAbout
- Application: Application:
- Theme: Theme:
- System DarkSystem Dark
- System LightSystem Light
+
+ Settings
+ Settings
+
+
+
+ Interface
+
+
+
+
+ Tune the shell density and how much operational detail the app exposes.
+
+
+
+
+ Compact layout
+
+
+
+
+ Reduces spacing to fit more information on screen.
+
+
+
+
+ Show advanced diagnostics
+
+
+
+
+ Shows verification reports and expanded monitor metrics.
+
+
+
+
+ System Dark Theme
+
+
+
+
+ System Light Theme
+
+
+
+
+ Compact Active
+
+
+
+
+ Comfort Active
+
+
+
+
+ Diagnostics
+
+
+
+
+ Useful runtime context before filing issues or performing support work.
+
+
+
+
+ Application
+
+
+
+
+ GPU
+
+
+
+
+ Not detected
+ Not detected
+
+
+
+ Driver
+ Driver
+
+
+
+ Session
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Use the Driver page to refresh detection before copying any diagnostic context.
+
+
+
+
+ Workflow Guidance
+
+
+
+
+ Recommended order of operations when changing drivers.
+
+
+
+
+ 1. Verify GPU detection and session type.
+2. Install or switch the driver stack.
+3. Check repository updates.
+4. Restart after successful package operations.
+
+
+
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.
+
+
+
+
+ No Secure Boot blocker is currently reported by the detector.
+
+
+
+
+ About
+ About
+
+
+
+ Project identity and current shell mode.
+
+
+
+
+ Application:
+ Application:
+
+
+
+ Theme:
+ Theme:
+
+
+
+ System Dark
+ System Dark
+
+
+
+ System Light
+ System Light
+
+
+
+ Layout density:
+
+
+
+
+ Compact
+
+
+
+
+ Comfort
+
+
+
+
+ Advanced diagnostics:
+
+
+
+
+ Visible
+
+
+
+
+ Hidden
+
+
+
+
+ SidebarMenu
+
+
+ Driver Management
+ Driver Management
+
+
+
+ System Monitoring
+ System Monitoring
+
+
+
+ Settings
+ Settings
+
+
+
+ ro-Control
+ ro-Control
+
diff --git a/i18n/ro-control_tr.ts b/i18n/ro-control_tr.ts
index b25da92..988de29 100644
--- a/i18n/ro-control_tr.ts
+++ b/i18n/ro-control_tr.ts
@@ -1,117 +1,1188 @@
-
+
DriverPage
- Driver ManagementSürücü Yönetimi
- GPU: GPU:
- Not detectedTespit edilmedi
- Active driver: Aktif sürücü:
- Driver version: Sürücü sürümü:
- NoneYok
- Secure Boot: Secure Boot:
- EnabledAçık
- Disabled / UnknownKapalı / Bilinmiyor
- Session type: Oturum türü:
- For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.Wayland oturumlarında nvidia-drm.modeset=1 otomatik uygulanır.
- For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.X11 oturumlarında xorg-x11-drv-nvidia paketi doğrulanır ve kurulur.
- I accept the license / agreement termsLisans / sözleşme koşullarını kabul ediyorum
- Install Proprietary DriverKapalı Kaynak Sürücüyü Kur
- Install Open-Source Driver (Nouveau)Açık Kaynak Sürücüyü Kur (Nouveau)
- Deep CleanDerin Temizlik
- Check for UpdatesGüncellemeleri Kontrol Et
- Apply UpdateGüncellemeyi Uygula
- Latest version: En son sürüm:
- RescanYeniden Tara
- Installed NVIDIA version: Kurulu NVIDIA sürümü:
+
+ Driver Management
+ Sürücü Yönetimi
+
+
+ GPU:
+ GPU:
+
+
+ Not detected
+ Tespit edilmedi
+
+
+ Active driver:
+ Aktif sürücü:
+
+
+ Driver version:
+ Sürücü sürümü:
+
+
+
+
+ None
+ Yok
+
+
+ Secure Boot:
+ Secure Boot:
+
+
+
+ GPU Detection
+ GPU Tespiti
+
+
+
+ Detected
+ Tespit Edildi
+
+
+
+ Missing
+ Bulunamadı
+
+
+
+ No NVIDIA GPU was detected on this system.
+ Bu sistemde NVIDIA GPU tespit edilemedi.
+
+
+
+ Active Driver
+ Aktif Sürücü
+
+
+
+ Session:
+ Oturum:
+
+
+
+ Unknown
+ Bilinmiyor
+
+
+
+ Installed Version
+ Kurulu Sürüm
+
+
+
+ Latest available:
+ En son sürüm:
+
+
+
+ No pending package update detected.
+ Bekleyen paket güncellemesi bulunamadı.
+
+
+
+ Secure Boot
+ Güvenli Önyükleme (Secure Boot)
+
+
+
+ Enabled
+ Açık
+
+
+
+ Disabled / Unknown
+ Kapalı / Bilinmiyor
+
+
+
+ Unsigned kernel modules may require manual signing.
+ İmzasız kernel modüllerinin manuel olarak imzalanması gerekebilir.
+
+
+
+ No Secure Boot blocker is currently detected.
+ Şu anda herhangi bir Secure Boot engeli tespit edilmedi.
+
+
+
+ Verification
+ Doğrulama
+
+
+
+ Review driver prerequisites before changing packages.
+ Paketleri değiştirmeden önce sürücü ön koşullarını gözden geçirin.
+
+
+
+ GPU Ready
+ GPU Hazır
+
+
+
+ GPU Missing
+ GPU Bulunamadı
+
+
+
+ Wayland Session
+ Wayland Oturumu
+
+
+
+ X11 / Other Session
+ X11 / Diğer Oturum
+
+
+
+ Nouveau Active
+ Nouveau Aktif
+
+
+
+ Nouveau Inactive
+ Nouveau Pasif
+
+
+
+ Kernel Module Loaded
+ Kernel Modülü Yüklü
+
+
+
+ Kernel Module Missing
+ Kernel Modülü Eksik
+
+
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
+ Wayland oturumları otomatik olarak nvidia-drm.modeset=1 çekirdek argümanına ihtiyaç duyar.
+
+
+
+ X11 sessions require matching userspace packages after install or update.
+ X11 oturumları, kurulum veya güncelleme sonrası eşleşen kullanıcı alanı paketleri gerektirir.
+
+
+
+ Driver Actions
+ Sürücü İşlemleri
+
+
+
+ Use guided actions to install, switch or remove the current stack.
+ Mevcut yığını kurmak, değiştirmek veya kaldırmak için rehberli işlemleri kullanın.
+
+
+
+ I accept the detected license / agreement terms
+ Tespit edilen lisans / sözleşme koşullarını kabul ediyorum
+
+
+
+ Install Proprietary
+ Sahipli Sürücüyü Kur
+
+
+
+ Install Nouveau
+ Nouveau Kur (Açık Kaynak)
+
+
+
+ Remove Driver
+ Sürücüyü Kaldır
+
+
+
+ Apply Latest
+ En Son Sürümü Uygula
+
+
+
+ Apply Selected
+ Seçileni Uygula
+
+
+
+ Repository versions loaded:
+ Yüklenen repo sürümleri:
+
+
+
+ No repository version list has been loaded yet.
+ Henüz hiçbir repo sürüm listesi yüklenmedi.
+
+
+
+ Activity Log
+ Aktivite Günlüğü
+
+
+
+ Operation output is streamed here in real time.
+ İşlem çıktıları burada gerçek zamanlı olarak gösterilir.
+
+
+
+ Clear Log
+ Günlüğü Temizle
+
+
+ Session type:
+ Oturum türü:
+
+
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+ Wayland oturumlarında nvidia-drm.modeset=1 otomatik uygulanır.
+
+
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+ X11 oturumlarında xorg-x11-drv-nvidia paketi doğrulanır ve kurulur.
+
+
+ I accept the license / agreement terms
+ Lisans / sözleşme koşullarını kabul ediyorum
+
+
+ Install Proprietary Driver
+ Kapalı Kaynak Sürücüyü Kur
+
+
+ Install Open-Source Driver (Nouveau)
+ Açık Kaynak Sürücüyü Kur (Nouveau)
+
+
+
+ Deep Clean
+ Derin Temizlik
+
+
+
+ Rescan System
+ Sistemi Yeniden Tara
+
+
+
+ Update Center
+ Güncelleme Merkezi
+
+
+
+ Check the repository version and pin a specific build when required.
+ Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
+
+
+
+ Installed:
+ Kurulu:
+
+
+
+ Update Available
+ Güncelleme Mevcut
+
+
+
+ Up to Date
+ Güncel
+
+
+
+ Check for Updates
+ Güncellemeleri Kontrol Et
+
+
+ Apply Update
+ Güncellemeyi Uygula
+
+
+ Latest version:
+ En son sürüm:
+
+
+ Rescan
+ Yeniden Tara
+
+
+ Installed NVIDIA version:
+ Kurulu NVIDIA sürümü:
+ Main
- ro-Controlro-Control
- Theme: System (Dark)Tema: Sistem (Koyu)
- Theme: System (Light)Tema: Sistem (Açık)
- DriverSürücü
- Monitorİzleme
- SettingsAyarlar
+
+
+ ro-Control
+ ro-Control
+
+
+
+ Driver Control Center
+ Sürücü Kontrol Merkezi
+
+
+
+ System Monitor
+ Sistem İzleyici
+
+
+
+ Preferences
+ Tercihler
+
+
+
+ Install, verify and update NVIDIA drivers with guided system checks.
+ NVIDIA sürücülerini, rehberli sistem kontrolleriyle kurun, doğrulayın ve güncelleyin.
+
+
+
+ Track live CPU, GPU and memory telemetry in one place.
+ Canlı CPU, GPU ve bellek telemetrisini tek bir yerden izleyin.
+
+
+
+ Tune the interface and review diagnostic context before support work.
+ Arayüz yoğunluğunu ayarlayın ve destek çalışmalarından önce sistem raporunu gözden geçirin.
+
+
+
+ System Dark
+ Sistem (Koyu)
+
+
+
+ System Light
+ Sistem (Açık)
+
+
+
+ Compact Layout
+ Sıkı Görünüm
+
+
+
+ Comfort Layout
+ Rahat Görünüm
+
+
+ Theme: System (Dark)
+ Tema: Sistem (Koyu)
+
+
+ Theme: System (Light)
+ Tema: Sistem (Açık)
+
+
+ Driver
+ Sürücü
+
+
+ Monitor
+ İzleme
+
+
+ Settings
+ Ayarlar
+ MonitorPage
- System MonitoringSistem İzleme
- CPUCPU
- Usage: Kullanım:
- CPU data unavailableCPU verisi alınamıyor
- Temperature: Sıcaklık:
- GPU (NVIDIA)GPU (NVIDIA)
- NVIDIA GPUNVIDIA GPU
- Failed to read data via nvidia-sminvidia-smi üzerinden veri okunamadı
- Load: Yük:
- VRAM: VRAM:
- RAMRAM
- RAM data unavailableRAM verisi alınamıyor
- RefreshYenile
- Refresh interval: Yenileme aralığı:
+
+ System Monitoring
+ Sistem İzleme
+
+
+
+ CPU Load
+ CPU Yükü
+
+
+
+
+
+ Unavailable
+ Kullanılamıyor
+
+
+
+ CPU telemetry is currently unavailable.
+ CPU telemetrisi şu anda kullanılamıyor.
+
+
+
+ GPU Load
+ GPU Yükü
+
+
+
+ nvidia-smi did not return live GPU telemetry.
+ nvidia-smi canlı GPU telemetrisi döndürmedi.
+
+
+
+ Memory Usage
+ Bellek Kullanımı
+
+
+
+ Used:
+ Kullanılan:
+
+
+
+ RAM telemetry is currently unavailable.
+ RAM telemetrisi şu anda kullanılamıyor.
+
+
+
+ Live Resource Curves
+ Canlı Kaynak Grafikleri
+
+
+
+ Quick pulse view for the most important machine resources.
+ En önemli makine kaynakları için hızlı anlık görünüm.
+
+
+
+ CPU
+ CPU
+
+
+
+ GPU
+ Ekran Kartı (GPU)
+
+
+
+ Health Summary
+ Sağlık Özeti
+
+
+
+ Fast interpretation of the raw telemetry values.
+ Ham telemetri değerlerinin hızlı yorumlaması.
+
+
+
+ CPU Busy
+ CPU Meşgul
+
+
+
+ CPU Stable
+ CPU Stabil
+
+
+
+ GPU Online
+ GPU Çevrimiçi
+
+
+
+ GPU Telemetry Missing
+ GPU Telemetrisi Bulunamadı
+
+
+
+ Memory Pressure
+ Bellek Baskısı
+
+
+
+ Memory Stable
+ Bellek Stabil
+
+
+
+ GPU temperature:
+ GPU sıcaklığı:
+
+
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.
+ GPU metrikleri kullanılamıyor. Sürücü kurulumunu ve nvidia-smi erişilebilirliğini kontrol edin.
+
+
+
+ Detailed Signals
+ Detaylı Sinyaller
+
+
+
+ Expanded raw values for support and diagnostics.
+ Destek ve tanılama için genişletilmiş ham değerler.
+
+
+
+ CPU Temperature
+ CPU Sıcaklığı
+
+
+
+
+
+
+ Unknown
+ Bilinmiyor
+
+
+
+ GPU Temperature
+ GPU Sıcaklığı
+
+
+
+ VRAM
+ VRAM Belleği
+
+
+
+ RAM Footprint
+ RAM Kapladığı Alan
+
+
+
+ Actions
+ İşlemler
+
+
+
+ Trigger a manual refresh when you need a fresh sample.
+ Taze bir örneğe ihtiyacınız olduğunda manuel yenilemeyi tetikleyin.
+
+
+
+ Refresh Telemetry
+ Telemetriyi Yenile
+
+
+
+ NVIDIA Path OK
+ NVIDIA Yolu Tamam
+
+
+
+ Check NVIDIA Path
+ NVIDIA Yolunu Kontrol Et
+
+
+ Usage:
+ Kullanım:
+
+
+ CPU data unavailable
+ CPU verisi alınamıyor
+
+
+
+ Temperature:
+ Sıcaklık:
+
+
+ GPU (NVIDIA)
+ GPU (NVIDIA)
+
+
+
+ NVIDIA GPU
+ NVIDIA GPU
+
+
+ Failed to read data via nvidia-smi
+ nvidia-smi üzerinden veri okunamadı
+
+
+ Load:
+ Yük:
+
+
+ VRAM:
+ VRAM:
+
+
+
+ RAM
+ RAM
+
+
+ RAM data unavailable
+ RAM verisi alınamıyor
+
+
+ Refresh
+ Yenile
+
+
+
+ Refresh interval:
+ Yenileme aralığı:
+ NvidiaDetector
- Proprietary (NVIDIA)Kapalı Kaynak (NVIDIA)
- Open Source (Nouveau)Açık Kaynak (Nouveau)
- Not Installed / UnknownKurulu Değil / Bilinmiyor
- GPU: %1
+
+
+ Proprietary (NVIDIA)
+ Kapalı Kaynak (NVIDIA)
+
+
+
+ Open Source (Nouveau)
+ Açık Kaynak (Nouveau)
+
+
+
+ Not Installed / Unknown
+ Kurulu Değil / Bilinmiyor
+
+
+
+
+ None
+ Yok
+
+
+
+ GPU: %1
Driver Version: %2
Secure Boot: %3
Session: %4
NVIDIA Module: %5
-Nouveau: %6GPU: %1
+Nouveau: %6
+ GPU: %1
Sürücü Sürümü: %2
Secure Boot: %3
Oturum: %4
NVIDIA Modülü: %5
-Nouveau: %6
- UnknownBilinmiyor
- LoadedYüklü
- Not loadedYüklü değil
- ActiveAktif
- InactiveAktif değil
+Nouveau: %6
+
+
+
+ Enabled
+ Etkin
+
+
+
+ Disabled
+ Devre Dışı
+
+
+
+ Disabled / Unknown
+ Devre Dışı / Bilinmiyor
+
+
+
+ Unknown
+ Bilinmiyor
+
+
+
+ Loaded
+ Yüklü
+
+
+
+ Not loaded
+ Yüklü değil
+
+
+
+ Active
+ Aktif
+
+
+
+ Inactive
+ Aktif değil
+ NvidiaInstaller
- You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
- License agreement acceptance is required before installation.Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
- Checking RPM Fusion repositories...RPM Fusion depoları kontrol ediliyor...
- Platform version could not be detected.Platform sürümü tespit edilemedi.
- Failed to enable RPM Fusion repositories: RPM Fusion depoları etkinleştirilemedi:
- Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
- Installation failed: Kurulum başarısız:
- Building the kernel module (akmods --force)...Kernel modülü derleniyor (akmods --force)...
- The proprietary NVIDIA driver was installed successfully. Please restart the system.Kapalı kaynak NVIDIA sürücüsü başarıyla kuruldu. Lütfen sistemi yeniden başlatın.
- Switching to the open-source driver...Açık kaynak sürücüye geçiliyor...
- Failed to remove proprietary packages: Kapalı kaynak paketler kaldırılamadı:
- Open-source driver installation failed: Açık kaynak sürücü kurulumu başarısız:
- The open-source driver (Nouveau) was installed. Please restart the system.Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
- Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
- Driver removed successfully.Sürücü başarıyla kaldırıldı.
- Removal failed: Kaldırma başarısız:
- Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
- Deep clean completed.Derin temizlik tamamlandı.
- Wayland detected: applying nvidia-drm.modeset=1...Wayland tespit edildi: nvidia-drm.modeset=1 uygulanıyor...
- Failed to apply the Wayland kernel parameter: Wayland kernel parametresi uygulanamadı:
- X11 detected: checking NVIDIA userspace packages...X11 tespit edildi: NVIDIA userspace paketleri kontrol ediliyor...
- Failed to install the X11 NVIDIA package: X11 NVIDIA paketi kurulamadı:
+
+
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+ Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
+
+
+
+ License agreement acceptance is required before installation.
+ Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
+
+
+
+ Checking RPM Fusion repositories...
+ RPM Fusion depoları kontrol ediliyor...
+
+
+
+ Platform version could not be detected.
+ Platform sürümü tespit edilemedi.
+
+
+
+ Failed to enable RPM Fusion repositories:
+ RPM Fusion depoları etkinleştirilemedi:
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
+
+
+
+ Installation failed:
+ Kurulum başarısız:
+
+
+
+ Building the kernel module (akmods --force)...
+ Kernel modülü derleniyor (akmods --force)...
+
+
+
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+ Kapalı kaynak NVIDIA sürücüsü başarıyla kuruldu. Lütfen sistemi yeniden başlatın.
+
+
+
+ Switching to the open-source driver...
+ Açık kaynak sürücüye geçiliyor...
+
+
+
+ Failed to remove proprietary packages:
+ Kapalı kaynak paketler kaldırılamadı:
+
+
+
+ Open-source driver installation failed:
+ Açık kaynak sürücü kurulumu başarısız:
+
+
+
+ The open-source driver (Nouveau) was installed. Please restart the system.
+ Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
+
+
+
+ Removing the NVIDIA driver...
+ NVIDIA sürücüsü kaldırılıyor...
+
+
+
+ Driver removed successfully.
+ Sürücü başarıyla kaldırıldı.
+
+
+
+ Removal failed:
+ Kaldırma başarısız:
+
+
+
+ Cleaning legacy driver leftovers...
+ Eski sürücü kalıntıları temizleniyor...
+
+
+
+ Deep clean failed:
+ Derin temizlik başarısız:
+
+
+
+ DNF cache cleanup failed:
+ DNF önbellek temizliği başarısız:
+
+
+
+ Deep clean completed.
+ Derin temizlik tamamlandı.
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland tespit edildi: nvidia-drm.modeset=1 uygulanıyor...
+
+
+
+ Another driver operation is already running.
+ Başka bir sürücü işlemi şu an yürütülüyor.
+
+
+
+ Unknown
+ Bilinmiyor
+
+
+
+ Legacy NVIDIA cleanup completed.
+ Eski NVIDIA temizliği tamamlandı.
+
+
+
+ Failed to apply the Wayland kernel parameter:
+ Wayland kernel parametresi uygulanamadı:
+
+
+
+ X11 detected: checking NVIDIA userspace packages...
+ X11 tespit edildi: NVIDIA userspace paketleri kontrol ediliyor...
+
+
+
+ Failed to install the X11 NVIDIA package:
+ X11 NVIDIA paketi kurulamadı:
+ NvidiaUpdater
- Updating the NVIDIA driver...NVIDIA sürücüsü güncelleniyor...
- Update failed: Güncelleme başarısız:
- Rebuilding the kernel module...Kernel modülü yeniden derleniyor...
- Wayland detected: refreshing nvidia-drm.modeset=1...Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
- Driver updated successfully. Please restart the system.Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
+
+ Updating the NVIDIA driver...
+ NVIDIA sürücüsü güncelleniyor...
+
+
+
+ Update failed:
+ Güncelleme başarısız:
+
+
+ Rebuilding the kernel module...
+ Kernel modülü yeniden derleniyor...
+
+
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+ Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
+
+
+
+ Driver updated successfully. Please restart the system.
+ Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
+
+
+
+
+ dnf not found.
+ dnf bulunamadı.
+
+
+
+ No installed NVIDIA driver found.
+ Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
+
+
+
+ Update found (version details unavailable).
+ Güncelleme bulundu (sürüm detayları kullanılamıyor).
+
+
+
+ Update found: %1
+ Güncelleme bulundu: %1
+
+
+
+ Driver is up to date. No new version found.
+ Sürücü güncel. Yeni sürüm bulunamadı.
+
+
+
+ Update check failed: %1
+ Güncelleme kontrolü başarısız: %1
+
+
+
+ Another driver operation is already running.
+ Başka bir sürücü işlemi şu an yürütülüyor.
+
+
+
+ Kernel module build failed:
+ Kernel modülü inşası başarısız:
+
+
+
+
+
+ unknown error
+ bilinmeyen hata
+
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland algılandı: nvidia-drm.modeset=1 uygulanıyor...
+
+
+
+ Failed to update the Wayland kernel parameter:
+ Wayland çekirdek parametresi güncellenemedi:
+
+
+
+ No available versions found.
+ Hiçbir uygun sürüm bulunamadı.
+
+
+
+ Available versions: %1
+ Mevcut sürümler: %1
+
+
+
+ Starting update check...
+ Güncelleme denetimi başlatılıyor...
+
+
+
+ Selected version not found in the repository.
+ Seçili sürüm depoda bulunamadı.
+
+
+
+ Updating NVIDIA driver to the latest version...
+ NVIDIA sürücüsü en yeni sürüme güncelleniyor...
+
+
+
+ Switching NVIDIA driver to selected version: %1
+ NVIDIA sürücüsü seçili sürüme değiştiriliyor: %1
+
+
+
+ Rebuilding kernel module...
+ Kernel modülü yeniden derleniyor...
+
+
+
+ Latest version installed successfully. Please restart the system.
+ En son sürüm başarıyla yüklendi. Lütfen sistemi yeniden başlatın.
+
+
+
+ Selected version applied successfully. Please restart the system.
+ Seçili sürüm başarıyla uygulandı. Lütfen sistemi yeniden başlatın.
+ SettingsPage
- SettingsAyarlar
- AboutHakkında
- Application: Uygulama:
- Theme: Tema:
- System DarkSistem Koyu
- System LightSistem Açık
+
+ Settings
+ Ayarlar
+
+
+
+ Interface
+ Arayüz
+
+
+
+ Tune the shell density and how much operational detail the app exposes.
+ Arayüz yoğunluğunu ve uygulamanın ne kadar operasyonel ayrıntı göstereceğini ayarlayın.
+
+
+
+ Compact layout
+ Sıkı görünüm
+
+
+
+ Reduces spacing to fit more information on screen.
+ Ekrana daha fazla bilgi sığdırmak için boşlukları azaltır.
+
+
+
+ Show advanced diagnostics
+ Gelişmiş tanılamayı göster
+
+
+
+ Shows verification reports and expanded monitor metrics.
+ Doğrulama raporlarını ve genişletilmiş monitör metriklerini gösterir.
+
+
+
+ System Dark Theme
+ Koyu Sistem Teması
+
+
+
+ System Light Theme
+ Açık Sistem Teması
+
+
+
+ Compact Active
+ Sıkı Düzen Etkin
+
+
+
+ Comfort Active
+ Rahat Düzen Etkin
+
+
+
+ Diagnostics
+ Tanılama
+
+
+
+ Useful runtime context before filing issues or performing support work.
+ Hata bildirmeden veya destek çalışması yapmadan önce yararlı çalışma zamanı bilgisi.
+
+
+
+ Application
+ Uygulama
+
+
+
+ GPU
+ Ekran Kartı (GPU)
+
+
+
+ Not detected
+ Tespit edilmedi
+
+
+
+ Driver
+ Sürücü
+
+
+
+ Session
+ Oturum
+
+
+
+ Unknown
+ Bilinmiyor
+
+
+
+ Use the Driver page to refresh detection before copying any diagnostic context.
+ Tanılama bağlamını kopyalamadan önce algılamayı yenilemek için Sürücü (Driver) sayfasını kullanın.
+
+
+
+ Workflow Guidance
+ İş Akışı Rehberi
+
+
+
+ Recommended order of operations when changing drivers.
+ Sürücü değiştirilirken önerilen işlem sırası.
+
+
+
+ 1. Verify GPU detection and session type.
+2. Install or switch the driver stack.
+3. Check repository updates.
+4. Restart after successful package operations.
+ 1. GPU algılamasını ve oturum tipini doğrulayın.
+2. Sürücü yığınını kurun veya değiştirin.
+3. Repo güncellemelerini kontrol edin.
+4. Başarılı paket işlemlerinden sonra yeniden başlatın.
+
+
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.
+ Güvenli Önyükleme etkin. Paket kurulumundan sonra kernel modülü imzalanması gerekebilir.
+
+
+
+ No Secure Boot blocker is currently reported by the detector.
+ Şu anda dedektör tarafından bildirilen herhangi bir (Secure Boot) engeli bulunmuyor.
+
+
+
+ About
+ Hakkında
+
+
+
+ Project identity and current shell mode.
+ Proje kimliği ve mevcut kabuk modu.
+
+
+
+ Application:
+ Uygulama:
+
+
+
+ Theme:
+ Tema:
+
+
+
+ System Dark
+ Sistem Koyu
+
+
+
+ System Light
+ Sistem Açık
+
+
+
+ Layout density:
+ Düzen Görünümü:
+
+
+
+ Compact
+ Sıkı
+
+
+
+ Comfort
+ Rahat
+
+
+
+ Advanced diagnostics:
+ Gelişmiş Tanılama:
+
+
+
+ Visible
+ Görünür
+
+
+
+ Hidden
+ Gizli
+
-
+
+ SidebarMenu
+
+
+ Driver Management
+ Sürücü Yönetimi
+
+
+
+ System Monitoring
+ Sistem İzleme
+
+
+
+ Settings
+ Ayarlar
+
+
+
+ ro-Control
+ ro-Control
+
+
+
\ No newline at end of file
diff --git a/src/backend/nvidia/detector.cpp b/src/backend/nvidia/detector.cpp
index 92ec1d0..c32a96b 100644
--- a/src/backend/nvidia/detector.cpp
+++ b/src/backend/nvidia/detector.cpp
@@ -1,5 +1,6 @@
#include "detector.h"
+#include "system/capabilityprobe.h"
#include "system/commandrunner.h"
#include "system/sessionutil.h"
@@ -64,6 +65,10 @@ void NvidiaDetector::refresh() {
}
QString NvidiaDetector::detectGpuName() const {
+ if (!CapabilityProbe::isToolAvailable(QStringLiteral("lspci"))) {
+ return {};
+ }
+
CommandRunner runner;
const auto result =
@@ -96,12 +101,19 @@ QString NvidiaDetector::detectGpuName() const {
QString NvidiaDetector::detectDriverVersion() const {
CommandRunner runner;
- const auto result = runner.run(QStringLiteral("nvidia-smi"),
- {QStringLiteral("--query-gpu=driver_version"),
- QStringLiteral("--format=csv,noheader")});
+ if (CapabilityProbe::isToolAvailable(QStringLiteral("nvidia-smi"))) {
+ const auto result =
+ runner.run(QStringLiteral("nvidia-smi"),
+ {QStringLiteral("--query-gpu=driver_version"),
+ QStringLiteral("--format=csv,noheader")});
- if (result.success())
- return result.stdout.trimmed();
+ if (result.success())
+ return result.stdout.trimmed();
+ }
+
+ if (!CapabilityProbe::isToolAvailable(QStringLiteral("modinfo"))) {
+ return {};
+ }
const auto modinfo =
runner.run(QStringLiteral("modinfo"), {QStringLiteral("nvidia")});
@@ -133,6 +145,13 @@ bool NvidiaDetector::isModuleLoaded(const QString &moduleName) const {
}
bool NvidiaDetector::detectSecureBoot(bool *known) const {
+ if (!CapabilityProbe::isToolAvailable(QStringLiteral("mokutil"))) {
+ if (known != nullptr) {
+ *known = false;
+ }
+ return false;
+ }
+
CommandRunner runner;
const auto result =
runner.run(QStringLiteral("mokutil"), {QStringLiteral("--sb-state")});
@@ -152,4 +171,3 @@ bool NvidiaDetector::detectSecureBoot(bool *known) const {
return false;
}
-
diff --git a/src/backend/nvidia/installer.cpp b/src/backend/nvidia/installer.cpp
index 9978c3a..8586920 100644
--- a/src/backend/nvidia/installer.cpp
+++ b/src/backend/nvidia/installer.cpp
@@ -125,7 +125,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Checking RPM Fusion repositories..."));
+ NvidiaInstaller::tr("Checking RPM Fusion repositories..."));
}
},
Qt::QueuedConnection);
@@ -142,7 +142,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
[guard]() {
if (guard) {
emit guard->installFinished(
- false, guard->tr("Platform version could not be detected."));
+ false, NvidiaInstaller::tr("Platform version could not be detected."));
}
},
Qt::QueuedConnection);
@@ -161,7 +161,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
if (!result.success()) {
const QString error =
- guard->tr("Failed to enable RPM Fusion repositories: ") +
+ NvidiaInstaller::tr("Failed to enable RPM Fusion repositories: ") +
result.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
@@ -178,7 +178,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
guard,
[guard]() {
if (guard) {
- emit guard->progressMessage(guard->tr(
+ emit guard->progressMessage(NvidiaInstaller::tr(
"Installing the proprietary NVIDIA driver (akmod-nvidia)..."));
}
},
@@ -190,7 +190,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
if (!result.success()) {
const QString error =
- guard->tr("Installation failed: ") + result.stderr.trimmed();
+ NvidiaInstaller::tr("Installation failed: ") + result.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -207,7 +207,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Building the kernel module (akmods --force)..."));
+ NvidiaInstaller::tr("Building the kernel module (akmods --force)..."));
}
},
Qt::QueuedConnection);
@@ -233,7 +233,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
if (guard) {
emit guard->installFinished(
true,
- guard->tr("The proprietary NVIDIA driver was installed "
+ NvidiaInstaller::tr("The proprietary NVIDIA driver was installed "
"successfully. Please restart the system."));
}
},
@@ -269,7 +269,7 @@ void NvidiaInstaller::installOpenSource() {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Switching to the open-source driver..."));
+ NvidiaInstaller::tr("Switching to the open-source driver..."));
}
},
Qt::QueuedConnection);
@@ -282,7 +282,7 @@ void NvidiaInstaller::installOpenSource() {
if (!result.success()) {
const QString error =
- guard->tr("Failed to remove proprietary packages: ") +
+ NvidiaInstaller::tr("Failed to remove proprietary packages: ") +
result.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
@@ -302,7 +302,7 @@ void NvidiaInstaller::installOpenSource() {
if (!result.success()) {
const QString error =
- guard->tr("Open-source driver installation failed: ") +
+ NvidiaInstaller::tr("Open-source driver installation failed: ") +
result.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
@@ -323,7 +323,7 @@ void NvidiaInstaller::installOpenSource() {
if (guard) {
emit guard->installFinished(
true,
- guard->tr("The open-source driver (Nouveau) was installed. "
+ NvidiaInstaller::tr("The open-source driver (Nouveau) was installed. "
"Please restart the system."));
}
},
@@ -359,7 +359,7 @@ void NvidiaInstaller::remove() {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Removing the NVIDIA driver..."));
+ NvidiaInstaller::tr("Removing the NVIDIA driver..."));
}
},
Qt::QueuedConnection);
@@ -372,8 +372,8 @@ void NvidiaInstaller::remove() {
const bool success = result.success();
const QString message =
- success ? guard->tr("Driver removed successfully.")
- : guard->tr("Removal failed: ") + result.stderr.trimmed();
+ success ? NvidiaInstaller::tr("Driver removed successfully.")
+ : NvidiaInstaller::tr("Removal failed: ") + result.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
[guard, success, message]() {
@@ -413,7 +413,7 @@ void NvidiaInstaller::deepClean() {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Cleaning legacy driver leftovers..."));
+ NvidiaInstaller::tr("Cleaning legacy driver leftovers..."));
}
},
Qt::QueuedConnection);
@@ -425,7 +425,7 @@ void NvidiaInstaller::deepClean() {
if (!removeResult.success()) {
const QString error =
- guard->tr("Deep clean failed: ") + removeResult.stderr.trimmed();
+ NvidiaInstaller::tr("Deep clean failed: ") + removeResult.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -441,7 +441,7 @@ void NvidiaInstaller::deepClean() {
runner.runAsRoot(QStringLiteral("dnf"),
{QStringLiteral("clean"), QStringLiteral("all")});
if (!cleanResult.success()) {
- const QString error = guard->tr("DNF cache cleanup failed: ") +
+ const QString error = NvidiaInstaller::tr("DNF cache cleanup failed: ") +
cleanResult.stderr.trimmed();
QMetaObject::invokeMethod(
guard,
@@ -458,9 +458,9 @@ void NvidiaInstaller::deepClean() {
guard,
[guard]() {
if (guard) {
- emit guard->progressMessage(guard->tr("Deep clean completed."));
+ emit guard->progressMessage(NvidiaInstaller::tr("Deep clean completed."));
emit guard->removeFinished(
- true, guard->tr("Legacy NVIDIA cleanup completed."));
+ true, NvidiaInstaller::tr("Legacy NVIDIA cleanup completed."));
}
},
Qt::QueuedConnection);
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index 3eab02d..92fbd88 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -218,8 +218,8 @@ void NvidiaUpdater::refreshAvailableVersions() {
guard->setAvailableVersions(snapshot.availableVersions);
emit guard->progressMessage(
snapshot.availableVersions.isEmpty()
- ? guard->tr("No available versions found.")
- : guard->tr("Available versions: %1")
+ ? NvidiaUpdater::tr("No available versions found.")
+ : NvidiaUpdater::tr("Available versions: %1")
.arg(snapshot.availableVersions.size()));
},
Qt::QueuedConnection);
@@ -297,7 +297,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
[guard]() {
if (guard) {
emit guard->updateFinished(false,
- guard->tr("dnf not found."));
+ NvidiaUpdater::tr("dnf not found."));
}
},
Qt::QueuedConnection);
@@ -315,7 +315,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
if (guard) {
emit guard->updateFinished(
false,
- guard->tr("Selected version not found in the repository."));
+ NvidiaUpdater::tr("Selected version not found in the repository."));
}
},
Qt::QueuedConnection);
@@ -331,9 +331,9 @@ void NvidiaUpdater::applyVersion(const QString &version) {
emit guard->progressMessage(
trimmedVersion.isEmpty()
- ? guard->tr(
+ ? NvidiaUpdater::tr(
"Updating NVIDIA driver to the latest version...")
- : guard->tr(
+ : NvidiaUpdater::tr(
"Switching NVIDIA driver to selected version: %1")
.arg(trimmedVersion));
},
@@ -353,8 +353,8 @@ void NvidiaUpdater::applyVersion(const QString &version) {
auto result = runner.runAsRoot(QStringLiteral("dnf"), args);
if (!result.success()) {
const QString error =
- guard->tr("Update failed: ") +
- commandError(result, guard->tr("unknown error"));
+ NvidiaUpdater::tr("Update failed: ") +
+ commandError(result, NvidiaUpdater::tr("unknown error"));
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -371,7 +371,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
[guard]() {
if (guard) {
emit guard->progressMessage(
- guard->tr("Rebuilding kernel module..."));
+ NvidiaUpdater::tr("Rebuilding kernel module..."));
}
},
Qt::QueuedConnection);
@@ -393,11 +393,11 @@ void NvidiaUpdater::applyVersion(const QString &version) {
const QString successMessage =
trimmedVersion.isEmpty()
? (installedVersion.isEmpty()
- ? guard->tr("Latest version installed successfully. "
+ ? NvidiaUpdater::tr("Latest version installed successfully. "
"Please restart the system.")
- : guard->tr("Driver updated successfully. "
+ : NvidiaUpdater::tr("Driver updated successfully. "
"Please restart the system."))
- : guard->tr(
+ : NvidiaUpdater::tr(
"Selected version applied successfully. "
"Please restart the system.");
diff --git a/src/backend/system/capabilityprobe.cpp b/src/backend/system/capabilityprobe.cpp
new file mode 100644
index 0000000..bc9d725
--- /dev/null
+++ b/src/backend/system/capabilityprobe.cpp
@@ -0,0 +1,41 @@
+#include "capabilityprobe.h"
+
+#include "commandrunner.h"
+
+namespace CapabilityProbe {
+
+ToolStatus probeTool(const QString &program) {
+ ToolStatus status;
+ status.program = program;
+ status.resolvedPath = CommandRunner::resolveProgramPath(program);
+ status.available = !status.resolvedPath.isEmpty();
+ return status;
+}
+
+bool isToolAvailable(const QString &program) {
+ return probeTool(program).available;
+}
+
+QStringList missingTools(const QStringList &programs) {
+ QStringList missing;
+ for (const QString &program : programs) {
+ if (!isToolAvailable(program)) {
+ missing << program;
+ }
+ }
+
+ missing.removeDuplicates();
+ return missing;
+}
+
+QString missingToolsMessage(const QStringList &programs) {
+ const QStringList missing = missingTools(programs);
+ if (missing.isEmpty()) {
+ return {};
+ }
+
+ return QStringLiteral("Missing required tools: %1")
+ .arg(missing.join(QStringLiteral(", ")));
+}
+
+} // namespace CapabilityProbe
diff --git a/src/backend/system/capabilityprobe.h b/src/backend/system/capabilityprobe.h
new file mode 100644
index 0000000..967488c
--- /dev/null
+++ b/src/backend/system/capabilityprobe.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+#include
+
+namespace CapabilityProbe {
+
+struct ToolStatus {
+ QString program;
+ QString resolvedPath;
+ bool available = false;
+};
+
+ToolStatus probeTool(const QString &program);
+bool isToolAvailable(const QString &program);
+QStringList missingTools(const QStringList &programs);
+QString missingToolsMessage(const QStringList &programs);
+
+} // namespace CapabilityProbe
diff --git a/src/backend/system/commandrunner.cpp b/src/backend/system/commandrunner.cpp
index 924b2a1..486c478 100644
--- a/src/backend/system/commandrunner.cpp
+++ b/src/backend/system/commandrunner.cpp
@@ -36,11 +36,26 @@ CommandRunner::Result CommandRunner::run(const QString &program,
return lastResult;
}
-QString CommandRunner::resolveProgram(const QString &program) const {
+QString CommandRunner::overrideEnvironmentVariableName(const QString &program) {
+ QString normalized = program.toUpper();
+ normalized.replace(QLatin1Char('-'), QLatin1Char('_'));
+ normalized.replace(QLatin1Char('.'), QLatin1Char('_'));
+ normalized.replace(QLatin1Char('/'), QLatin1Char('_'));
+ return QStringLiteral("RO_CONTROL_COMMAND_%1").arg(normalized);
+}
+
+QString CommandRunner::resolveProgramPath(const QString &program) {
if (program.isEmpty()) {
return {};
}
+ const QString overridePath =
+ qEnvironmentVariable(overrideEnvironmentVariableName(program).toUtf8())
+ .trimmed();
+ if (!overridePath.isEmpty()) {
+ return overridePath;
+ }
+
if (program.contains(QLatin1Char('/'))) {
return program;
}
@@ -48,6 +63,10 @@ QString CommandRunner::resolveProgram(const QString &program) const {
return QStandardPaths::findExecutable(program);
}
+QString CommandRunner::resolveProgram(const QString &program) const {
+ return resolveProgramPath(program);
+}
+
CommandRunner::Result CommandRunner::runOnce(const QString &program,
const QStringList &args,
const RunOptions &options,
diff --git a/src/backend/system/commandrunner.h b/src/backend/system/commandrunner.h
index 4b18e68..c3929cd 100644
--- a/src/backend/system/commandrunner.h
+++ b/src/backend/system/commandrunner.h
@@ -26,6 +26,7 @@ class CommandRunner : public QObject {
};
explicit CommandRunner(QObject *parent = nullptr);
+ static QString resolveProgramPath(const QString &program);
// Bloklayan komut — sonuç dönene kadar bekler
Result run(const QString &program, const QStringList &args = {});
@@ -48,6 +49,7 @@ class CommandRunner : public QObject {
private:
QString resolveProgram(const QString &program) const;
+ static QString overrideEnvironmentVariableName(const QString &program);
Result runOnce(const QString &program, const QStringList &args,
const RunOptions &options, int attempt);
};
diff --git a/src/backend/system/dnfmanager.cpp b/src/backend/system/dnfmanager.cpp
index 3e30c40..e0c0328 100644
--- a/src/backend/system/dnfmanager.cpp
+++ b/src/backend/system/dnfmanager.cpp
@@ -1,10 +1,9 @@
// DNF paket yoneticisi
#include "dnfmanager.h"
+#include "capabilityprobe.h"
#include "commandrunner.h"
-#include
-
namespace {
CommandRunner::Result dnfUnavailableResult() {
@@ -25,7 +24,7 @@ DnfManager::DnfManager(QObject *parent) : QObject(parent) {
}
bool DnfManager::isAvailable() const {
- return !QStandardPaths::findExecutable(QStringLiteral("dnf")).isEmpty();
+ return CapabilityProbe::isToolAvailable(QStringLiteral("dnf"));
}
CommandRunner::Result DnfManager::checkUpdates(const QStringList &packages) {
diff --git a/src/backend/system/polkit.cpp b/src/backend/system/polkit.cpp
index 87746e1..fbc8cd4 100644
--- a/src/backend/system/polkit.cpp
+++ b/src/backend/system/polkit.cpp
@@ -2,7 +2,7 @@
#include "polkit.h"
-#include
+#include "capabilityprobe.h"
PolkitHelper::PolkitHelper(QObject *parent) : QObject(parent) {
connect(&m_runner, &CommandRunner::outputLine, this,
@@ -12,7 +12,7 @@ PolkitHelper::PolkitHelper(QObject *parent) : QObject(parent) {
}
bool PolkitHelper::isPkexecAvailable() const {
- return !QStandardPaths::findExecutable(QStringLiteral("pkexec")).isEmpty();
+ return CapabilityProbe::isToolAvailable(QStringLiteral("pkexec"));
}
CommandRunner::Result PolkitHelper::runPrivileged(const QString &program,
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 79a715d..48926f9 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -12,6 +12,38 @@ Item {
property string bannerText: ""
property string bannerTone: "info"
+ property string operationSource: ""
+ property string operationPhase: ""
+ property string operationDetail: ""
+ property bool operationRunning: nvidiaInstaller.busy || nvidiaUpdater.busy
+
+ function classifyOperationPhase(message) {
+ const lowered = message.toLowerCase();
+ if (lowered.indexOf("rpm fusion") >= 0 || lowered.indexOf("repository") >= 0)
+ return qsTr("Repository Setup");
+ if (lowered.indexOf("install") >= 0 || lowered.indexOf("remove") >= 0 || lowered.indexOf("deep clean") >= 0)
+ return qsTr("Package Transaction");
+ if (lowered.indexOf("kernel") >= 0 || lowered.indexOf("akmods") >= 0 || lowered.indexOf("dracut") >= 0)
+ return qsTr("Kernel Integration");
+ if (lowered.indexOf("wayland") >= 0 || lowered.indexOf("x11") >= 0 || lowered.indexOf("session") >= 0)
+ return qsTr("Session Finalization");
+ if (lowered.indexOf("update") >= 0 || lowered.indexOf("version") >= 0)
+ return qsTr("Update Check");
+ return qsTr("General");
+ }
+
+ function setOperationState(source, message, tone, running) {
+ operationSource = source;
+ operationDetail = message;
+ operationPhase = classifyOperationPhase(message);
+ operationRunning = running;
+ bannerText = (operationPhase.length > 0 ? operationPhase + ": " : "") + message;
+ bannerTone = tone;
+ }
+
+ function finishOperation(source, success, message) {
+ setOperationState(source, message, success ? "success" : "error", false);
+ }
ScrollView {
id: pageScroll
@@ -62,6 +94,7 @@ Item {
value: nvidiaDetector.driverVersion.length > 0 ? nvidiaDetector.driverVersion : qsTr("None")
subtitle: nvidiaUpdater.updateAvailable ? qsTr("Latest available: ") + nvidiaUpdater.latestVersion : qsTr("No pending package update detected.")
accentColor: page.theme.accentC
+ busy: page.operationRunning
}
StatCard {
@@ -154,6 +187,30 @@ Item {
title: qsTr("Driver Actions")
subtitle: qsTr("Use guided actions to install, switch or remove the current stack.")
+ Flow {
+ Layout.fillWidth: true
+ spacing: 8
+ visible: page.operationDetail.length > 0
+
+ InfoBadge {
+ text: qsTr("Source: ") + (page.operationSource.length > 0 ? page.operationSource : qsTr("Idle"))
+ backgroundColor: page.theme.cardStrong
+ foregroundColor: page.theme.text
+ }
+
+ InfoBadge {
+ text: qsTr("Phase: ") + (page.operationPhase.length > 0 ? page.operationPhase : qsTr("Idle"))
+ backgroundColor: page.operationRunning ? page.theme.infoBg : page.theme.cardStrong
+ foregroundColor: page.theme.text
+ }
+
+ InfoBadge {
+ text: page.operationRunning ? qsTr("Running") : qsTr("Idle")
+ backgroundColor: page.operationRunning ? page.theme.warningBg : page.theme.successBg
+ foregroundColor: page.theme.text
+ }
+ }
+
StatusBanner {
Layout.fillWidth: true
theme: page.theme
@@ -177,28 +234,40 @@ Item {
Layout.fillWidth: true
text: qsTr("Install Proprietary")
enabled: !nvidiaInstaller.busy && (!nvidiaInstaller.proprietaryAgreementRequired || eulaAccept.checked)
- onClicked: nvidiaInstaller.installProprietary(eulaAccept.checked)
+ onClicked: {
+ page.setOperationState(qsTr("Installer"), qsTr("Installing the proprietary NVIDIA driver (akmod-nvidia)..."), "info", true);
+ nvidiaInstaller.installProprietary(eulaAccept.checked);
+ }
}
Button {
Layout.fillWidth: true
text: qsTr("Install Nouveau")
enabled: !nvidiaInstaller.busy
- onClicked: nvidiaInstaller.installOpenSource()
+ onClicked: {
+ page.setOperationState(qsTr("Installer"), qsTr("Switching to the open-source driver..."), "info", true);
+ nvidiaInstaller.installOpenSource();
+ }
}
Button {
Layout.fillWidth: true
text: qsTr("Remove Driver")
enabled: !nvidiaInstaller.busy
- onClicked: nvidiaInstaller.remove()
+ onClicked: {
+ page.setOperationState(qsTr("Installer"), qsTr("Removing the NVIDIA driver..."), "info", true);
+ nvidiaInstaller.remove();
+ }
}
Button {
Layout.fillWidth: true
text: qsTr("Deep Clean")
enabled: !nvidiaInstaller.busy
- onClicked: nvidiaInstaller.deepClean()
+ onClicked: {
+ page.setOperationState(qsTr("Installer"), qsTr("Cleaning legacy driver leftovers..."), "info", true);
+ nvidiaInstaller.deepClean();
+ }
}
}
@@ -256,13 +325,19 @@ Item {
Button {
text: qsTr("Check for Updates")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy
- onClicked: nvidiaUpdater.checkForUpdate()
+ onClicked: {
+ page.setOperationState(qsTr("Updater"), qsTr("Starting update check..."), "info", true);
+ nvidiaUpdater.checkForUpdate();
+ }
}
Button {
text: qsTr("Apply Latest")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && nvidiaUpdater.updateAvailable
- onClicked: nvidiaUpdater.applyUpdate()
+ onClicked: {
+ page.setOperationState(qsTr("Updater"), qsTr("Updating NVIDIA driver to the latest version..."), "info", true);
+ nvidiaUpdater.applyUpdate();
+ }
}
}
@@ -280,7 +355,10 @@ Item {
Button {
text: qsTr("Apply Selected")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && versionPicker.currentIndex >= 0
- onClicked: nvidiaUpdater.applyVersion(versionPicker.currentText)
+ onClicked: {
+ page.setOperationState(qsTr("Updater"), qsTr("Switching NVIDIA driver to selected version: ") + versionPicker.currentText, "info", true);
+ nvidiaUpdater.applyVersion(versionPicker.currentText);
+ }
}
}
@@ -331,14 +409,12 @@ Item {
function onProgressMessage(message) {
logArea.append(message);
- page.bannerText = message;
- page.bannerTone = "info";
+ page.setOperationState(qsTr("Installer"), message, "info", true);
}
function onInstallFinished(success, message) {
logArea.append(message);
- page.bannerText = message;
- page.bannerTone = success ? "success" : "error";
+ page.finishOperation(qsTr("Installer"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
nvidiaInstaller.refreshProprietaryAgreement();
@@ -347,8 +423,7 @@ Item {
function onRemoveFinished(success, message) {
logArea.append(message);
- page.bannerText = message;
- page.bannerTone = success ? "success" : "error";
+ page.finishOperation(qsTr("Installer"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
nvidiaInstaller.refreshProprietaryAgreement();
@@ -360,14 +435,12 @@ Item {
function onProgressMessage(message) {
logArea.append(message);
- page.bannerText = message;
- page.bannerTone = "info";
+ page.setOperationState(qsTr("Updater"), message, "info", true);
}
function onUpdateFinished(success, message) {
logArea.append(message);
- page.bannerText = message;
- page.bannerTone = success ? "success" : "error";
+ page.finishOperation(qsTr("Updater"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 90d447b..dfcfb08 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,28 +1,22 @@
find_package(Qt6 REQUIRED COMPONENTS Test)
+function(ro_control_configure_test target_name)
+ target_include_directories(${target_name} PRIVATE
+ ${CMAKE_SOURCE_DIR}/src
+ ${CMAKE_SOURCE_DIR}/src/backend
+ )
+endfunction()
# ─── Detector Tests ──────────────────────────────────────────────────────────
qt_add_executable(test_detector
test_detector.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/detector.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
-target_include_directories(test_detector PRIVATE
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_SOURCE_DIR}/src/backend
-)
-
-target_compile_definitions(test_detector PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
-)
+ro_control_configure_test(test_detector)
target_link_libraries(test_detector PRIVATE
- Qt6::Core
Qt6::Test
+ ro-control-backend
)
add_test(NAME test_detector COMMAND test_detector)
@@ -30,27 +24,13 @@ add_test(NAME test_detector COMMAND test_detector)
# ─── Updater/Version Parser Tests ────────────────────────────────────────────
qt_add_executable(test_updater
test_updater.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/updater.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/versionparser.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/detector.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
-)
-
-target_include_directories(test_updater PRIVATE
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_SOURCE_DIR}/src/backend
)
-target_compile_definitions(test_updater PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
-)
+ro_control_configure_test(test_updater)
target_link_libraries(test_updater PRIVATE
- Qt6::Core
Qt6::Test
+ ro-control-backend
)
add_test(NAME test_updater COMMAND test_updater)
@@ -58,27 +38,13 @@ add_test(NAME test_updater COMMAND test_updater)
# ─── Monitor Tests ───────────────────────────────────────────────────────────
qt_add_executable(test_monitor
test_monitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/cpumonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/gpumonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/rammonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
-)
-
-target_include_directories(test_monitor PRIVATE
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_SOURCE_DIR}/src/backend
)
-target_compile_definitions(test_monitor PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
-)
+ro_control_configure_test(test_monitor)
target_link_libraries(test_monitor PRIVATE
- Qt6::Core
Qt6::Test
+ ro-control-backend
)
add_test(NAME test_monitor COMMAND test_monitor)
@@ -86,25 +52,13 @@ add_test(NAME test_monitor COMMAND test_monitor)
# ─── System Integration Tests ───────────────────────────────────────────────
qt_add_executable(test_system_integration
test_system_integration.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/dnfmanager.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/polkit.cpp
)
-target_include_directories(test_system_integration PRIVATE
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_SOURCE_DIR}/src/backend
-)
-
-target_compile_definitions(test_system_integration PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
-)
+ro_control_configure_test(test_system_integration)
target_link_libraries(test_system_integration PRIVATE
- Qt6::Core
Qt6::Test
+ ro-control-backend
)
add_test(NAME test_system_integration COMMAND test_system_integration)
@@ -115,9 +69,6 @@ qt_add_executable(test_metadata
)
target_compile_definitions(test_metadata PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
"RO_CONTROL_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\""
)
@@ -132,30 +83,13 @@ add_test(NAME test_metadata COMMAND test_metadata)
qt_add_executable(test_cli
test_cli.cpp
${CMAKE_SOURCE_DIR}/src/cli/cli.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/detector.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/updater.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/nvidia/versionparser.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/cpumonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/gpumonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/monitor/rammonitor.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/commandrunner.cpp
- ${CMAKE_SOURCE_DIR}/src/backend/system/sessionutil.cpp
)
-target_include_directories(test_cli PRIVATE
- ${CMAKE_SOURCE_DIR}/src
- ${CMAKE_SOURCE_DIR}/src/backend
-)
-
-target_compile_definitions(test_cli PRIVATE
- "RO_CONTROL_POLICY_ID=\"${RO_CONTROL_POLICY_ID}\""
- "RO_CONTROL_HELPER_BUILD_PATH=\"${RO_CONTROL_HELPER_BUILD_PATH}\""
- "RO_CONTROL_HELPER_INSTALL_PATH=\"${RO_CONTROL_HELPER_INSTALL_PATH}\""
-)
+ro_control_configure_test(test_cli)
target_link_libraries(test_cli PRIVATE
- Qt6::Core
Qt6::Test
+ ro-control-backend
)
add_test(NAME test_cli COMMAND test_cli)
diff --git a/tests/test_system_integration.cpp b/tests/test_system_integration.cpp
index 432904d..e25f3b8 100644
--- a/tests/test_system_integration.cpp
+++ b/tests/test_system_integration.cpp
@@ -1,6 +1,10 @@
+#include
+#include
+#include
#include
#include
+#include "system/capabilityprobe.h"
#include "system/commandrunner.h"
#include "system/dnfmanager.h"
#include "system/polkit.h"
@@ -9,6 +13,52 @@ class TestSystemIntegration : public QObject {
Q_OBJECT
private slots:
+ void testCommandRunnerUsesProgramOverride() {
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const QString scriptPath = tempDir.filePath(QStringLiteral("fake-dnf.sh"));
+ QFile script(scriptPath);
+ QVERIFY(script.open(QIODevice::WriteOnly | QIODevice::Text));
+ QVERIFY(script.write("#!/bin/sh\nprintf 'override-ok\\n'\nexit 0\n") > 0);
+ script.close();
+ QVERIFY(script.setPermissions(QFileDevice::ReadOwner |
+ QFileDevice::WriteOwner |
+ QFileDevice::ExeOwner));
+
+ qputenv("RO_CONTROL_COMMAND_DNF", scriptPath.toUtf8());
+
+ CommandRunner runner;
+ const auto result = runner.run(QStringLiteral("dnf"));
+ QCOMPARE(result.exitCode, 0);
+ QCOMPARE(result.stdout.trimmed(), QStringLiteral("override-ok"));
+
+ qunsetenv("RO_CONTROL_COMMAND_DNF");
+ }
+
+ void testCapabilityProbeUsesProgramOverride() {
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const QString scriptPath =
+ tempDir.filePath(QStringLiteral("fake-nvidia-smi.sh"));
+ QFile script(scriptPath);
+ QVERIFY(script.open(QIODevice::WriteOnly | QIODevice::Text));
+ QVERIFY(script.write("#!/bin/sh\nexit 0\n") > 0);
+ script.close();
+ QVERIFY(script.setPermissions(QFileDevice::ReadOwner |
+ QFileDevice::WriteOwner |
+ QFileDevice::ExeOwner));
+
+ qputenv("RO_CONTROL_COMMAND_NVIDIA_SMI", scriptPath.toUtf8());
+
+ const auto status = CapabilityProbe::probeTool(QStringLiteral("nvidia-smi"));
+ QVERIFY(status.available);
+ QCOMPARE(QDir::cleanPath(status.resolvedPath), QDir::cleanPath(scriptPath));
+
+ qunsetenv("RO_CONTROL_COMMAND_NVIDIA_SMI");
+ }
+
void testCommandRunnerBasic() {
CommandRunner runner;
const auto result = runner.run(QStringLiteral("true"));
From cfa3d81177ad3b24e23c65fe9118173a3eb0efa8 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:13:55 +0300
Subject: [PATCH 15/22] feat: Add dynamic language selection and management
with a new backend and QML settings page.
---
CMakeLists.txt | 4 +
i18n/ro-control_en.ts | 496 +++++++++++++----
i18n/ro-control_tr.ts | 723 +++++++++++++++++--------
src/backend/system/languagemanager.cpp | 123 +++++
src/backend/system/languagemanager.h | 45 ++
src/main.cpp | 14 +-
src/qml/pages/SettingsPage.qml | 65 +++
7 files changed, 1150 insertions(+), 320 deletions(-)
create mode 100644 src/backend/system/languagemanager.cpp
create mode 100644 src/backend/system/languagemanager.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad9d8b2..e4f6a45 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,7 @@ set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS
Core
+ Qml
Quick # Qt Quick / QML engine
QuickControls2 # QML controls (Button, Slider, etc.)
Widgets # QApplication base
@@ -72,6 +73,7 @@ set(BACKEND_SOURCES
src/backend/system/commandrunner.cpp
src/backend/system/sessionutil.cpp
src/backend/system/capabilityprobe.cpp
+ src/backend/system/languagemanager.cpp
)
set(APP_SOURCES
@@ -85,6 +87,7 @@ add_library(ro-control-backend STATIC ${BACKEND_SOURCES})
target_link_libraries(ro-control-backend PUBLIC
Qt6::Core
Qt6::DBus
+ Qt6::Qml
)
target_compile_options(ro-control-backend PRIVATE
@@ -189,6 +192,7 @@ configure_file(
@ONLY
NEWLINE_STYLE UNIX
)
+execute_process(COMMAND chmod +x ${RO_CONTROL_HELPER_BUILD_PATH})
configure_file(
data/polkit/io.github.ProjectRoASD.rocontrol.policy.in
diff --git a/i18n/ro-control_en.ts b/i18n/ro-control_en.ts
index 30d4826..95e36a5 100644
--- a/i18n/ro-control_en.ts
+++ b/i18n/ro-control_en.ts
@@ -24,8 +24,10 @@
Driver version:
-
-
+
+
+
+ NoneNone
@@ -34,202 +36,380 @@
Secure Boot:
-
+
+
+ Repository Setup
+
+
+
+
+
+ Package Transaction
+
+
+
+
+
+ Kernel Integration
+
+
+
+
+
+ Session Finalization
+
+
+
+
+
+ Update Check
+
+
+
+
+
+ General
+
+
+
+
+ GPU Detection
-
+
+ Detected
-
+
+ Missing
-
+
+ No NVIDIA GPU was detected on this system.
-
+
+ Active Driver
-
+
+ Session:
-
+
+ UnknownUnknown
-
+
+ Installed Version
-
+
+ Latest available:
-
+
+ No pending package update detected.
-
+
+ Secure Boot
-
+
+ EnabledEnabled
-
+
+ Disabled / UnknownDisabled / Unknown
-
+
+ Unsigned kernel modules may require manual signing.
-
+
+ No Secure Boot blocker is currently detected.
-
+
+ Verification
-
+
+ Review driver prerequisites before changing packages.
-
+
+ GPU Ready
-
+
+ GPU Missing
-
+
+ Wayland Session
-
+
+ X11 / Other Session
-
+
+ Nouveau Active
-
+
+ Nouveau Inactive
-
+
+ Kernel Module Loaded
-
+
+ Kernel Module Missing
-
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
-
+
+ X11 sessions require matching userspace packages after install or update.
-
+
+ Driver Actions
-
+
+ Use guided actions to install, switch or remove the current stack.
-
+
+
+ Source:
+
+
+
+
+
+
+
+
+
+ Idle
+
+
+
+
+
+ Phase:
+
+
+
+
+
+ Running
+
+
+
+
+ I accept the detected license / agreement terms
-
+
+ Install Proprietary
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Installer
+
+
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+
+ Install Nouveau
-
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+
+ Remove Driver
-
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+
+
+
+
+
+
+
+
+
+ Updater
+
+
+
+
+
+ Starting update check...
+
+
+
+
+ Apply Latest
-
+
+
+ Updating NVIDIA driver to the latest version...
+
+
+
+
+ Apply Selected
-
+
+
+ Switching NVIDIA driver to selected version:
+
+
+
+
+ Repository versions loaded:
-
+
+ No repository version list has been loaded yet.
-
+
+ Activity Log
-
+
+ Operation output is streamed here in real time.
-
+
+ Clear Log
@@ -258,42 +438,50 @@
Install Open-Source Driver (Nouveau)
-
+
+ Deep CleanDeep Clean
-
+
+ Rescan System
-
+
+ Update Center
-
+
+ Check the repository version and pin a specific build when required.
-
+
+ Installed:
-
+
+ Update Available
-
+
+ Up to Date
-
+
+ Check for UpdatesCheck for Updates
@@ -317,56 +505,67 @@
Main
+ ro-Controlro-Control
+ Driver Control Center
+ System Monitor
+ Preferences
+ Install, verify and update NVIDIA drivers with guided system checks.
+ Track live CPU, GPU and memory telemetry in one place.
+ Tune the interface and review diagnostic context before support work.
+ System DarkSystem Dark
+ System LightSystem Light
+ Compact Layout
+ Comfort Layout
@@ -399,11 +598,15 @@
System Monitoring
+ CPU Load
+
+
+
@@ -411,121 +614,148 @@
+ CPU telemetry is currently unavailable.
+ GPU Load
+ nvidia-smi did not return live GPU telemetry.
+ Memory Usage
+ Used:
+ RAM telemetry is currently unavailable.
+ Live Resource Curves
+ Quick pulse view for the most important machine resources.
+ CPUCPU
+ GPU
+ Health Summary
+ Fast interpretation of the raw telemetry values.
+ CPU Busy
+ CPU Stable
+ GPU Online
+ GPU Telemetry Missing
+ Memory Pressure
+ Memory Stable
+ GPU temperature:
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.
+ Detailed Signals
+ Expanded raw values for support and diagnostics.
+ CPU Temperature
+
+
+
+
@@ -534,41 +764,49 @@
Unknown
+ GPU Temperature
+ VRAM
+ RAM Footprint
+ Actions
+ Trigger a manual refresh when you need a fresh sample.
+ Refresh Telemetry
+ NVIDIA Path OK
+ Check NVIDIA Path
@@ -582,6 +820,7 @@
CPU data unavailable
+ Temperature: Temperature:
@@ -591,6 +830,7 @@
GPU (NVIDIA)
+ NVIDIA GPUNVIDIA GPU
@@ -608,6 +848,7 @@
VRAM:
+ RAMRAM
@@ -621,6 +862,7 @@
Refresh
+ Refresh interval: Refresh interval:
@@ -629,28 +871,28 @@
NvidiaDetector
-
+ Proprietary (NVIDIA)Proprietary (NVIDIA)
-
+ Open Source (Nouveau)Open Source (Nouveau)
-
+ Not Installed / UnknownNot Installed / Unknown
-
-
+
+ NoneNone
-
+ GPU: %1
Driver Version: %2
Secure Boot: %3
@@ -665,42 +907,42 @@ NVIDIA Module: %5
Nouveau: %6
-
+ EnabledEnabled
-
+ Disabled
-
+ Disabled / UnknownDisabled / Unknown
-
+ UnknownUnknown
-
+ LoadedLoaded
-
+ Not loadedNot loaded
-
+ ActiveActive
-
+ InactiveInactive
@@ -977,112 +1219,152 @@ Nouveau: %6
Settings
+ Interface
+ Tune the shell density and how much operational detail the app exposes.
-
+
+
+ Language
+
+
+
+
+
+ Changes the application language immediately and keeps the selection for the next launch.
+
+
+
+
+ Compact layout
-
+
+ Reduces spacing to fit more information on screen.
-
+
+ Show advanced diagnostics
-
+
+ Shows verification reports and expanded monitor metrics.
-
+
+
+ Language:
+
+
+
+
+ System Dark Theme
-
+
+ System Light Theme
-
+
+ Compact Active
-
+
+ Comfort Active
-
+
+ Diagnostics
-
+
+ Useful runtime context before filing issues or performing support work.
-
+
+ Application
-
+
+ GPU
-
+
+ Not detectedNot detected
-
+
+ DriverDriver
-
+
+ Session
-
+
+ UnknownUnknown
-
+
+ Use the Driver page to refresh detection before copying any diagnostic context.
-
+
+ Workflow Guidance
-
+
+ Recommended order of operations when changing drivers.
-
+
+ 1. Verify GPU detection and session type.
2. Install or switch the driver stack.
3. Check repository updates.
@@ -1090,72 +1372,86 @@ Nouveau: %6
-
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.
-
+
+ No Secure Boot blocker is currently reported by the detector.
-
+
+ AboutAbout
-
+
+ Project identity and current shell mode.
-
+
+ Application: Application:
-
+
+ Theme: Theme:
-
+
+ System DarkSystem Dark
-
+
+ System LightSystem Light
-
+
+ Layout density:
-
+
+ Compact
-
+
+ Comfort
-
+
+ Advanced diagnostics:
-
+
+ Visible
-
+
+ Hidden
@@ -1163,21 +1459,25 @@ Nouveau: %6
SidebarMenu
+ Driver ManagementDriver Management
+ System MonitoringSystem Monitoring
+ SettingsSettings
+ ro-Controlro-Control
diff --git a/i18n/ro-control_tr.ts b/i18n/ro-control_tr.ts
index 988de29..5dd51e2 100644
--- a/i18n/ro-control_tr.ts
+++ b/i18n/ro-control_tr.ts
@@ -1,4 +1,5 @@
-
+
+
DriverPage
@@ -23,8 +24,10 @@
Sürücü sürümü:
-
-
+
+
+
+ NoneYok
@@ -33,202 +36,380 @@
Secure Boot:
-
+
+
+ Repository Setup
+
+
+
+
+
+ Package Transaction
+
+
+
+
+
+ Kernel Integration
+
+
+
+
+
+ Session Finalization
+
+
+
+
+
+ Update Check
+
+
+
+
+
+ General
+
+
+
+
+ GPU DetectionGPU Tespiti
-
+
+ DetectedTespit Edildi
-
+
+ MissingBulunamadı
-
+
+ No NVIDIA GPU was detected on this system.Bu sistemde NVIDIA GPU tespit edilemedi.
-
+
+ Active DriverAktif Sürücü
-
+
+ Session: Oturum:
-
+
+ UnknownBilinmiyor
-
+
+ Installed VersionKurulu Sürüm
-
+
+ Latest available: En son sürüm:
-
+
+ No pending package update detected.Bekleyen paket güncellemesi bulunamadı.
-
+
+ Secure BootGüvenli Önyükleme (Secure Boot)
-
+
+ EnabledAçık
-
+
+ Disabled / UnknownKapalı / Bilinmiyor
-
+
+ Unsigned kernel modules may require manual signing.İmzasız kernel modüllerinin manuel olarak imzalanması gerekebilir.
-
+
+ No Secure Boot blocker is currently detected.Şu anda herhangi bir Secure Boot engeli tespit edilmedi.
-
+
+ VerificationDoğrulama
-
+
+ Review driver prerequisites before changing packages.Paketleri değiştirmeden önce sürücü ön koşullarını gözden geçirin.
-
+
+ GPU ReadyGPU Hazır
-
+
+ GPU MissingGPU Bulunamadı
-
+
+ Wayland SessionWayland Oturumu
-
+
+ X11 / Other SessionX11 / Diğer Oturum
-
+
+ Nouveau ActiveNouveau Aktif
-
+
+ Nouveau InactiveNouveau Pasif
-
+
+ Kernel Module LoadedKernel Modülü Yüklü
-
+
+ Kernel Module MissingKernel Modülü Eksik
-
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.Wayland oturumları otomatik olarak nvidia-drm.modeset=1 çekirdek argümanına ihtiyaç duyar.
-
+
+ X11 sessions require matching userspace packages after install or update.X11 oturumları, kurulum veya güncelleme sonrası eşleşen kullanıcı alanı paketleri gerektirir.
-
+
+ Driver ActionsSürücü İşlemleri
-
+
+ Use guided actions to install, switch or remove the current stack.Mevcut yığını kurmak, değiştirmek veya kaldırmak için rehberli işlemleri kullanın.
-
+
+
+ Source:
+
+
+
+
+
+
+
+
+
+ Idle
+
+
+
+
+
+ Phase:
+
+
+
+
+
+ Running
+
+
+
+
+ I accept the detected license / agreement termsTespit edilen lisans / sözleşme koşullarını kabul ediyorum
-
+
+ Install ProprietarySahipli Sürücüyü Kur
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Installer
+
+
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
+
+
+
+ Install NouveauNouveau Kur (Açık Kaynak)
-
+
+
+ Switching to the open-source driver...
+ Açık kaynak sürücüye geçiliyor...
+
+
+
+ Remove DriverSürücüyü Kaldır
-
+
+
+ Removing the NVIDIA driver...
+ NVIDIA sürücüsü kaldırılıyor...
+
+
+
+
+ Cleaning legacy driver leftovers...
+ Eski sürücü kalıntıları temizleniyor...
+
+
+
+
+
+
+
+
+
+
+
+
+ Updater
+
+
+
+
+
+ Starting update check...
+ Güncelleme denetimi başlatılıyor...
+
+
+
+ Apply LatestEn Son Sürümü Uygula
-
+
+
+ Updating NVIDIA driver to the latest version...
+ NVIDIA sürücüsü en yeni sürüme güncelleniyor...
+
+
+
+ Apply SelectedSeçileni Uygula
-
+
+
+ Switching NVIDIA driver to selected version:
+
+
+
+
+ Repository versions loaded: Yüklenen repo sürümleri:
-
+
+ No repository version list has been loaded yet.Henüz hiçbir repo sürüm listesi yüklenmedi.
-
+
+ Activity LogAktivite Günlüğü
-
+
+ Operation output is streamed here in real time.İşlem çıktıları burada gerçek zamanlı olarak gösterilir.
-
+
+ Clear LogGünlüğü Temizle
@@ -257,42 +438,50 @@
Açık Kaynak Sürücüyü Kur (Nouveau)
-
+
+ Deep CleanDerin Temizlik
-
+
+ Rescan SystemSistemi Yeniden Tara
-
+
+ Update CenterGüncelleme Merkezi
-
+
+ Check the repository version and pin a specific build when required.Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
-
+
+ Installed: Kurulu:
-
+
+ Update AvailableGüncelleme Mevcut
-
+
+ Up to DateGüncel
-
+
+ Check for UpdatesGüncellemeleri Kontrol Et
@@ -316,57 +505,68 @@
Main
-
+
+ ro-Controlro-Control
-
+
+ Driver Control CenterSürücü Kontrol Merkezi
-
+
+ System MonitorSistem İzleyici
-
+
+ PreferencesTercihler
-
+
+ Install, verify and update NVIDIA drivers with guided system checks.NVIDIA sürücülerini, rehberli sistem kontrolleriyle kurun, doğrulayın ve güncelleyin.
-
+
+ Track live CPU, GPU and memory telemetry in one place.Canlı CPU, GPU ve bellek telemetrisini tek bir yerden izleyin.
-
+
+ Tune the interface and review diagnostic context before support work.Arayüz yoğunluğunu ayarlayın ve destek çalışmalarından önce sistem raporunu gözden geçirin.
-
+
+ System DarkSistem (Koyu)
-
+
+ System LightSistem (Açık)
-
+
+ Compact LayoutSıkı Görünüm
-
+
+ Comfort LayoutRahat Görünüm
@@ -398,177 +598,216 @@
Sistem İzleme
-
+
+ CPU LoadCPU Yükü
-
-
-
+
+
+
+
+
+ UnavailableKullanılamıyor
-
+
+ CPU telemetry is currently unavailable.CPU telemetrisi şu anda kullanılamıyor.
-
+
+ GPU LoadGPU Yükü
-
+
+ nvidia-smi did not return live GPU telemetry.nvidia-smi canlı GPU telemetrisi döndürmedi.
-
+
+ Memory UsageBellek Kullanımı
-
+
+ Used: Kullanılan:
-
+
+ RAM telemetry is currently unavailable.RAM telemetrisi şu anda kullanılamıyor.
-
+
+ Live Resource CurvesCanlı Kaynak Grafikleri
-
+
+ Quick pulse view for the most important machine resources.En önemli makine kaynakları için hızlı anlık görünüm.
-
+
+ CPUCPU
-
+
+ GPUEkran Kartı (GPU)
-
+
+ Health SummarySağlık Özeti
-
+
+ Fast interpretation of the raw telemetry values.Ham telemetri değerlerinin hızlı yorumlaması.
-
+
+ CPU BusyCPU Meşgul
-
+
+ CPU StableCPU Stabil
-
+
+ GPU OnlineGPU Çevrimiçi
-
+
+ GPU Telemetry MissingGPU Telemetrisi Bulunamadı
-
+
+ Memory PressureBellek Baskısı
-
+
+ Memory StableBellek Stabil
-
+
+ GPU temperature: GPU sıcaklığı:
-
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.GPU metrikleri kullanılamıyor. Sürücü kurulumunu ve nvidia-smi erişilebilirliğini kontrol edin.
-
+
+ Detailed SignalsDetaylı Sinyaller
-
+
+ Expanded raw values for support and diagnostics.Destek ve tanılama için genişletilmiş ham değerler.
-
+
+ CPU TemperatureCPU Sıcaklığı
-
-
-
-
+
+
+
+
+
+
+
+ UnknownBilinmiyor
-
+
+ GPU TemperatureGPU Sıcaklığı
-
+
+ VRAMVRAM Belleği
-
+
+ RAM FootprintRAM Kapladığı Alan
-
+
+ Actionsİşlemler
-
+
+ Trigger a manual refresh when you need a fresh sample.Taze bir örneğe ihtiyacınız olduğunda manuel yenilemeyi tetikleyin.
-
+
+ Refresh TelemetryTelemetriyi Yenile
-
+
+ NVIDIA Path OKNVIDIA Yolu Tamam
-
+
+ Check NVIDIA PathNVIDIA Yolunu Kontrol Et
@@ -581,7 +820,8 @@
CPU verisi alınamıyor
-
+
+ Temperature: Sıcaklık:
@@ -590,7 +830,8 @@
GPU (NVIDIA)
-
+
+ NVIDIA GPUNVIDIA GPU
@@ -607,7 +848,8 @@
VRAM:
-
+
+ RAMRAM
@@ -620,7 +862,8 @@
Yenile
-
+
+ Refresh interval: Yenileme aralığı:
@@ -628,28 +871,28 @@
NvidiaDetector
-
+ Proprietary (NVIDIA)Kapalı Kaynak (NVIDIA)
-
+ Open Source (Nouveau)Açık Kaynak (Nouveau)
-
+ Not Installed / UnknownKurulu Değil / Bilinmiyor
-
-
+
+ NoneYok
-
+ GPU: %1
Driver Version: %2
Secure Boot: %3
@@ -664,42 +907,42 @@ NVIDIA Modülü: %5
Nouveau: %6
-
+ EnabledEtkin
-
+ DisabledDevre Dışı
-
+ Disabled / UnknownDevre Dışı / Bilinmiyor
-
+ UnknownBilinmiyor
-
+ LoadedYüklü
-
+ Not loadedYüklü değil
-
+ ActiveAktif
-
+ InactiveAktif değil
@@ -707,102 +950,102 @@ Nouveau: %6
NvidiaInstaller
-
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
-
+ License agreement acceptance is required before installation.Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
-
+ Checking RPM Fusion repositories...RPM Fusion depoları kontrol ediliyor...
-
+ Platform version could not be detected.Platform sürümü tespit edilemedi.
-
+ Failed to enable RPM Fusion repositories: RPM Fusion depoları etkinleştirilemedi:
-
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
+ Installation failed: Kurulum başarısız:
-
+ Building the kernel module (akmods --force)...Kernel modülü derleniyor (akmods --force)...
-
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.Kapalı kaynak NVIDIA sürücüsü başarıyla kuruldu. Lütfen sistemi yeniden başlatın.
-
+ Switching to the open-source driver...Açık kaynak sürücüye geçiliyor...
-
+ Failed to remove proprietary packages: Kapalı kaynak paketler kaldırılamadı:
-
+ Open-source driver installation failed: Açık kaynak sürücü kurulumu başarısız:
-
+ The open-source driver (Nouveau) was installed. Please restart the system.Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
-
+ Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
-
+ Driver removed successfully.Sürücü başarıyla kaldırıldı.
-
+ Removal failed: Kaldırma başarısız:
-
+ Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
-
+ Deep clean failed: Derin temizlik başarısız:
-
+ DNF cache cleanup failed: DNF önbellek temizliği başarısız:
-
+ Deep clean completed.Derin temizlik tamamlandı.
@@ -811,32 +1054,32 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 uygulanıyor...
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ UnknownBilinmiyor
-
+ Legacy NVIDIA cleanup completed.Eski NVIDIA temizliği tamamlandı.
-
+ Failed to apply the Wayland kernel parameter: Wayland kernel parametresi uygulanamadı:
-
+ X11 detected: checking NVIDIA userspace packages...X11 tespit edildi: NVIDIA userspace paketleri kontrol ediliyor...
-
+ Failed to install the X11 NVIDIA package: X11 NVIDIA paketi kurulamadı:
@@ -848,7 +1091,7 @@ Nouveau: %6
NVIDIA sürücüsü güncelleniyor...
-
+ Update failed: Güncelleme başarısız:
@@ -861,110 +1104,110 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
-
+ Driver updated successfully. Please restart the system.Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
-
-
+
+ dnf not found.dnf bulunamadı.
-
+ No installed NVIDIA driver found.Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
-
+ Update found (version details unavailable).Güncelleme bulundu (sürüm detayları kullanılamıyor).
-
+ Update found: %1Güncelleme bulundu: %1
-
+ Driver is up to date. No new version found.Sürücü güncel. Yeni sürüm bulunamadı.
-
+ Update check failed: %1Güncelleme kontrolü başarısız: %1
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ Kernel module build failed: Kernel modülü inşası başarısız:
-
-
-
+
+
+ unknown errorbilinmeyen hata
-
+ Wayland detected: applying nvidia-drm.modeset=1...Wayland algılandı: nvidia-drm.modeset=1 uygulanıyor...
-
+ Failed to update the Wayland kernel parameter: Wayland çekirdek parametresi güncellenemedi:
-
+ No available versions found.Hiçbir uygun sürüm bulunamadı.
-
+ Available versions: %1Mevcut sürümler: %1
-
+ Starting update check...Güncelleme denetimi başlatılıyor...
-
+ Selected version not found in the repository.Seçili sürüm depoda bulunamadı.
-
+ Updating NVIDIA driver to the latest version...NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
+ Switching NVIDIA driver to selected version: %1NVIDIA sürücüsü seçili sürüme değiştiriliyor: %1
-
+ Rebuilding kernel module...Kernel modülü yeniden derleniyor...
-
+ Latest version installed successfully. Please restart the system.En son sürüm başarıyla yüklendi. Lütfen sistemi yeniden başlatın.
-
+ Selected version applied successfully. Please restart the system.Seçili sürüm başarıyla uygulandı. Lütfen sistemi yeniden başlatın.
@@ -976,112 +1219,152 @@ Nouveau: %6
Ayarlar
-
+
+ InterfaceArayüz
-
+
+ Tune the shell density and how much operational detail the app exposes.Arayüz yoğunluğunu ve uygulamanın ne kadar operasyonel ayrıntı göstereceğini ayarlayın.
-
+
+
+ Language
+
+
+
+
+
+ Changes the application language immediately and keeps the selection for the next launch.
+
+
+
+
+ Compact layoutSıkı görünüm
-
+
+ Reduces spacing to fit more information on screen.Ekrana daha fazla bilgi sığdırmak için boşlukları azaltır.
-
+
+ Show advanced diagnosticsGelişmiş tanılamayı göster
-
+
+ Shows verification reports and expanded monitor metrics.Doğrulama raporlarını ve genişletilmiş monitör metriklerini gösterir.
-
+
+
+ Language:
+
+
+
+
+ System Dark ThemeKoyu Sistem Teması
-
+
+ System Light ThemeAçık Sistem Teması
-
+
+ Compact ActiveSıkı Düzen Etkin
-
+
+ Comfort ActiveRahat Düzen Etkin
-
+
+ DiagnosticsTanılama
-
+
+ Useful runtime context before filing issues or performing support work.Hata bildirmeden veya destek çalışması yapmadan önce yararlı çalışma zamanı bilgisi.
-
+
+ ApplicationUygulama
-
+
+ GPUEkran Kartı (GPU)
-
+
+ Not detectedTespit edilmedi
-
+
+ DriverSürücü
-
+
+ SessionOturum
-
+
+ UnknownBilinmiyor
-
+
+ Use the Driver page to refresh detection before copying any diagnostic context.Tanılama bağlamını kopyalamadan önce algılamayı yenilemek için Sürücü (Driver) sayfasını kullanın.
-
+
+ Workflow Guidanceİş Akışı Rehberi
-
+
+ Recommended order of operations when changing drivers.Sürücü değiştirilirken önerilen işlem sırası.
-
+
+ 1. Verify GPU detection and session type.
2. Install or switch the driver stack.
3. Check repository updates.
@@ -1092,72 +1375,86 @@ Nouveau: %6
4. Başarılı paket işlemlerinden sonra yeniden başlatın.
-
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.Güvenli Önyükleme etkin. Paket kurulumundan sonra kernel modülü imzalanması gerekebilir.
-
+
+ No Secure Boot blocker is currently reported by the detector.Şu anda dedektör tarafından bildirilen herhangi bir (Secure Boot) engeli bulunmuyor.
-
+
+ AboutHakkında
-
+
+ Project identity and current shell mode.Proje kimliği ve mevcut kabuk modu.
-
+
+ Application: Uygulama:
-
+
+ Theme: Tema:
-
+
+ System DarkSistem Koyu
-
+
+ System LightSistem Açık
-
+
+ Layout density: Düzen Görünümü:
-
+
+ CompactSıkı
-
+
+ ComfortRahat
-
+
+ Advanced diagnostics: Gelişmiş Tanılama:
-
+
+ VisibleGörünür
-
+
+ HiddenGizli
@@ -1165,24 +1462,28 @@ Nouveau: %6
SidebarMenu
-
+
+ Driver ManagementSürücü Yönetimi
-
+
+ System MonitoringSistem İzleme
-
+
+ SettingsAyarlar
-
+
+ ro-Controlro-Control
-
\ No newline at end of file
+
diff --git a/src/backend/system/languagemanager.cpp b/src/backend/system/languagemanager.cpp
new file mode 100644
index 0000000..8d434e1
--- /dev/null
+++ b/src/backend/system/languagemanager.cpp
@@ -0,0 +1,123 @@
+#include "languagemanager.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+
+const std::pair kSupportedLanguages[] = {
+ {QStringLiteral("system"), QStringLiteral("System Default")},
+ {QStringLiteral("en"), QStringLiteral("English")},
+ {QStringLiteral("tr"), QStringLiteral("Turkce")},
+};
+
+} // namespace
+
+LanguageManager::LanguageManager(QCoreApplication *application, QQmlEngine *engine,
+ QTranslator *translator, QObject *parent)
+ : QObject(parent), m_application(application), m_engine(engine),
+ m_translator(translator) {
+ QSettings settings;
+ const QString storedLanguage =
+ settings.value(QStringLiteral("ui/language"), QStringLiteral("system"))
+ .toString();
+ setCurrentLanguage(storedLanguage);
+}
+
+QString LanguageManager::currentLanguage() const { return m_currentLanguage; }
+
+QString LanguageManager::currentLanguageLabel() const {
+ return displayNameForLanguage(effectiveLanguageCode(m_currentLanguage));
+}
+
+QVariantList LanguageManager::availableLanguages() const {
+ QVariantList languages;
+ for (const auto &entry : kSupportedLanguages) {
+ QVariantMap language;
+ language.insert(QStringLiteral("code"), entry.first);
+ language.insert(QStringLiteral("label"), entry.second);
+ languages.append(language);
+ }
+
+ return languages;
+}
+
+void LanguageManager::setCurrentLanguage(const QString &languageCode) {
+ const QString normalizedLanguage = normalizeLanguageCode(languageCode);
+ if (normalizedLanguage == m_currentLanguage &&
+ loadLanguage(normalizedLanguage)) {
+ return;
+ }
+
+ if (!loadLanguage(normalizedLanguage)) {
+ return;
+ }
+
+ m_currentLanguage = normalizedLanguage;
+
+ QSettings settings;
+ settings.setValue(QStringLiteral("ui/language"), m_currentLanguage);
+
+ emit currentLanguageChanged();
+}
+
+QString LanguageManager::displayNameForLanguage(
+ const QString &languageCode) const {
+ const QString normalizedLanguage = normalizeLanguageCode(languageCode);
+ for (const auto &entry : kSupportedLanguages) {
+ if (entry.first == normalizedLanguage) {
+ return entry.second;
+ }
+ }
+
+ return normalizedLanguage;
+}
+
+QString LanguageManager::normalizeLanguageCode(const QString &languageCode) const {
+ const QString normalizedLanguage = languageCode.trimmed().toLower();
+ for (const auto &entry : kSupportedLanguages) {
+ if (entry.first == normalizedLanguage) {
+ return normalizedLanguage;
+ }
+ }
+
+ return QStringLiteral("system");
+}
+
+QString LanguageManager::systemLanguageCode() const {
+ return QLocale::system().name().section(QLatin1Char('_'), 0, 0).toLower();
+}
+
+QString LanguageManager::effectiveLanguageCode(const QString &languageCode) const {
+ const QString normalizedLanguage = normalizeLanguageCode(languageCode);
+ return normalizedLanguage == QStringLiteral("system") ? systemLanguageCode()
+ : normalizedLanguage;
+}
+
+bool LanguageManager::loadLanguage(const QString &languageCode) {
+ if (m_application == nullptr || m_engine == nullptr || m_translator == nullptr) {
+ return false;
+ }
+
+ const QString effectiveLanguage = effectiveLanguageCode(languageCode);
+
+ m_application->removeTranslator(m_translator);
+
+ bool loaded = false;
+ if (effectiveLanguage != QStringLiteral("en")) {
+ loaded = m_translator->load(
+ QStringLiteral(":/i18n/ro-control_%1.qm").arg(effectiveLanguage));
+ }
+
+ if (loaded) {
+ m_application->installTranslator(m_translator);
+ }
+
+ m_engine->setUiLanguage(effectiveLanguage);
+ m_engine->retranslate();
+ return true;
+}
diff --git a/src/backend/system/languagemanager.h b/src/backend/system/languagemanager.h
new file mode 100644
index 0000000..179cedb
--- /dev/null
+++ b/src/backend/system/languagemanager.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include
+#include
+#include
+
+class QApplication;
+class QCoreApplication;
+class QQmlEngine;
+class QTranslator;
+
+class LanguageManager : public QObject {
+ Q_OBJECT
+
+ Q_PROPERTY(QString currentLanguage READ currentLanguage WRITE setCurrentLanguage
+ NOTIFY currentLanguageChanged)
+ Q_PROPERTY(QString currentLanguageLabel READ currentLanguageLabel NOTIFY
+ currentLanguageChanged)
+ Q_PROPERTY(QVariantList availableLanguages READ availableLanguages CONSTANT)
+
+public:
+ explicit LanguageManager(QCoreApplication *application, QQmlEngine *engine,
+ QTranslator *translator, QObject *parent = nullptr);
+
+ QString currentLanguage() const;
+ QString currentLanguageLabel() const;
+ QVariantList availableLanguages() const;
+
+ Q_INVOKABLE void setCurrentLanguage(const QString &languageCode);
+ Q_INVOKABLE QString displayNameForLanguage(const QString &languageCode) const;
+
+signals:
+ void currentLanguageChanged();
+
+private:
+ QString normalizeLanguageCode(const QString &languageCode) const;
+ QString systemLanguageCode() const;
+ QString effectiveLanguageCode(const QString &languageCode) const;
+ bool loadLanguage(const QString &languageCode);
+
+ QCoreApplication *m_application = nullptr;
+ QQmlEngine *m_engine = nullptr;
+ QTranslator *m_translator = nullptr;
+ QString m_currentLanguage = QStringLiteral("system");
+};
diff --git a/src/main.cpp b/src/main.cpp
index 409447f..9d86dd6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -17,6 +17,7 @@
#include "backend/nvidia/detector.h"
#include "backend/nvidia/installer.h"
#include "backend/nvidia/updater.h"
+#include "backend/system/languagemanager.h"
#include "cli/cli.h"
namespace {
@@ -217,17 +218,6 @@ int main(int argc, char *argv[]) {
"ro-control", QIcon(":/qt/qml/rocontrol/assets/ro-control-logo.svg")));
QTranslator translator;
- const QString localeName = QLocale::system().name();
- const QString baseLanguage =
- localeName.section(QLatin1Char('_'), 0, 0).toLower();
-
- if (translator.load(
- QStringLiteral(":/i18n/ro-control_%1.qm").arg(localeName)) ||
- translator.load(
- QStringLiteral(":/i18n/ro-control_%1.qm").arg(baseLanguage))) {
- app.installTranslator(&translator);
- }
-
NvidiaDetector detector;
NvidiaInstaller installer;
NvidiaUpdater updater;
@@ -236,6 +226,7 @@ int main(int argc, char *argv[]) {
RamMonitor ramMonitor;
QQmlApplicationEngine engine;
+ LanguageManager languageManager(&app, &engine, &translator);
// Backend nesnelerini tüm QML dosyalarına global olarak aç
engine.rootContext()->setContextProperty("nvidiaDetector", &detector);
@@ -244,6 +235,7 @@ int main(int argc, char *argv[]) {
engine.rootContext()->setContextProperty("cpuMonitor", &cpuMonitor);
engine.rootContext()->setContextProperty("gpuMonitor", &gpuMonitor);
engine.rootContext()->setContextProperty("ramMonitor", &ramMonitor);
+ engine.rootContext()->setContextProperty("languageManager", &languageManager);
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
diff --git a/src/qml/pages/SettingsPage.qml b/src/qml/pages/SettingsPage.qml
index c4fe16e..635f50f 100644
--- a/src/qml/pages/SettingsPage.qml
+++ b/src/qml/pages/SettingsPage.qml
@@ -32,6 +32,52 @@ Item {
title: qsTr("Interface")
subtitle: qsTr("Tune the shell density and how much operational detail the app exposes.")
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 12
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 4
+
+ Label {
+ text: qsTr("Language")
+ font.bold: true
+ color: settingsPage.theme.text
+ }
+
+ Label {
+ text: qsTr("Changes the application language immediately and keeps the selection for the next launch.")
+ wrapMode: Text.Wrap
+ color: settingsPage.theme.textSoft
+ Layout.fillWidth: true
+ }
+ }
+
+ ComboBox {
+ id: languagePicker
+ Layout.preferredWidth: 220
+ model: languageManager.availableLanguages
+ textRole: "label"
+
+ Component.onCompleted: {
+ for (let i = 0; i < model.length; ++i) {
+ if (model[i].code === languageManager.currentLanguage) {
+ currentIndex = i;
+ break;
+ }
+ }
+ }
+
+ onActivated: {
+ const selected = model[currentIndex];
+ if (selected && selected.code) {
+ languageManager.setCurrentLanguage(selected.code);
+ }
+ }
+ }
+ }
+
RowLayout {
Layout.fillWidth: true
@@ -90,6 +136,12 @@ Item {
Layout.fillWidth: true
spacing: 8
+ InfoBadge {
+ text: qsTr("Language: ") + languageManager.currentLanguageLabel
+ backgroundColor: settingsPage.theme.cardStrong
+ foregroundColor: settingsPage.theme.text
+ }
+
InfoBadge {
text: settingsPage.darkMode ? qsTr("System Dark Theme") : qsTr("System Light Theme")
backgroundColor: settingsPage.theme.infoBg
@@ -217,4 +269,17 @@ Item {
}
}
}
+
+ Connections {
+ target: languageManager
+
+ function onCurrentLanguageChanged() {
+ for (let i = 0; i < languagePicker.model.length; ++i) {
+ if (languagePicker.model[i].code === languageManager.currentLanguage) {
+ languagePicker.currentIndex = i;
+ break;
+ }
+ }
+ }
+ }
}
From f1c352f078bc4bfefe9aac99ee45b84fb80b07ea Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:16:42 +0300
Subject: [PATCH 16/22] feat: Add support for initial NVIDIA driver
installation from remote catalog and refine update status reporting.
---
i18n/ro-control_en.ts | 416 ++++++++++++++++--------------
i18n/ro-control_tr.ts | 446 ++++++++++++++++++++-------------
src/backend/nvidia/updater.cpp | 54 +++-
src/qml/pages/DriverPage.qml | 44 +++-
4 files changed, 582 insertions(+), 378 deletions(-)
diff --git a/i18n/ro-control_en.ts b/i18n/ro-control_en.ts
index 95e36a5..aec2d15 100644
--- a/i18n/ro-control_en.ts
+++ b/i18n/ro-control_en.ts
@@ -24,10 +24,10 @@
Driver version:
-
-
-
-
+
+
+
+ NoneNone
@@ -72,344 +72,392 @@
-
-
+
+ GPU Detection
-
-
+
+ Detected
-
-
+
+ Missing
-
-
+
+ No NVIDIA GPU was detected on this system.
-
-
+
+ Active Driver
-
-
+
+ Session:
-
-
+
+ UnknownUnknown
-
-
+
+ Installed Version
-
-
- Latest available:
+
+
+ Latest available online:
-
-
- No pending package update detected.
+
+
+ No pending online package update detected.
-
-
+
+
+ Latest driver found online:
+
+
+
+
+
+ No online driver catalog has been loaded yet.
+
+
+
+
+ Secure Boot
-
-
+
+ EnabledEnabled
-
-
+
+ Disabled / UnknownDisabled / Unknown
-
-
+
+ Unsigned kernel modules may require manual signing.
-
-
+
+ No Secure Boot blocker is currently detected.
-
-
+
+ Verification
-
-
+
+ Review driver prerequisites before changing packages.
-
-
+
+ GPU Ready
-
-
+
+ GPU Missing
-
-
+
+ Wayland Session
-
-
+
+ X11 / Other Session
-
-
+
+ Nouveau Active
-
-
+
+ Nouveau Inactive
-
-
+
+ Kernel Module Loaded
-
-
+
+ Kernel Module Missing
-
-
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
-
-
+
+ X11 sessions require matching userspace packages after install or update.
-
-
+
+ Driver Actions
-
-
+
+ Use guided actions to install, switch or remove the current stack.
-
-
+
+ Source:
-
-
-
-
-
-
+
+
+
+
+
+ Idle
-
-
+
+ Phase:
-
-
+
+ Running
-
-
+
+ I accept the detected license / agreement terms
-
-
+
+ Install Proprietary
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
+ Installer
-
-
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Installing the proprietary NVIDIA driver (akmod-nvidia)...
-
-
+
+ Install Nouveau
-
-
+
+ Switching to the open-source driver...Switching to the open-source driver...
-
-
+
+ Remove Driver
-
-
+
+ Removing the NVIDIA driver...Removing the NVIDIA driver...
-
-
+
+ Cleaning legacy driver leftovers...Cleaning legacy driver leftovers...
-
-
-
-
-
-
-
-
-
-
- Updater
+
+
+ Search the online package catalog, then download and install a matching driver build.
-
-
- Starting update check...
+
+
+ Remote Driver Available
-
-
- Apply Latest
+
+
+ Catalog Not Ready
-
-
- Updating NVIDIA driver to the latest version...
+
+
+
+
+
+
+
+
+
+
+ Updater
-
-
- Apply Selected
+
+
+ Searching the online NVIDIA package catalog...
-
-
- Switching NVIDIA driver to selected version:
+
+
+ Install Latest
-
-
- Repository versions loaded:
+
+
+ Updating NVIDIA driver to the latest online version...
-
-
- No repository version list has been loaded yet.
+
+
+ Downloading and installing the latest online NVIDIA driver...
+
+
+
+
+
+ Switching NVIDIA driver to selected online version:
- Activity Log
+ Downloading and installing selected NVIDIA driver version:
-
-
- Operation output is streamed here in real time.
+
+
+ Online repository versions loaded:
+
+
+
+
+
+ No online repository version list has been loaded yet.
+
+
+
+
+
+ Apply Latest
+
+
+
+
+
+ Apply Selected
+
+
+
+
+
+ Activity Log
+ Operation output is streamed here in real time.
+
+
+
+
+ Clear Log
@@ -438,50 +486,44 @@
Install Open-Source Driver (Nouveau)
-
-
+
+ Deep CleanDeep Clean
-
-
+
+ Rescan System
-
-
+
+ Update Center
-
-
- Check the repository version and pin a specific build when required.
-
-
-
-
-
+
+ Installed:
-
-
+
+ Update Available
-
-
+
+ Up to Date
-
-
+
+ Check for UpdatesCheck for Updates
@@ -1091,7 +1133,7 @@ Nouveau: %6
Updating the NVIDIA driver...
-
+ Update failed: Update failed:
@@ -1104,110 +1146,120 @@ Nouveau: %6
Wayland detected: refreshing nvidia-drm.modeset=1...
-
+ Driver updated successfully. Please restart the system.Driver updated successfully. Please restart the system.
-
-
+
+ dnf not found.
-
- No installed NVIDIA driver found.
+
+ Online NVIDIA packages were found. You can download and install the driver now.
+
+
+
+
+ Online NVIDIA driver found. Latest remote version: %1
+
+
+
+
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.
-
+ Update found (version details unavailable).
-
+ Update found: %1
-
+ Driver is up to date. No new version found.
-
+ Update check failed: %1
-
+ Another driver operation is already running.
-
+ Kernel module build failed:
-
-
-
+
+
+ unknown error
-
+ Wayland detected: applying nvidia-drm.modeset=1...Wayland detected: applying nvidia-drm.modeset=1...
-
+ Failed to update the Wayland kernel parameter:
-
+ No available versions found.
-
+ Available versions: %1
-
+ Starting update check...
-
+ Selected version not found in the repository.
-
+ Updating NVIDIA driver to the latest version...
-
+ Switching NVIDIA driver to selected version: %1
-
+ Rebuilding kernel module...
-
+ Latest version installed successfully. Please restart the system.
-
+ Selected version applied successfully. Please restart the system.
diff --git a/i18n/ro-control_tr.ts b/i18n/ro-control_tr.ts
index 5dd51e2..539483e 100644
--- a/i18n/ro-control_tr.ts
+++ b/i18n/ro-control_tr.ts
@@ -24,10 +24,10 @@
Sürücü sürümü:
-
-
-
-
+
+
+
+ NoneYok
@@ -72,344 +72,416 @@
-
-
+
+ GPU DetectionGPU Tespiti
-
-
+
+ DetectedTespit Edildi
-
-
+
+ MissingBulunamadı
-
-
+
+ No NVIDIA GPU was detected on this system.Bu sistemde NVIDIA GPU tespit edilemedi.
-
-
+
+ Active DriverAktif Sürücü
-
-
+
+ Session: Oturum:
-
-
+
+ UnknownBilinmiyor
-
-
+
+ Installed VersionKurulu Sürüm
-
- Latest available:
- En son sürüm:
+ En son sürüm:
-
- No pending package update detected.
- Bekleyen paket güncellemesi bulunamadı.
+ Bekleyen paket güncellemesi bulunamadı.
-
-
- Secure Boot
- Güvenli Önyükleme (Secure Boot)
+
+
+ Latest available online:
+
+
+
+
+
+ No pending online package update detected.
+
+ Latest driver found online:
+
+
+
+
+
+ No online driver catalog has been loaded yet.
+
+
+
+
+
+ Secure Boot
+ Güvenli Önyükleme (Secure Boot)
+
+
+
+ EnabledAçık
-
-
+
+ Disabled / UnknownKapalı / Bilinmiyor
-
-
+
+ Unsigned kernel modules may require manual signing.İmzasız kernel modüllerinin manuel olarak imzalanması gerekebilir.
-
-
+
+ No Secure Boot blocker is currently detected.Şu anda herhangi bir Secure Boot engeli tespit edilmedi.
-
-
+
+ VerificationDoğrulama
-
-
+
+ Review driver prerequisites before changing packages.Paketleri değiştirmeden önce sürücü ön koşullarını gözden geçirin.
-
-
+
+ GPU ReadyGPU Hazır
-
-
+
+ GPU MissingGPU Bulunamadı
-
-
+
+ Wayland SessionWayland Oturumu
-
-
+
+ X11 / Other SessionX11 / Diğer Oturum
-
-
+
+ Nouveau ActiveNouveau Aktif
-
-
+
+ Nouveau InactiveNouveau Pasif
-
-
+
+ Kernel Module LoadedKernel Modülü Yüklü
-
-
+
+ Kernel Module MissingKernel Modülü Eksik
-
-
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.Wayland oturumları otomatik olarak nvidia-drm.modeset=1 çekirdek argümanına ihtiyaç duyar.
-
-
+
+ X11 sessions require matching userspace packages after install or update.X11 oturumları, kurulum veya güncelleme sonrası eşleşen kullanıcı alanı paketleri gerektirir.
-
-
+
+ Driver ActionsSürücü İşlemleri
-
-
+
+ Use guided actions to install, switch or remove the current stack.Mevcut yığını kurmak, değiştirmek veya kaldırmak için rehberli işlemleri kullanın.
-
-
+
+ Source:
-
-
-
-
-
-
+
+
+
+
+
+ Idle
-
-
+
+ Phase:
-
-
+
+ Running
-
-
+
+ I accept the detected license / agreement termsTespit edilen lisans / sözleşme koşullarını kabul ediyorum
-
-
+
+ Install ProprietarySahipli Sürücüyü Kur
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
+ Installer
-
-
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
-
+
+ Install NouveauNouveau Kur (Açık Kaynak)
-
-
+
+ Switching to the open-source driver...Açık kaynak sürücüye geçiliyor...
-
-
+
+ Remove DriverSürücüyü Kaldır
-
-
+
+ Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
-
-
+
+ Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
-
-
-
-
-
-
-
-
-
-
+
+
+ Search the online package catalog, then download and install a matching driver build.
+
+
+
+
+
+ Remote Driver Available
+
+
+
+
+
+ Catalog Not Ready
+
+
+
+
+
+
+
+
+
+
+
+
+ Updater
-
-
+
+
+ Searching the online NVIDIA package catalog...
+
+
+
+
+
+ Install Latest
+
+
+
+
+
+ Updating NVIDIA driver to the latest online version...
+
+
+
+
+
+ Downloading and installing the latest online NVIDIA driver...
+
+
+
+
+
+ Switching NVIDIA driver to selected online version:
+
+
+
+
+
+ Downloading and installing selected NVIDIA driver version:
+
+
+
+
+
+ Online repository versions loaded:
+
+
+
+
+
+ No online repository version list has been loaded yet.
+
+
+ Starting update check...
- Güncelleme denetimi başlatılıyor...
+ Güncelleme denetimi başlatılıyor...
-
-
+
+ Apply LatestEn Son Sürümü Uygula
-
- Updating NVIDIA driver to the latest version...
- NVIDIA sürücüsü en yeni sürüme güncelleniyor...
+ NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
-
+
+ Apply SelectedSeçileni Uygula
-
-
- Switching NVIDIA driver to selected version:
-
-
-
-
- Repository versions loaded:
- Yüklenen repo sürümleri:
+ Yüklenen repo sürümleri:
-
- No repository version list has been loaded yet.
- Henüz hiçbir repo sürüm listesi yüklenmedi.
+ Henüz hiçbir repo sürüm listesi yüklenmedi.
-
-
+
+ Activity LogAktivite Günlüğü
-
-
+
+ Operation output is streamed here in real time.İşlem çıktıları burada gerçek zamanlı olarak gösterilir.
-
-
+
+ Clear LogGünlüğü Temizle
@@ -438,50 +510,48 @@
Açık Kaynak Sürücüyü Kur (Nouveau)
-
-
+
+ Deep CleanDerin Temizlik
-
-
+
+ Rescan SystemSistemi Yeniden Tara
-
-
+
+ Update CenterGüncelleme Merkezi
-
- Check the repository version and pin a specific build when required.
- Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
+ Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
-
-
+
+ Installed: Kurulu:
-
-
+
+ Update AvailableGüncelleme Mevcut
-
-
+
+ Up to DateGüncel
-
-
+
+ Check for UpdatesGüncellemeleri Kontrol Et
@@ -1091,7 +1161,7 @@ Nouveau: %6
NVIDIA sürücüsü güncelleniyor...
-
+ Update failed: Güncelleme başarısız:
@@ -1104,110 +1174,124 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
-
+ Driver updated successfully. Please restart the system.Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
-
-
+
+ dnf not found.dnf bulunamadı.
- No installed NVIDIA driver found.
- Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
+ Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
+
+
+
+ Online NVIDIA packages were found. You can download and install the driver now.
+
+
+
+
+ Online NVIDIA driver found. Latest remote version: %1
+
+
+
+
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.
+
-
+ Update found (version details unavailable).Güncelleme bulundu (sürüm detayları kullanılamıyor).
-
+ Update found: %1Güncelleme bulundu: %1
-
+ Driver is up to date. No new version found.Sürücü güncel. Yeni sürüm bulunamadı.
-
+ Update check failed: %1Güncelleme kontrolü başarısız: %1
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ Kernel module build failed: Kernel modülü inşası başarısız:
-
-
-
+
+
+ unknown errorbilinmeyen hata
-
+ Wayland detected: applying nvidia-drm.modeset=1...Wayland algılandı: nvidia-drm.modeset=1 uygulanıyor...
-
+ Failed to update the Wayland kernel parameter: Wayland çekirdek parametresi güncellenemedi:
-
+ No available versions found.Hiçbir uygun sürüm bulunamadı.
-
+ Available versions: %1Mevcut sürümler: %1
-
+ Starting update check...Güncelleme denetimi başlatılıyor...
-
+ Selected version not found in the repository.Seçili sürüm depoda bulunamadı.
-
+ Updating NVIDIA driver to the latest version...NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
+ Switching NVIDIA driver to selected version: %1NVIDIA sürücüsü seçili sürüme değiştiriliyor: %1
-
+ Rebuilding kernel module...Kernel modülü yeniden derleniyor...
-
+ Latest version installed successfully. Please restart the system.En son sürüm başarıyla yüklendi. Lütfen sistemi yeniden başlatın.
-
+ Selected version applied successfully. Please restart the system.Seçili sürüm başarıyla uygulandı. Lütfen sistemi yeniden başlatın.
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index 92fbd88..437aa3a 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -46,11 +46,37 @@ struct UpdateStatusSnapshot {
QString currentVersion;
QString latestVersion;
QStringList availableVersions;
+ bool remoteCatalogAvailable = false;
bool updateAvailable = false;
QString message;
};
+QString firstNonEmptyLine(const QString &text) {
+ const QStringList lines = text.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
+ for (const QString &line : lines) {
+ const QString trimmedLine = line.trimmed();
+ if (!trimmedLine.isEmpty()) {
+ return trimmedLine;
+ }
+ }
+
+ return {};
+}
+QString queryLatestRemoteVersion(CommandRunner &runner) {
+ const auto result = runner.run(
+ QStringLiteral("dnf"),
+ {QStringLiteral("--refresh"), QStringLiteral("repoquery"),
+ QStringLiteral("--latest-limit"), QStringLiteral("1"),
+ QStringLiteral("--qf"), QStringLiteral("%{epoch}:%{version}-%{release}"),
+ QStringLiteral("akmod-nvidia")});
+
+ if (!result.success()) {
+ return {};
+ }
+
+ return firstNonEmptyLine(result.stdout);
+}
UpdateStatusSnapshot collectUpdateStatus() {
UpdateStatusSnapshot snapshot;
@@ -65,17 +91,36 @@ UpdateStatusSnapshot collectUpdateStatus() {
CommandRunner runner;
const auto listResult =
runner.run(QStringLiteral("dnf"),
- {QStringLiteral("list"), QStringLiteral("--showduplicates"),
+ {QStringLiteral("--refresh"), QStringLiteral("list"),
+ QStringLiteral("--showduplicates"),
QStringLiteral("akmod-nvidia")});
if (listResult.success()) {
snapshot.availableVersions =
NvidiaVersionParser::parseAvailablePackageVersions(
listResult.stdout, QStringLiteral("akmod-nvidia"));
+ snapshot.remoteCatalogAvailable = !snapshot.availableVersions.isEmpty();
+ }
+
+ snapshot.latestVersion = queryLatestRemoteVersion(runner);
+ if (snapshot.latestVersion.isEmpty() && !snapshot.availableVersions.isEmpty()) {
+ snapshot.latestVersion = snapshot.availableVersions.constLast();
}
if (snapshot.currentVersion.isEmpty()) {
- snapshot.message = NvidiaUpdater::tr("No installed NVIDIA driver found.");
+ if (snapshot.remoteCatalogAvailable) {
+ snapshot.updateAvailable = true;
+ snapshot.message =
+ snapshot.latestVersion.isEmpty()
+ ? NvidiaUpdater::tr(
+ "Online NVIDIA packages were found. You can download and install the driver now.")
+ : NvidiaUpdater::tr(
+ "Online NVIDIA driver found. Latest remote version: %1")
+ .arg(snapshot.latestVersion);
+ } else {
+ snapshot.message = NvidiaUpdater::tr(
+ "No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.");
+ }
return snapshot;
}
@@ -84,8 +129,11 @@ UpdateStatusSnapshot collectUpdateStatus() {
QStringLiteral("akmod-nvidia")});
if (checkResult.exitCode == 100) {
- snapshot.latestVersion = NvidiaVersionParser::parseCheckUpdateVersion(
+ const QString checkUpdateVersion = NvidiaVersionParser::parseCheckUpdateVersion(
checkResult.stdout, QStringLiteral("akmod-nvidia"));
+ if (!checkUpdateVersion.isEmpty()) {
+ snapshot.latestVersion = checkUpdateVersion;
+ }
snapshot.updateAvailable = true;
snapshot.message =
snapshot.latestVersion.isEmpty()
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 48926f9..d61eb51 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -45,6 +45,10 @@ Item {
setOperationState(source, message, success ? "success" : "error", false);
}
+ readonly property bool remoteDriverCatalogAvailable: nvidiaUpdater.availableVersions.length > 0
+ readonly property bool canInstallLatestRemoteDriver: nvidiaDetector.gpuFound && remoteDriverCatalogAvailable
+ readonly property bool driverInstalledLocally: nvidiaUpdater.currentVersion.length > 0
+
ScrollView {
id: pageScroll
anchors.fill: parent
@@ -92,7 +96,13 @@ Item {
theme: page.theme
title: qsTr("Installed Version")
value: nvidiaDetector.driverVersion.length > 0 ? nvidiaDetector.driverVersion : qsTr("None")
- subtitle: nvidiaUpdater.updateAvailable ? qsTr("Latest available: ") + nvidiaUpdater.latestVersion : qsTr("No pending package update detected.")
+ subtitle: page.driverInstalledLocally
+ ? (nvidiaUpdater.updateAvailable
+ ? qsTr("Latest available online: ") + nvidiaUpdater.latestVersion
+ : qsTr("No pending online package update detected."))
+ : (page.remoteDriverCatalogAvailable
+ ? qsTr("Latest driver found online: ") + nvidiaUpdater.latestVersion
+ : qsTr("No online driver catalog has been loaded yet."))
accentColor: page.theme.accentC
busy: page.operationRunning
}
@@ -299,7 +309,7 @@ Item {
Layout.fillWidth: true
theme: page.theme
title: qsTr("Update Center")
- subtitle: qsTr("Check the repository version and pin a specific build when required.")
+ subtitle: qsTr("Search the online package catalog, then download and install a matching driver build.")
RowLayout {
Layout.fillWidth: true
@@ -312,8 +322,12 @@ Item {
}
InfoBadge {
- text: nvidiaUpdater.updateAvailable ? qsTr("Update Available") : qsTr("Up to Date")
- backgroundColor: nvidiaUpdater.updateAvailable ? page.theme.warningBg : page.theme.successBg
+ text: page.driverInstalledLocally
+ ? (nvidiaUpdater.updateAvailable ? qsTr("Update Available") : qsTr("Up to Date"))
+ : (page.remoteDriverCatalogAvailable ? qsTr("Remote Driver Available") : qsTr("Catalog Not Ready"))
+ backgroundColor: page.driverInstalledLocally
+ ? (nvidiaUpdater.updateAvailable ? page.theme.warningBg : page.theme.successBg)
+ : (page.remoteDriverCatalogAvailable ? page.theme.successBg : page.theme.warningBg)
foregroundColor: page.theme.text
}
}
@@ -326,16 +340,19 @@ Item {
text: qsTr("Check for Updates")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy
onClicked: {
- page.setOperationState(qsTr("Updater"), qsTr("Starting update check..."), "info", true);
+ page.setOperationState(qsTr("Updater"), qsTr("Searching the online NVIDIA package catalog..."), "info", true);
nvidiaUpdater.checkForUpdate();
}
}
Button {
- text: qsTr("Apply Latest")
- enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && nvidiaUpdater.updateAvailable
+ text: page.driverInstalledLocally ? qsTr("Apply Latest") : qsTr("Install Latest")
+ enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && (nvidiaUpdater.updateAvailable || page.canInstallLatestRemoteDriver)
onClicked: {
- page.setOperationState(qsTr("Updater"), qsTr("Updating NVIDIA driver to the latest version..."), "info", true);
+ page.setOperationState(qsTr("Updater"), page.driverInstalledLocally
+ ? qsTr("Updating NVIDIA driver to the latest online version...")
+ : qsTr("Downloading and installing the latest online NVIDIA driver..."),
+ "info", true);
nvidiaUpdater.applyUpdate();
}
}
@@ -354,9 +371,12 @@ Item {
Button {
text: qsTr("Apply Selected")
- enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && versionPicker.currentIndex >= 0
+ enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && versionPicker.currentIndex >= 0 && page.remoteDriverCatalogAvailable
onClicked: {
- page.setOperationState(qsTr("Updater"), qsTr("Switching NVIDIA driver to selected version: ") + versionPicker.currentText, "info", true);
+ page.setOperationState(qsTr("Updater"), page.driverInstalledLocally
+ ? qsTr("Switching NVIDIA driver to selected online version: ") + versionPicker.currentText
+ : qsTr("Downloading and installing selected NVIDIA driver version: ") + versionPicker.currentText,
+ "info", true);
nvidiaUpdater.applyVersion(versionPicker.currentText);
}
}
@@ -367,8 +387,8 @@ Item {
wrapMode: Text.Wrap
color: page.theme.textSoft
text: nvidiaUpdater.availableVersions.length > 0
- ? qsTr("Repository versions loaded: ") + nvidiaUpdater.availableVersions.length
- : qsTr("No repository version list has been loaded yet.")
+ ? qsTr("Online repository versions loaded: ") + nvidiaUpdater.availableVersions.length
+ : qsTr("No online repository version list has been loaded yet.")
}
}
From 0c76ab157bc50661efaae84db535faaf28d31c62 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:18:56 +0300
Subject: [PATCH 17/22] feat: Centralize DNF transaction argument construction
for driver updates and refactor CLI argument parsing.
---
src/backend/nvidia/updater.cpp | 44 +++++++++++++++-----
src/backend/nvidia/updater.h | 3 ++
src/main.cpp | 75 ++++++++++++++++++----------------
tests/test_updater.cpp | 34 +++++++++++++++
4 files changed, 111 insertions(+), 45 deletions(-)
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index 437aa3a..fc42ef9 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -216,6 +216,37 @@ NvidiaUpdater::buildDriverTargets(const QString &version,
return targets;
}
+QStringList NvidiaUpdater::buildTransactionArguments(
+ const QString &requestedVersion, const QString &installedVersion,
+ const QString &sessionType) const {
+ const QString normalizedRequestedVersion = requestedVersion.trimmed();
+ const QString normalizedInstalledVersion = installedVersion.trimmed();
+ const QString targetVersion =
+ normalizedRequestedVersion.isEmpty() ? m_latestVersion.trimmed()
+ : normalizedRequestedVersion;
+
+ QStringList args;
+ if (normalizedInstalledVersion.isEmpty()) {
+ args << QStringLiteral("install");
+ } else if (!targetVersion.isEmpty()) {
+ args << QStringLiteral("distro-sync");
+ } else {
+ // Installing the named NVIDIA package set keeps the transaction scoped to
+ // the driver stack instead of invoking a broad system update.
+ args << QStringLiteral("install");
+ }
+
+ args << QStringLiteral("-y") << QStringLiteral("--refresh")
+ << QStringLiteral("--best");
+
+ if (!normalizedInstalledVersion.isEmpty()) {
+ args << QStringLiteral("--allowerasing");
+ }
+
+ args << buildDriverTargets(targetVersion, sessionType);
+ return args;
+}
+
bool NvidiaUpdater::finalizeDriverChange(CommandRunner &runner,
const QString &sessionType,
QString *errorMessage) {
@@ -387,16 +418,9 @@ void NvidiaUpdater::applyVersion(const QString &version) {
},
Qt::QueuedConnection);
- const QStringList packageTargets =
- guard->buildDriverTargets(trimmedVersion, sessionType);
- auto args = QStringList{
- trimmedVersion.isEmpty()
- ? (installedVersion.isEmpty() ? QStringLiteral("install")
- : QStringLiteral("update"))
- : (installedVersion.isEmpty() ? QStringLiteral("install")
- : QStringLiteral("distro-sync")),
- QStringLiteral("-y"), QStringLiteral("--allowerasing")};
- args << packageTargets;
+ const QStringList args =
+ guard->buildTransactionArguments(trimmedVersion, installedVersion,
+ sessionType);
auto result = runner.runAsRoot(QStringLiteral("dnf"), args);
if (!result.success()) {
diff --git a/src/backend/nvidia/updater.h b/src/backend/nvidia/updater.h
index 482d0cc..343681b 100644
--- a/src/backend/nvidia/updater.h
+++ b/src/backend/nvidia/updater.h
@@ -49,6 +49,9 @@ class NvidiaUpdater : public QObject {
void runAsyncTask(const std::function &task);
void setLatestVersion(const QString &version);
void setAvailableVersions(const QStringList &versions);
+ QStringList buildTransactionArguments(const QString &requestedVersion,
+ const QString &installedVersion,
+ const QString &sessionType) const;
QStringList buildDriverTargets(const QString &version,
const QString &sessionType) const;
bool finalizeDriverChange(CommandRunner &runner, const QString &sessionType,
diff --git a/src/main.cpp b/src/main.cpp
index 9d86dd6..b1aef42 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include "backend/monitor/cpumonitor.h"
#include "backend/monitor/gpumonitor.h"
@@ -161,50 +162,54 @@ int main(int argc, char *argv[]) {
const QString applicationDescription =
QStringLiteral("ro-Control GPU driver manager and diagnostics CLI.");
- {
- QCoreApplication cliApp(argc, argv);
- cliApp.setApplicationName(QString::fromLatin1(kApplicationName));
- cliApp.setApplicationVersion(QString::fromLatin1(kApplicationVersion));
+ QStringList arguments;
+ arguments.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ arguments << QString::fromLocal8Bit(argv[i]);
+ }
- const auto command = RoControlCli::parseArguments(
- cliApp.arguments(), cliApp.applicationName(),
- cliApp.applicationVersion(), applicationDescription);
+ const auto command = RoControlCli::parseArguments(
+ arguments, QString::fromLatin1(kApplicationName),
+ QString::fromLatin1(kApplicationVersion), applicationDescription);
- QTextStream out(stdout);
- QTextStream err(stderr);
+ QTextStream out(stdout);
+ QTextStream err(stderr);
- if (command.action == RoControlCli::CommandAction::PrintHelp ||
- command.action == RoControlCli::CommandAction::PrintVersion) {
- out << command.payload;
- if (!command.payload.endsWith(QLatin1Char('\n'))) {
- out << Qt::endl;
- }
- return 0;
+ if (command.action == RoControlCli::CommandAction::PrintHelp ||
+ command.action == RoControlCli::CommandAction::PrintVersion) {
+ out << command.payload;
+ if (!command.payload.endsWith(QLatin1Char('\n'))) {
+ out << Qt::endl;
}
+ return 0;
+ }
- if (command.action == RoControlCli::CommandAction::Invalid) {
- err << command.payload << Qt::endl;
- err << "Run `ro-control --help` for usage." << Qt::endl;
- return 2;
- }
+ if (command.action == RoControlCli::CommandAction::Invalid) {
+ err << command.payload << Qt::endl;
+ err << "Run `ro-control --help` for usage." << Qt::endl;
+ return 2;
+ }
- if (command.action != RoControlCli::CommandAction::LaunchGui) {
- const auto result = executeCliCommand(command, cliApp.applicationName(),
- cliApp.applicationVersion());
- if (!result.stdoutText.isEmpty()) {
- out << result.stdoutText;
- if (!result.stdoutText.endsWith(QLatin1Char('\n'))) {
- out << Qt::endl;
- }
+ if (command.action != RoControlCli::CommandAction::LaunchGui) {
+ QCoreApplication cliApp(argc, argv);
+ cliApp.setApplicationName(QString::fromLatin1(kApplicationName));
+ cliApp.setApplicationVersion(QString::fromLatin1(kApplicationVersion));
+
+ const auto result = executeCliCommand(command, cliApp.applicationName(),
+ cliApp.applicationVersion());
+ if (!result.stdoutText.isEmpty()) {
+ out << result.stdoutText;
+ if (!result.stdoutText.endsWith(QLatin1Char('\n'))) {
+ out << Qt::endl;
}
- if (!result.stderrText.isEmpty()) {
- err << result.stderrText;
- if (!result.stderrText.endsWith(QLatin1Char('\n'))) {
- err << Qt::endl;
- }
+ }
+ if (!result.stderrText.isEmpty()) {
+ err << result.stderrText;
+ if (!result.stderrText.endsWith(QLatin1Char('\n'))) {
+ err << Qt::endl;
}
- return result.exitCode;
}
+ return result.exitCode;
}
QApplication app(argc, argv);
diff --git a/tests/test_updater.cpp b/tests/test_updater.cpp
index 18b359d..0cf3e75 100644
--- a/tests/test_updater.cpp
+++ b/tests/test_updater.cpp
@@ -1,5 +1,8 @@
#include
+#define private public
+#include "nvidia/updater.h"
+#undef private
#include "nvidia/versionparser.h"
class TestUpdater : public QObject {
@@ -51,6 +54,37 @@ private slots:
QCOMPARE(specs.at(1),
QStringLiteral("xorg-x11-drv-nvidia-3:570.153.02-1.fc42"));
}
+
+ void testBuildTransactionArgumentsForFreshInstallStaysScoped() {
+ NvidiaUpdater updater;
+ updater.m_latestVersion = QStringLiteral("3:570.153.02-1.fc42");
+
+ const QStringList args =
+ updater.buildTransactionArguments(QString(), QString(), QString());
+
+ QCOMPARE(args.value(0), QStringLiteral("install"));
+ QVERIFY(args.contains(QStringLiteral("--refresh")));
+ QVERIFY(args.contains(QStringLiteral("--best")));
+ QVERIFY(!args.contains(QStringLiteral("update")));
+ QVERIFY(!args.contains(QStringLiteral("upgrade")));
+ QVERIFY(!args.contains(QStringLiteral("system-upgrade")));
+ QVERIFY(args.contains(QStringLiteral("akmod-nvidia-3:570.153.02-1.fc42")));
+ }
+
+ void testBuildTransactionArgumentsForInstalledDriverAvoidsBroadUpdate() {
+ NvidiaUpdater updater;
+ updater.m_latestVersion = QStringLiteral("3:570.153.02-1.fc42");
+
+ const QStringList args = updater.buildTransactionArguments(
+ QString(), QStringLiteral("3:565.77-1.fc42"), QString());
+
+ QCOMPARE(args.value(0), QStringLiteral("distro-sync"));
+ QVERIFY(args.contains(QStringLiteral("--allowerasing")));
+ QVERIFY(!args.contains(QStringLiteral("update")));
+ QVERIFY(!args.contains(QStringLiteral("upgrade")));
+ QVERIFY(!args.contains(QStringLiteral("system-upgrade")));
+ QVERIFY(args.contains(QStringLiteral("akmod-nvidia-3:570.153.02-1.fc42")));
+ }
};
QTEST_MAIN(TestUpdater)
From 08bcdebadac4b5dadcf30eb4cdd5545a5cb40165 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:38:19 +0300
Subject: [PATCH 18/22] feat: Enhance driver operations with detailed progress
logging, elapsed time, and improved version management UI.
---
src/backend/monitor/cpumonitor.cpp | 126 +++++++++++++++----
src/backend/monitor/gpumonitor.cpp | 70 ++++++++---
src/backend/nvidia/installer.cpp | 186 ++++++++++++-----------------
src/backend/nvidia/updater.cpp | 156 ++++++++++++++++++------
src/backend/nvidia/updater.h | 3 +-
src/main.cpp | 23 ++++
src/qml/pages/DriverPage.qml | 172 ++++++++++++++++++++++++--
src/qml/pages/MonitorPage.qml | 63 ++++++----
tests/test_monitor.cpp | 36 ++++++
tests/test_updater.cpp | 24 ++++
10 files changed, 643 insertions(+), 216 deletions(-)
diff --git a/src/backend/monitor/cpumonitor.cpp b/src/backend/monitor/cpumonitor.cpp
index 9fc81e8..18da29f 100644
--- a/src/backend/monitor/cpumonitor.cpp
+++ b/src/backend/monitor/cpumonitor.cpp
@@ -2,6 +2,7 @@
#include "cpumonitor.h"
+#include
#include
#include
@@ -9,36 +10,119 @@
namespace {
-int readCpuTemperatureC() {
- for (int i = 0; i < 32; ++i) {
- QFile thermalFile(QString("/sys/class/thermal/thermal_zone%1/temp").arg(i));
- if (!thermalFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- continue;
- }
+QString readFileText(const QString &path) {
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return {};
+ }
+
+ return QString::fromUtf8(file.readAll()).trimmed();
+}
+
+int parseMilliCelsius(const QString &value) {
+ bool ok = false;
+ const int milliC = value.trimmed().toInt(&ok);
+ return ok && milliC > 0 ? milliC / 1000 : 0;
+}
- const QByteArray raw = thermalFile.readAll().trimmed();
- bool ok = false;
- const int milliC = raw.toInt(&ok);
- if (ok && milliC > 0) {
- return milliC / 1000;
+bool isPreferredCpuSensorType(const QString &sensorType) {
+ const QString lowered = sensorType.trimmed().toLower();
+ return lowered.contains(QStringLiteral("cpu")) ||
+ lowered.contains(QStringLiteral("pkg")) ||
+ lowered.contains(QStringLiteral("package")) ||
+ lowered.contains(QStringLiteral("core")) ||
+ lowered.contains(QStringLiteral("k10temp")) ||
+ lowered.contains(QStringLiteral("tctl")) ||
+ lowered.contains(QStringLiteral("tdie")) ||
+ lowered.contains(QStringLiteral("x86_pkg_temp"));
+}
+
+int readFirstValidTemperature(const QStringList &paths) {
+ for (const QString &path : paths) {
+ const int temperatureC = parseMilliCelsius(readFileText(path));
+ if (temperatureC > 0) {
+ return temperatureC;
}
}
- for (int i = 0; i < 32; ++i) {
- QFile hwmonFile(QString("/sys/class/hwmon/hwmon%1/temp1_input").arg(i));
- if (!hwmonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- continue;
+ return 0;
+}
+
+int readCpuTemperatureFromThermalZones() {
+ QDir thermalDir(QStringLiteral("/sys/class/thermal"));
+ const QFileInfoList entries = thermalDir.entryInfoList(
+ {QStringLiteral("thermal_zone*")}, QDir::Dirs | QDir::NoDotAndDotDot,
+ QDir::Name);
+
+ QStringList preferredPaths;
+ QStringList fallbackPaths;
+
+ for (const QFileInfo &entry : entries) {
+ const QString basePath = entry.absoluteFilePath();
+ const QString type = readFileText(basePath + QStringLiteral("/type"));
+ const QString tempPath = basePath + QStringLiteral("/temp");
+
+ if (isPreferredCpuSensorType(type)) {
+ preferredPaths << tempPath;
+ } else {
+ fallbackPaths << tempPath;
}
+ }
+
+ const int preferredTemperature = readFirstValidTemperature(preferredPaths);
+ if (preferredTemperature > 0) {
+ return preferredTemperature;
+ }
- const QByteArray raw = hwmonFile.readAll().trimmed();
- bool ok = false;
- const int milliC = raw.toInt(&ok);
- if (ok && milliC > 0) {
- return milliC / 1000;
+ return readFirstValidTemperature(fallbackPaths);
+}
+
+int readCpuTemperatureFromHwmon() {
+ QDir hwmonDir(QStringLiteral("/sys/class/hwmon"));
+ const QFileInfoList entries = hwmonDir.entryInfoList(
+ {QStringLiteral("hwmon*")}, QDir::Dirs | QDir::NoDotAndDotDot,
+ QDir::Name);
+
+ QStringList preferredPaths;
+ QStringList fallbackPaths;
+
+ for (const QFileInfo &entry : entries) {
+ const QString basePath = entry.absoluteFilePath();
+ const QString sensorName =
+ readFileText(basePath + QStringLiteral("/name")).toLower();
+ const bool preferredSensor = isPreferredCpuSensorType(sensorName);
+
+ const QFileInfoList inputs = QDir(basePath).entryInfoList(
+ {QStringLiteral("temp*_input")}, QDir::Files, QDir::Name);
+ for (const QFileInfo &input : inputs) {
+ const QString inputPath = input.absoluteFilePath();
+ const QString labelPath =
+ inputPath.left(inputPath.size() - QStringLiteral("_input").size()) +
+ QStringLiteral("_label");
+ const QString label = readFileText(labelPath);
+ if (preferredSensor || isPreferredCpuSensorType(label)) {
+ preferredPaths << inputPath;
+ } else {
+ fallbackPaths << inputPath;
+ }
}
}
- return 0;
+ const int preferredTemperature = readFirstValidTemperature(preferredPaths);
+ if (preferredTemperature > 0) {
+ return preferredTemperature;
+ }
+
+ return readFirstValidTemperature(fallbackPaths);
+}
+
+int readCpuTemperatureC() {
+ const int thermalZoneTemperature = readCpuTemperatureFromThermalZones();
+ if (thermalZoneTemperature > 0) {
+ return thermalZoneTemperature;
+ }
+
+ return readCpuTemperatureFromHwmon();
}
} // namespace
diff --git a/src/backend/monitor/gpumonitor.cpp b/src/backend/monitor/gpumonitor.cpp
index 1a7f8fa..e52e36c 100644
--- a/src/backend/monitor/gpumonitor.cpp
+++ b/src/backend/monitor/gpumonitor.cpp
@@ -2,6 +2,41 @@
#include "system/commandrunner.h"
#include
+#include
+
+namespace {
+
+QString normalizedMetricField(const QString &field) {
+ QString normalized = field.trimmed();
+ normalized.remove(QRegularExpression(QStringLiteral(R"(\s*\[[^\]]+\]\s*)")));
+ normalized.remove(QRegularExpression(QStringLiteral(R"(\s*%\s*)")));
+ return normalized.trimmed();
+}
+
+bool parseMetricInt(const QString &field, int *value) {
+ if (value == nullptr) {
+ return false;
+ }
+
+ const QString normalized = normalizedMetricField(field);
+ if (normalized.isEmpty() ||
+ normalized.compare(QStringLiteral("n/a"), Qt::CaseInsensitive) == 0 ||
+ normalized.compare(QStringLiteral("not supported"), Qt::CaseInsensitive) == 0 ||
+ normalized.compare(QStringLiteral("unknown"), Qt::CaseInsensitive) == 0) {
+ return false;
+ }
+
+ bool ok = false;
+ const int parsedValue = normalized.toInt(&ok);
+ if (!ok) {
+ return false;
+ }
+
+ *value = parsedValue;
+ return true;
+}
+
+} // namespace
GpuMonitor::GpuMonitor(QObject *parent) : QObject(parent) {
m_timer.setInterval(1500);
@@ -59,32 +94,39 @@ void GpuMonitor::refresh() {
return;
}
- bool ok = true;
const QString nextName = fields.at(0).trimmed();
- const int nextTemp = fields.at(1).trimmed().toInt(&ok);
- if (!ok) {
- setAvailable(false);
- clearMetrics();
- return;
- }
+ int nextTemp = 0;
+ int nextUtil = 0;
+ int nextUsed = 0;
+ int nextTotal = 0;
- const int nextUtil = fields.at(2).trimmed().toInt(&ok);
- const int nextUsed = fields.at(3).trimmed().toInt(&ok);
- const int nextTotal = fields.at(4).trimmed().toInt(&ok);
- if (!ok || nextTotal < 0 || nextUsed < 0) {
+ const bool tempAvailable = parseMetricInt(fields.at(1), &nextTemp);
+ const bool utilAvailable = parseMetricInt(fields.at(2), &nextUtil);
+ const bool usedAvailable = parseMetricInt(fields.at(3), &nextUsed);
+ const bool totalAvailable = parseMetricInt(fields.at(4), &nextTotal);
+
+ if (nextTotal < 0 || nextUsed < 0) {
setAvailable(false);
clearMetrics();
return;
}
- const int usagePercent =
- nextTotal > 0
+ const int usagePercent = (usedAvailable && totalAvailable && nextTotal > 0)
? std::clamp(static_cast((static_cast(nextUsed) /
static_cast(nextTotal)) *
100.0),
0, 100)
: 0;
+ const bool telemetryAvailable = !nextName.isEmpty() || tempAvailable ||
+ utilAvailable || usedAvailable ||
+ totalAvailable;
+ if (!telemetryAvailable) {
+ setAvailable(false);
+ clearMetrics();
+ return;
+ }
+
if (m_gpuName != nextName) {
m_gpuName = nextName;
emit gpuNameChanged();
@@ -95,7 +137,7 @@ void GpuMonitor::refresh() {
emit temperatureCChanged();
}
- if (m_utilizationPercent != nextUtil) {
+ if (m_utilizationPercent != std::clamp(nextUtil, 0, 100)) {
m_utilizationPercent = std::clamp(nextUtil, 0, 100);
emit utilizationPercentChanged();
}
diff --git a/src/backend/nvidia/installer.cpp b/src/backend/nvidia/installer.cpp
index 8586920..d6acb7c 100644
--- a/src/backend/nvidia/installer.cpp
+++ b/src/backend/nvidia/installer.cpp
@@ -8,6 +8,66 @@
#include
#include
+namespace {
+
+void emitProgressAsync(const QPointer &guard,
+ const QString &message) {
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, message]() {
+ if (guard) {
+ emit guard->progressMessage(message);
+ }
+ },
+ Qt::QueuedConnection);
+}
+
+void attachRunnerLogging(CommandRunner &runner,
+ const QPointer &guard) {
+ QObject::connect(&runner, &CommandRunner::outputLine, guard,
+ [guard](const QString &message) {
+ emitProgressAsync(guard, message);
+ });
+
+ QObject::connect(&runner, &CommandRunner::errorLine, guard,
+ [guard](const QString &message) {
+ emitProgressAsync(guard, message);
+ });
+
+ QObject::connect(
+ &runner, &CommandRunner::commandStarted, guard,
+ [guard](const QString &program, const QStringList &args, int attempt) {
+ QStringList visibleArgs = args;
+ if (!visibleArgs.isEmpty() &&
+ visibleArgs.constFirst().contains(QStringLiteral("ro-control-helper"))) {
+ visibleArgs.removeFirst();
+ }
+
+ const QString commandLine =
+ QStringLiteral("$ %1 %2")
+ .arg(program, visibleArgs.join(QLatin1Char(' ')).trimmed());
+ emitProgressAsync(
+ guard, NvidiaInstaller::tr("Starting command (attempt %1): %2")
+ .arg(attempt)
+ .arg(commandLine.trimmed()));
+ });
+
+ QObject::connect(&runner, &CommandRunner::commandFinished, guard,
+ [guard](const QString &program, int exitCode, int attempt,
+ int elapsedMs) {
+ emitProgressAsync(
+ guard,
+ NvidiaInstaller::tr(
+ "Command finished (attempt %1, exit %2, %3 ms): %4")
+ .arg(attempt)
+ .arg(exitCode)
+ .arg(elapsedMs)
+ .arg(program));
+ });
+}
+
+} // namespace
+
NvidiaInstaller::NvidiaInstaller(QObject *parent) : QObject(parent) {
refreshProprietaryAgreement();
}
@@ -105,30 +165,10 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
}
CommandRunner runner;
- QObject::connect(&runner, &CommandRunner::outputLine, guard,
- [guard](const QString &message) {
- if (!guard) {
- return;
- }
- QMetaObject::invokeMethod(
- guard,
- [guard, message]() {
- if (guard) {
- emit guard->progressMessage(message);
- }
- },
- Qt::QueuedConnection);
- });
+ attachRunnerLogging(runner, guard);
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaInstaller::tr("Checking RPM Fusion repositories..."));
- }
- },
- Qt::QueuedConnection);
+ emitProgressAsync(guard,
+ NvidiaInstaller::tr("Checking RPM Fusion repositories..."));
CommandRunner rpmRunner;
const auto fedoraResult =
@@ -174,15 +214,9 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
return;
}
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(NvidiaInstaller::tr(
- "Installing the proprietary NVIDIA driver (akmod-nvidia)..."));
- }
- },
- Qt::QueuedConnection);
+ emitProgressAsync(
+ guard, NvidiaInstaller::tr(
+ "Installing the proprietary NVIDIA driver (akmod-nvidia)..."));
result = runner.runAsRoot(QStringLiteral("dnf"),
{QStringLiteral("install"), QStringLiteral("-y"),
@@ -202,15 +236,9 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
return;
}
- QMetaObject::invokeMethod(
+ emitProgressAsync(
guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaInstaller::tr("Building the kernel module (akmods --force)..."));
- }
- },
- Qt::QueuedConnection);
+ NvidiaInstaller::tr("Building the kernel module (akmods --force)..."));
runner.runAsRoot(QStringLiteral("akmods"), {QStringLiteral("--force")});
const QString sessionType = SessionUtil::detectSessionType();
@@ -249,30 +277,10 @@ void NvidiaInstaller::installOpenSource() {
}
CommandRunner runner;
- QObject::connect(&runner, &CommandRunner::outputLine, guard,
- [guard](const QString &message) {
- if (!guard) {
- return;
- }
- QMetaObject::invokeMethod(
- guard,
- [guard, message]() {
- if (guard) {
- emit guard->progressMessage(message);
- }
- },
- Qt::QueuedConnection);
- });
+ attachRunnerLogging(runner, guard);
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaInstaller::tr("Switching to the open-source driver..."));
- }
- },
- Qt::QueuedConnection);
+ emitProgressAsync(guard,
+ NvidiaInstaller::tr("Switching to the open-source driver..."));
auto result = runner.runAsRoot(
QStringLiteral("dnf"),
@@ -339,30 +347,10 @@ void NvidiaInstaller::remove() {
}
CommandRunner runner;
- QObject::connect(&runner, &CommandRunner::outputLine, guard,
- [guard](const QString &message) {
- if (!guard) {
- return;
- }
- QMetaObject::invokeMethod(
- guard,
- [guard, message]() {
- if (guard) {
- emit guard->progressMessage(message);
- }
- },
- Qt::QueuedConnection);
- });
+ attachRunnerLogging(runner, guard);
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaInstaller::tr("Removing the NVIDIA driver..."));
- }
- },
- Qt::QueuedConnection);
+ emitProgressAsync(guard,
+ NvidiaInstaller::tr("Removing the NVIDIA driver..."));
const auto result = runner.runAsRoot(
QStringLiteral("dnf"),
@@ -393,30 +381,10 @@ void NvidiaInstaller::deepClean() {
}
CommandRunner runner;
- QObject::connect(&runner, &CommandRunner::outputLine, guard,
- [guard](const QString &message) {
- if (!guard) {
- return;
- }
- QMetaObject::invokeMethod(
- guard,
- [guard, message]() {
- if (guard) {
- emit guard->progressMessage(message);
- }
- },
- Qt::QueuedConnection);
- });
+ attachRunnerLogging(runner, guard);
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaInstaller::tr("Cleaning legacy driver leftovers..."));
- }
- },
- Qt::QueuedConnection);
+ emitProgressAsync(guard,
+ NvidiaInstaller::tr("Cleaning legacy driver leftovers..."));
const auto removeResult = runner.runAsRoot(
QStringLiteral("dnf"),
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index fc42ef9..5ff3c74 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -42,6 +42,10 @@ QString commandError(const CommandRunner::Result &result,
return fallback;
}
+QString normalizedTransactionOutput(const CommandRunner::Result &result) {
+ return (result.stdout + QLatin1Char('\n') + result.stderr).toLower();
+}
+
struct UpdateStatusSnapshot {
QString currentVersion;
QString latestVersion;
@@ -152,6 +156,62 @@ UpdateStatusSnapshot collectUpdateStatus() {
return snapshot;
}
+void emitProgressAsync(const QPointer &guard,
+ const QString &message) {
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, message]() {
+ if (guard) {
+ emit guard->progressMessage(message);
+ }
+ },
+ Qt::QueuedConnection);
+}
+
+void attachRunnerLogging(CommandRunner &runner,
+ const QPointer &guard) {
+ QObject::connect(&runner, &CommandRunner::outputLine, guard,
+ [guard](const QString &message) {
+ emitProgressAsync(guard, message);
+ });
+
+ QObject::connect(&runner, &CommandRunner::errorLine, guard,
+ [guard](const QString &message) {
+ emitProgressAsync(guard, message);
+ });
+
+ QObject::connect(
+ &runner, &CommandRunner::commandStarted, guard,
+ [guard](const QString &program, const QStringList &args, int attempt) {
+ QStringList visibleArgs = args;
+ if (!visibleArgs.isEmpty() &&
+ visibleArgs.constFirst().contains(QStringLiteral("ro-control-helper"))) {
+ visibleArgs.removeFirst();
+ }
+
+ const QString commandLine =
+ QStringLiteral("$ %1 %2")
+ .arg(program, visibleArgs.join(QLatin1Char(' ')).trimmed());
+ emitProgressAsync(
+ guard, NvidiaUpdater::tr("Starting command (attempt %1): %2")
+ .arg(attempt)
+ .arg(commandLine.trimmed()));
+ });
+
+ QObject::connect(&runner, &CommandRunner::commandFinished, guard,
+ [guard](const QString &program, int exitCode, int attempt,
+ int elapsedMs) {
+ emitProgressAsync(
+ guard,
+ NvidiaUpdater::tr(
+ "Command finished (attempt %1, exit %2, %3 ms): %4")
+ .arg(attempt)
+ .arg(exitCode)
+ .arg(elapsedMs)
+ .arg(program));
+ });
+}
+
} // namespace
NvidiaUpdater::NvidiaUpdater(QObject *parent) : QObject(parent) {}
@@ -200,6 +260,21 @@ void NvidiaUpdater::setAvailableVersions(const QStringList &versions) {
emit availableVersionsChanged();
}
+bool NvidiaUpdater::transactionChanged(const CommandRunner::Result &result) const {
+ const QString output = normalizedTransactionOutput(result);
+
+ if (output.contains(QStringLiteral("nothing to do")) ||
+ output.contains(QStringLiteral("nothing to do.")) ||
+ output.contains(QStringLiteral("no packages marked for upgrade")) ||
+ output.contains(QStringLiteral("no packages marked for update")) ||
+ output.contains(QStringLiteral("no packages marked for reinstall")) ||
+ output.contains(QStringLiteral("package is already installed"))) {
+ return false;
+ }
+
+ return true;
+}
+
QString NvidiaUpdater::detectSessionType() const {
return SessionUtil::detectSessionType();
}
@@ -355,20 +430,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
}
CommandRunner runner;
- QObject::connect(&runner, &CommandRunner::outputLine, guard,
- [guard](const QString &message) {
- if (!guard) {
- return;
- }
- QMetaObject::invokeMethod(
- guard,
- [guard, message]() {
- if (guard) {
- emit guard->progressMessage(message);
- }
- },
- Qt::QueuedConnection);
- });
+ attachRunnerLogging(runner, guard);
if (QStandardPaths::findExecutable(QStringLiteral("dnf")).isEmpty()) {
QMetaObject::invokeMethod(
@@ -401,22 +463,13 @@ void NvidiaUpdater::applyVersion(const QString &version) {
return;
}
- QMetaObject::invokeMethod(
- guard,
- [guard, trimmedVersion]() {
- if (!guard) {
- return;
- }
-
- emit guard->progressMessage(
- trimmedVersion.isEmpty()
- ? NvidiaUpdater::tr(
- "Updating NVIDIA driver to the latest version...")
- : NvidiaUpdater::tr(
- "Switching NVIDIA driver to selected version: %1")
- .arg(trimmedVersion));
- },
- Qt::QueuedConnection);
+ emitProgressAsync(
+ guard, trimmedVersion.isEmpty()
+ ? NvidiaUpdater::tr(
+ "Updating NVIDIA driver to the latest version...")
+ : NvidiaUpdater::tr(
+ "Switching NVIDIA driver to selected version: %1")
+ .arg(trimmedVersion));
const QStringList args =
guard->buildTransactionArguments(trimmedVersion, installedVersion,
@@ -438,15 +491,40 @@ void NvidiaUpdater::applyVersion(const QString &version) {
return;
}
- QMetaObject::invokeMethod(
- guard,
- [guard]() {
- if (guard) {
- emit guard->progressMessage(
- NvidiaUpdater::tr("Rebuilding kernel module..."));
- }
- },
- Qt::QueuedConnection);
+ if (!guard->transactionChanged(result)) {
+ const UpdateStatusSnapshot snapshot = collectUpdateStatus();
+ const QString noChangeMessage =
+ trimmedVersion.isEmpty()
+ ? NvidiaUpdater::tr(
+ "Driver is already at the latest available version.")
+ : NvidiaUpdater::tr(
+ "Selected driver version is already installed.");
+
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, snapshot, noChangeMessage]() {
+ if (!guard) {
+ return;
+ }
+
+ if (guard->m_currentVersion != snapshot.currentVersion) {
+ guard->m_currentVersion = snapshot.currentVersion;
+ emit guard->currentVersionChanged();
+ }
+ if (guard->m_updateAvailable != snapshot.updateAvailable) {
+ guard->m_updateAvailable = snapshot.updateAvailable;
+ emit guard->updateAvailableChanged();
+ }
+ guard->setLatestVersion(snapshot.latestVersion);
+ guard->setAvailableVersions(snapshot.availableVersions);
+ emit guard->progressMessage(noChangeMessage);
+ emit guard->updateFinished(true, noChangeMessage);
+ },
+ Qt::QueuedConnection);
+ return;
+ }
+
+ emitProgressAsync(guard, NvidiaUpdater::tr("Rebuilding kernel module..."));
QString finalizeError;
if (!guard->finalizeDriverChange(runner, sessionType, &finalizeError)) {
diff --git a/src/backend/nvidia/updater.h b/src/backend/nvidia/updater.h
index 343681b..884063b 100644
--- a/src/backend/nvidia/updater.h
+++ b/src/backend/nvidia/updater.h
@@ -5,7 +5,7 @@
#include
#include
-class CommandRunner;
+#include "system/commandrunner.h"
// NvidiaUpdater: Kurulu surucu ile mevcut en guncel surumu karsilastirir.
class NvidiaUpdater : public QObject {
@@ -49,6 +49,7 @@ class NvidiaUpdater : public QObject {
void runAsyncTask(const std::function &task);
void setLatestVersion(const QString &version);
void setAvailableVersions(const QStringList &versions);
+ bool transactionChanged(const CommandRunner::Result &result) const;
QStringList buildTransactionArguments(const QString &requestedVersion,
const QString &installedVersion,
const QString &sessionType) const;
diff --git a/src/main.cpp b/src/main.cpp
index b1aef42..80db609 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -7,6 +7,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -153,6 +155,25 @@ CliExecutionResult executeCliCommand(const RoControlCli::ParsedCommand &command,
return result;
}
+void configureGuiGraphicsEnvironment() {
+#if defined(Q_OS_LINUX)
+ const QByteArray sessionType =
+ qgetenv("XDG_SESSION_TYPE").trimmed().toLower();
+
+ // NVIDIA on Fedora/X11 is generally more stable through the GLX path than
+ // the EGL/DRI2 integration that can emit startup errors.
+ if (sessionType == "x11" && qEnvironmentVariableIsEmpty("QT_XCB_GL_INTEGRATION")) {
+ qputenv("QT_XCB_GL_INTEGRATION", QByteArrayLiteral("glx"));
+ }
+
+ // Keep an explicit escape hatch for hosts that still need software rendering.
+ if (!qEnvironmentVariableIsEmpty("RO_CONTROL_FORCE_SOFTWARE_RENDER")) {
+ qputenv("QT_QUICK_BACKEND", QByteArrayLiteral("software"));
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
+ }
+#endif
+}
+
} // namespace
int main(int argc, char *argv[]) {
@@ -212,6 +233,8 @@ int main(int argc, char *argv[]) {
return result.exitCode;
}
+ configureGuiGraphicsEnvironment();
+
QApplication app(argc, argv);
app.setApplicationName(QString::fromLatin1(kApplicationName));
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index d61eb51..ef2d712 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -16,6 +16,9 @@ Item {
property string operationPhase: ""
property string operationDetail: ""
property bool operationRunning: nvidiaInstaller.busy || nvidiaUpdater.busy
+ property double operationStartedAt: 0
+ property double lastLogAt: 0
+ property int operationElapsedSeconds: 0
function classifyOperationPhase(message) {
const lowered = message.toLowerCase();
@@ -33,10 +36,17 @@ Item {
}
function setOperationState(source, message, tone, running) {
+ if (running && !operationRunning)
+ operationStartedAt = Date.now();
+ if (!running)
+ operationStartedAt = 0;
operationSource = source;
operationDetail = message;
operationPhase = classifyOperationPhase(message);
operationRunning = running;
+ operationElapsedSeconds = operationRunning && operationStartedAt > 0
+ ? Math.max(0, Math.floor((Date.now() - operationStartedAt) / 1000))
+ : 0;
bannerText = (operationPhase.length > 0 ? operationPhase + ": " : "") + message;
bannerTone = tone;
}
@@ -45,9 +55,131 @@ Item {
setOperationState(source, message, success ? "success" : "error", false);
}
+ function formatTimestamp(epochMs) {
+ if (epochMs <= 0)
+ return "--:--:--";
+ const stamp = new Date(epochMs);
+ return Qt.formatTime(stamp, "HH:mm:ss");
+ }
+
+ function formatDuration(totalSeconds) {
+ const seconds = Math.max(0, totalSeconds);
+ const hours = Math.floor(seconds / 3600);
+ const minutes = Math.floor((seconds % 3600) / 60);
+ const remainingSeconds = seconds % 60;
+
+ function pad(value) {
+ return value < 10 ? "0" + value : value.toString();
+ }
+
+ if (hours > 0)
+ return hours + ":" + pad(minutes) + ":" + pad(remainingSeconds);
+ return minutes + ":" + pad(remainingSeconds);
+ }
+
+ function cleanVersionLabel(rawVersion) {
+ let normalized = (rawVersion || "").trim();
+ if (normalized.length === 0)
+ return "";
+
+ const epochIndex = normalized.indexOf(":");
+ if (epochIndex >= 0)
+ normalized = normalized.substring(epochIndex + 1);
+
+ const releaseMatch = normalized.match(/^([0-9]+(?:\.[0-9]+)+)/);
+ if (releaseMatch && releaseMatch.length > 1)
+ return releaseMatch[1];
+
+ const hyphenIndex = normalized.indexOf("-");
+ if (hyphenIndex > 0)
+ return normalized.substring(0, hyphenIndex);
+
+ return normalized;
+ }
+
+ function compareVersionLabels(leftVersion, rightVersion) {
+ const leftParts = cleanVersionLabel(leftVersion).split(".");
+ const rightParts = cleanVersionLabel(rightVersion).split(".");
+ const maxLength = Math.max(leftParts.length, rightParts.length);
+
+ for (let i = 0; i < maxLength; ++i) {
+ const leftValue = i < leftParts.length ? parseInt(leftParts[i], 10) : 0;
+ const rightValue = i < rightParts.length ? parseInt(rightParts[i], 10) : 0;
+
+ if (leftValue > rightValue)
+ return -1;
+ if (leftValue < rightValue)
+ return 1;
+ }
+
+ return 0;
+ }
+
+ function buildVersionTitle(displayVersion, isInstalled, isLatest) {
+ const tags = [];
+ if (isInstalled)
+ tags.push(qsTr("Installed"));
+ if (isLatest)
+ tags.push(qsTr("Latest"));
+
+ return tags.length > 0
+ ? displayVersion + " (" + tags.join(", ") + ")"
+ : displayVersion;
+ }
+
+ function buildAvailableVersionOptions(rawVersions) {
+ const options = [];
+ const seenLabels = {};
+ const installedVersionLabel = cleanVersionLabel(nvidiaUpdater.currentVersion);
+ const latestVersionLabel = cleanVersionLabel(nvidiaUpdater.latestVersion);
+
+ for (let i = 0; i < rawVersions.length; ++i) {
+ const rawVersion = rawVersions[i];
+ const displayVersion = cleanVersionLabel(rawVersion);
+ if (displayVersion.length === 0 || seenLabels[displayVersion])
+ continue;
+
+ seenLabels[displayVersion] = true;
+ const isInstalled = installedVersionLabel.length > 0 && displayVersion === installedVersionLabel;
+ const isLatest = latestVersionLabel.length > 0 && displayVersion === latestVersionLabel;
+ options.push({
+ rawVersion: rawVersion,
+ displayVersion: displayVersion,
+ versionTitle: buildVersionTitle(displayVersion, isInstalled, isLatest),
+ isInstalled: isInstalled,
+ isLatest: isLatest
+ });
+ }
+
+ options.sort(function(left, right) {
+ return compareVersionLabels(left.displayVersion, right.displayVersion);
+ });
+
+ return options;
+ }
+
+ function appendLog(source, message) {
+ const now = Date.now();
+ lastLogAt = now;
+ logArea.append("[" + formatTimestamp(now) + "] " + source + ": " + message);
+ logArea.cursorPosition = logArea.length;
+ }
+
+ Timer {
+ interval: 1000
+ repeat: true
+ running: page.operationRunning
+ onTriggered: {
+ if (page.operationStartedAt > 0)
+ page.operationElapsedSeconds = Math.max(0, Math.floor((Date.now() - page.operationStartedAt) / 1000));
+ }
+ }
+
readonly property bool remoteDriverCatalogAvailable: nvidiaUpdater.availableVersions.length > 0
readonly property bool canInstallLatestRemoteDriver: nvidiaDetector.gpuFound && remoteDriverCatalogAvailable
readonly property bool driverInstalledLocally: nvidiaUpdater.currentVersion.length > 0
+ readonly property string latestVersionLabel: cleanVersionLabel(nvidiaUpdater.latestVersion)
+ readonly property var availableVersionOptions: buildAvailableVersionOptions(nvidiaUpdater.availableVersions)
ScrollView {
id: pageScroll
@@ -98,10 +230,10 @@ Item {
value: nvidiaDetector.driverVersion.length > 0 ? nvidiaDetector.driverVersion : qsTr("None")
subtitle: page.driverInstalledLocally
? (nvidiaUpdater.updateAvailable
- ? qsTr("Latest available online: ") + nvidiaUpdater.latestVersion
+ ? qsTr("Latest available online: ") + page.latestVersionLabel
: qsTr("No pending online package update detected."))
: (page.remoteDriverCatalogAvailable
- ? qsTr("Latest driver found online: ") + nvidiaUpdater.latestVersion
+ ? qsTr("Latest driver found online: ") + page.latestVersionLabel
: qsTr("No online driver catalog has been loaded yet."))
accentColor: page.theme.accentC
busy: page.operationRunning
@@ -219,6 +351,20 @@ Item {
backgroundColor: page.operationRunning ? page.theme.warningBg : page.theme.successBg
foregroundColor: page.theme.text
}
+
+ InfoBadge {
+ text: qsTr("Elapsed: ") + page.formatDuration(page.operationElapsedSeconds)
+ backgroundColor: page.theme.cardStrong
+ foregroundColor: page.theme.text
+ visible: page.operationRunning || page.operationElapsedSeconds > 0
+ }
+
+ InfoBadge {
+ text: qsTr("Last Log: ") + page.formatTimestamp(page.lastLogAt)
+ backgroundColor: page.theme.cardStrong
+ foregroundColor: page.theme.text
+ visible: page.lastLogAt > 0
+ }
}
StatusBanner {
@@ -361,12 +507,13 @@ Item {
RowLayout {
Layout.fillWidth: true
spacing: 10
- visible: nvidiaUpdater.availableVersions.length > 0
+ visible: page.availableVersionOptions.length > 0
ComboBox {
id: versionPicker
Layout.fillWidth: true
- model: nvidiaUpdater.availableVersions
+ model: page.availableVersionOptions
+ textRole: "versionTitle"
}
Button {
@@ -377,7 +524,7 @@ Item {
? qsTr("Switching NVIDIA driver to selected online version: ") + versionPicker.currentText
: qsTr("Downloading and installing selected NVIDIA driver version: ") + versionPicker.currentText,
"info", true);
- nvidiaUpdater.applyVersion(versionPicker.currentText);
+ nvidiaUpdater.applyVersion(page.availableVersionOptions[versionPicker.currentIndex].rawVersion);
}
}
}
@@ -417,7 +564,10 @@ Item {
Button {
text: qsTr("Clear Log")
- onClicked: logArea.text = ""
+ onClicked: {
+ logArea.text = ""
+ page.lastLogAt = 0
+ }
}
}
}
@@ -428,12 +578,12 @@ Item {
target: nvidiaInstaller
function onProgressMessage(message) {
- logArea.append(message);
+ page.appendLog(qsTr("Installer"), message);
page.setOperationState(qsTr("Installer"), message, "info", true);
}
function onInstallFinished(success, message) {
- logArea.append(message);
+ page.appendLog(qsTr("Installer"), message);
page.finishOperation(qsTr("Installer"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
@@ -442,7 +592,7 @@ Item {
}
function onRemoveFinished(success, message) {
- logArea.append(message);
+ page.appendLog(qsTr("Installer"), message);
page.finishOperation(qsTr("Installer"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
@@ -454,12 +604,12 @@ Item {
target: nvidiaUpdater
function onProgressMessage(message) {
- logArea.append(message);
+ page.appendLog(qsTr("Updater"), message);
page.setOperationState(qsTr("Updater"), message, "info", true);
}
function onUpdateFinished(success, message) {
- logArea.append(message);
+ page.appendLog(qsTr("Updater"), message);
page.finishOperation(qsTr("Updater"), success, message);
nvidiaDetector.refresh();
nvidiaUpdater.checkForUpdate();
diff --git a/src/qml/pages/MonitorPage.qml b/src/qml/pages/MonitorPage.qml
index a7fb3aa..54dbe26 100644
--- a/src/qml/pages/MonitorPage.qml
+++ b/src/qml/pages/MonitorPage.qml
@@ -9,6 +9,19 @@ Item {
property bool darkMode: false
property bool compactMode: false
property bool showAdvancedInfo: true
+ readonly property bool cpuTemperatureAvailable: cpuMonitor.temperatureC > 0
+ readonly property bool gpuTelemetryAvailable: gpuMonitor.available || gpuMonitor.gpuName.length > 0
+ readonly property bool gpuTemperatureAvailable: gpuMonitor.temperatureC > 0
+ readonly property bool gpuMemoryAvailable: gpuMonitor.memoryTotalMiB > 0
+ readonly property bool ramTelemetryAvailable: ramMonitor.available || ramMonitor.totalMiB > 0
+
+ function formatTemperature(value) {
+ return value > 0 ? value + " C" : qsTr("Unavailable");
+ }
+
+ function formatMemoryUsage(usedMiB, totalMiB) {
+ return totalMiB > 0 ? usedMiB + " / " + totalMiB + " MiB" : qsTr("Unavailable");
+ }
ScrollView {
id: pageScroll
@@ -32,7 +45,7 @@ Item {
title: qsTr("CPU Load")
value: cpuMonitor.available ? cpuMonitor.usagePercent.toFixed(1) + "%" : qsTr("Unavailable")
subtitle: cpuMonitor.available
- ? qsTr("Temperature: ") + cpuMonitor.temperatureC + " C"
+ ? qsTr("Temperature: ") + page.formatTemperature(cpuMonitor.temperatureC)
: qsTr("CPU telemetry is currently unavailable.")
accentColor: page.theme.accentA
emphasized: cpuMonitor.available && cpuMonitor.usagePercent >= 85
@@ -42,24 +55,24 @@ Item {
Layout.fillWidth: true
theme: page.theme
title: qsTr("GPU Load")
- value: gpuMonitor.available ? gpuMonitor.utilizationPercent + "%" : qsTr("Unavailable")
- subtitle: gpuMonitor.available
+ value: page.gpuTelemetryAvailable ? gpuMonitor.utilizationPercent + "%" : qsTr("Unavailable")
+ subtitle: page.gpuTelemetryAvailable
? (gpuMonitor.gpuName.length > 0 ? gpuMonitor.gpuName : qsTr("NVIDIA GPU"))
: qsTr("nvidia-smi did not return live GPU telemetry.")
accentColor: page.theme.accentB
- emphasized: gpuMonitor.available && gpuMonitor.temperatureC >= 80
+ emphasized: page.gpuTemperatureAvailable && gpuMonitor.temperatureC >= 80
}
StatCard {
Layout.fillWidth: true
theme: page.theme
title: qsTr("Memory Usage")
- value: ramMonitor.available ? ramMonitor.usagePercent + "%" : qsTr("Unavailable")
- subtitle: ramMonitor.available
- ? qsTr("Used: ") + ramMonitor.usedMiB + " / " + ramMonitor.totalMiB + " MiB"
+ value: page.ramTelemetryAvailable ? ramMonitor.usagePercent + "%" : qsTr("Unavailable")
+ subtitle: page.ramTelemetryAvailable
+ ? qsTr("Used: ") + page.formatMemoryUsage(ramMonitor.usedMiB, ramMonitor.totalMiB)
: qsTr("RAM telemetry is currently unavailable.")
accentColor: page.theme.accentC
- emphasized: ramMonitor.available && ramMonitor.usagePercent >= 85
+ emphasized: page.ramTelemetryAvailable && ramMonitor.usagePercent >= 85
}
}
@@ -103,7 +116,6 @@ Item {
from: 0
to: 100
value: gpuMonitor.utilizationPercent
- visible: gpuMonitor.available
}
Label {
@@ -138,14 +150,14 @@ Item {
}
InfoBadge {
- text: gpuMonitor.available ? qsTr("GPU Online") : qsTr("GPU Telemetry Missing")
- backgroundColor: gpuMonitor.available ? page.theme.successBg : page.theme.warningBg
+ text: page.gpuTelemetryAvailable ? qsTr("GPU Online") : qsTr("GPU Telemetry Missing")
+ backgroundColor: page.gpuTelemetryAvailable ? page.theme.successBg : page.theme.warningBg
foregroundColor: page.theme.text
}
InfoBadge {
- text: ramMonitor.available && ramMonitor.usagePercent >= 85 ? qsTr("Memory Pressure") : qsTr("Memory Stable")
- backgroundColor: ramMonitor.available && ramMonitor.usagePercent >= 85 ? page.theme.warningBg : page.theme.successBg
+ text: page.ramTelemetryAvailable && ramMonitor.usagePercent >= 85 ? qsTr("Memory Pressure") : qsTr("Memory Stable")
+ backgroundColor: page.ramTelemetryAvailable && ramMonitor.usagePercent >= 85 ? page.theme.warningBg : page.theme.successBg
foregroundColor: page.theme.text
}
}
@@ -154,8 +166,8 @@ Item {
Layout.fillWidth: true
wrapMode: Text.Wrap
color: page.theme.textSoft
- text: gpuMonitor.available
- ? qsTr("GPU temperature: ") + gpuMonitor.temperatureC + " C, VRAM " + gpuMonitor.memoryUsedMiB + " / " + gpuMonitor.memoryTotalMiB + " MiB."
+ text: page.gpuTelemetryAvailable
+ ? qsTr("GPU temperature: ") + page.formatTemperature(gpuMonitor.temperatureC) + qsTr(", VRAM ") + page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) + "."
: qsTr("GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.")
}
@@ -187,7 +199,7 @@ Item {
}
Label {
- text: cpuMonitor.temperatureC > 0 ? cpuMonitor.temperatureC + " C" : qsTr("Unknown")
+ text: page.formatTemperature(cpuMonitor.temperatureC)
color: page.theme.text
}
@@ -197,7 +209,7 @@ Item {
}
Label {
- text: gpuMonitor.available && gpuMonitor.temperatureC > 0 ? gpuMonitor.temperatureC + " C" : qsTr("Unknown")
+ text: page.gpuTelemetryAvailable ? page.formatTemperature(gpuMonitor.temperatureC) : qsTr("Unknown")
color: page.theme.text
}
@@ -207,7 +219,7 @@ Item {
}
Label {
- text: gpuMonitor.available ? gpuMonitor.memoryUsedMiB + " / " + gpuMonitor.memoryTotalMiB + " MiB" : qsTr("Unknown")
+ text: page.gpuTelemetryAvailable ? page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) : qsTr("Unknown")
color: page.theme.text
}
@@ -217,7 +229,7 @@ Item {
}
Label {
- text: ramMonitor.available ? ramMonitor.usedMiB + " / " + ramMonitor.totalMiB + " MiB" : qsTr("Unknown")
+ text: page.ramTelemetryAvailable ? page.formatMemoryUsage(ramMonitor.usedMiB, ramMonitor.totalMiB) : qsTr("Unknown")
color: page.theme.text
}
}
@@ -247,8 +259,8 @@ Item {
}
InfoBadge {
- text: gpuMonitor.available ? qsTr("NVIDIA Path OK") : qsTr("Check NVIDIA Path")
- backgroundColor: gpuMonitor.available ? page.theme.successBg : page.theme.warningBg
+ text: page.gpuTelemetryAvailable ? qsTr("NVIDIA Path OK") : qsTr("Check NVIDIA Path")
+ backgroundColor: page.gpuTelemetryAvailable ? page.theme.successBg : page.theme.warningBg
foregroundColor: page.theme.text
}
}
@@ -256,4 +268,13 @@ Item {
}
}
}
+
+ Component.onCompleted: {
+ cpuMonitor.start();
+ gpuMonitor.start();
+ ramMonitor.start();
+ cpuMonitor.refresh();
+ gpuMonitor.refresh();
+ ramMonitor.refresh();
+ }
}
diff --git a/tests/test_monitor.cpp b/tests/test_monitor.cpp
index c596404..30228eb 100644
--- a/tests/test_monitor.cpp
+++ b/tests/test_monitor.cpp
@@ -1,4 +1,8 @@
#include
+#include
+#include
+#include
+#include
#include "monitor/cpumonitor.h"
#include "monitor/gpumonitor.h"
@@ -71,6 +75,38 @@ private slots:
QVERIFY(gpu.running());
}
+ void testGpuPartialTelemetryKeepsMonitorAvailable() {
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const QString scriptPath = tempDir.filePath(QStringLiteral("fake-nvidia-smi.sh"));
+ QFile script(scriptPath);
+ QVERIFY(script.open(QIODevice::WriteOnly | QIODevice::Text));
+
+ QTextStream stream(&script);
+ stream << "#!/bin/sh\n";
+ stream << "printf 'NVIDIA GeForce RTX 4080, 61, N/A, 2048, 16384\\n'\n";
+ script.close();
+ QVERIFY(QFile::setPermissions(scriptPath, QFileDevice::ReadOwner |
+ QFileDevice::WriteOwner |
+ QFileDevice::ExeOwner));
+
+ qputenv("RO_CONTROL_COMMAND_NVIDIA_SMI", scriptPath.toUtf8());
+ GpuMonitor gpu;
+ gpu.stop();
+ gpu.refresh();
+
+ QVERIFY(gpu.available());
+ QCOMPARE(gpu.gpuName(), QStringLiteral("NVIDIA GeForce RTX 4080"));
+ QCOMPARE(gpu.temperatureC(), 61);
+ QCOMPARE(gpu.utilizationPercent(), 0);
+ QCOMPARE(gpu.memoryUsedMiB(), 2048);
+ QCOMPARE(gpu.memoryTotalMiB(), 16384);
+ QCOMPARE(gpu.memoryUsagePercent(), 12);
+
+ qunsetenv("RO_CONTROL_COMMAND_NVIDIA_SMI");
+ }
+
void testRamConstruction() {
RamMonitor ram;
QVERIFY(ram.running());
diff --git a/tests/test_updater.cpp b/tests/test_updater.cpp
index 0cf3e75..5a0bcae 100644
--- a/tests/test_updater.cpp
+++ b/tests/test_updater.cpp
@@ -85,6 +85,30 @@ private slots:
QVERIFY(!args.contains(QStringLiteral("system-upgrade")));
QVERIFY(args.contains(QStringLiteral("akmod-nvidia-3:570.153.02-1.fc42")));
}
+
+ void testTransactionChangedReturnsFalseForNoopOutput() {
+ NvidiaUpdater updater;
+ CommandRunner::Result result{
+ .exitCode = 0,
+ .stdout = QStringLiteral("Last metadata expiration check: 0:00:12 ago.\nNothing to do.\n"),
+ .stderr = QString(),
+ .attempt = 1,
+ };
+
+ QVERIFY(!updater.transactionChanged(result));
+ }
+
+ void testTransactionChangedReturnsTrueForRealPackageTransaction() {
+ NvidiaUpdater updater;
+ CommandRunner::Result result{
+ .exitCode = 0,
+ .stdout = QStringLiteral("Installing:\nakmod-nvidia.x86_64 3:570.153.02-1.fc42\nComplete!\n"),
+ .stderr = QString(),
+ .attempt = 1,
+ };
+
+ QVERIFY(updater.transactionChanged(result));
+ }
};
QTEST_MAIN(TestUpdater)
From 24a0ce1d08380ad18de133a11f79ebea259f2a02 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 20:52:24 +0300
Subject: [PATCH 19/22] feat: Add unit tests for DriverPage, refactor operation
state, improve installed version display, and enhance backend parsing and
error handling.
---
src/backend/monitor/rammonitor.cpp | 23 +-
src/backend/nvidia/installer.cpp | 17 +-
src/qml/pages/DriverPage.qml | 13 +-
tests/CMakeLists.txt | 22 +-
tests/test_driver_page.cpp | 446 +++++++++++++++++++++++++++++
5 files changed, 505 insertions(+), 16 deletions(-)
create mode 100644 tests/test_driver_page.cpp
diff --git a/src/backend/monitor/rammonitor.cpp b/src/backend/monitor/rammonitor.cpp
index 4d825d1..d761a77 100644
--- a/src/backend/monitor/rammonitor.cpp
+++ b/src/backend/monitor/rammonitor.cpp
@@ -46,26 +46,31 @@ void RamMonitor::refresh() {
qint64 shmemKiB = -1;
// TR: "Anahtar: deger" satirlarini guvenli sekilde ayriştir.
- // EN: Safely parse "Key: value" lines.
- static const QRegularExpression lineRe(
- QStringLiteral(R"(^([A-Za-z_]+):\s+(\d+))"));
-
QTextStream stream(&meminfo);
while (!stream.atEnd()) {
- const QString line = stream.readLine();
+ const QString line = stream.readLine().trimmed();
+ if (line.isEmpty()) {
+ continue;
+ }
- const auto match = lineRe.match(line);
- if (!match.hasMatch()) {
+ const int colonIdx = line.indexOf(QLatin1Char(':'));
+ if (colonIdx <= 0) {
continue;
}
+ const QString key = line.left(colonIdx).trimmed();
+ const QString valueStr = line.mid(colonIdx + 1).trimmed();
+
+ // The value might be "19842104 kB". We just need the number.
+ const int spaceIdx = valueStr.indexOf(QLatin1Char(' '));
+ const QString numStr = spaceIdx > 0 ? valueStr.left(spaceIdx) : valueStr;
+
bool ok = false;
- const qint64 value = match.captured(2).toLongLong(&ok);
+ const qint64 value = numStr.toLongLong(&ok);
if (!ok) {
continue;
}
- const QString key = match.captured(1);
if (key == QStringLiteral("MemTotal")) {
memTotalKiB = value;
} else if (key == QStringLiteral("MemAvailable")) {
diff --git a/src/backend/nvidia/installer.cpp b/src/backend/nvidia/installer.cpp
index d6acb7c..85dfa23 100644
--- a/src/backend/nvidia/installer.cpp
+++ b/src/backend/nvidia/installer.cpp
@@ -239,7 +239,22 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
emitProgressAsync(
guard,
NvidiaInstaller::tr("Building the kernel module (akmods --force)..."));
- runner.runAsRoot(QStringLiteral("akmods"), {QStringLiteral("--force")});
+ result = runner.runAsRoot(QStringLiteral("akmods"), {QStringLiteral("--force")});
+ if (!result.success()) {
+ const QString error =
+ NvidiaInstaller::tr("Kernel module build failed: ") +
+ (result.stderr.trimmed().isEmpty() ? result.stdout.trimmed()
+ : result.stderr.trimmed());
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, error]() {
+ if (guard) {
+ emit guard->installFinished(false, error);
+ }
+ },
+ Qt::QueuedConnection);
+ return;
+ }
const QString sessionType = SessionUtil::detectSessionType();
QString sessionError;
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index ef2d712..08b0543 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -15,10 +15,12 @@ Item {
property string operationSource: ""
property string operationPhase: ""
property string operationDetail: ""
- property bool operationRunning: nvidiaInstaller.busy || nvidiaUpdater.busy
+ property bool operationActive: false
property double operationStartedAt: 0
property double lastLogAt: 0
property int operationElapsedSeconds: 0
+ readonly property bool backendBusy: nvidiaInstaller.busy || nvidiaUpdater.busy
+ readonly property bool operationRunning: page.operationActive || page.backendBusy
function classifyOperationPhase(message) {
const lowered = message.toLowerCase();
@@ -43,7 +45,7 @@ Item {
operationSource = source;
operationDetail = message;
operationPhase = classifyOperationPhase(message);
- operationRunning = running;
+ operationActive = running;
operationElapsedSeconds = operationRunning && operationStartedAt > 0
? Math.max(0, Math.floor((Date.now() - operationStartedAt) / 1000))
: 0;
@@ -177,7 +179,8 @@ Item {
readonly property bool remoteDriverCatalogAvailable: nvidiaUpdater.availableVersions.length > 0
readonly property bool canInstallLatestRemoteDriver: nvidiaDetector.gpuFound && remoteDriverCatalogAvailable
- readonly property bool driverInstalledLocally: nvidiaUpdater.currentVersion.length > 0
+ readonly property bool driverInstalledLocally: nvidiaDetector.driverVersion.length > 0 || nvidiaUpdater.currentVersion.length > 0
+ readonly property string installedVersionLabel: nvidiaDetector.driverVersion.length > 0 ? nvidiaDetector.driverVersion : nvidiaUpdater.currentVersion
readonly property string latestVersionLabel: cleanVersionLabel(nvidiaUpdater.latestVersion)
readonly property var availableVersionOptions: buildAvailableVersionOptions(nvidiaUpdater.availableVersions)
@@ -227,7 +230,7 @@ Item {
Layout.fillWidth: true
theme: page.theme
title: qsTr("Installed Version")
- value: nvidiaDetector.driverVersion.length > 0 ? nvidiaDetector.driverVersion : qsTr("None")
+ value: page.installedVersionLabel.length > 0 ? page.installedVersionLabel : qsTr("None")
subtitle: page.driverInstalledLocally
? (nvidiaUpdater.updateAvailable
? qsTr("Latest available online: ") + page.latestVersionLabel
@@ -462,7 +465,7 @@ Item {
spacing: 8
InfoBadge {
- text: qsTr("Installed: ") + (nvidiaUpdater.currentVersion.length > 0 ? nvidiaUpdater.currentVersion : qsTr("None"))
+ text: qsTr("Installed: ") + (page.installedVersionLabel.length > 0 ? page.installedVersionLabel : qsTr("None"))
backgroundColor: page.theme.cardStrong
foregroundColor: page.theme.text
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index dfcfb08..7de7d5c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Qt6 REQUIRED COMPONENTS Test)
+find_package(Qt6 REQUIRED COMPONENTS Test Qml Quick QuickControls2)
function(ro_control_configure_test target_name)
target_include_directories(${target_name} PRIVATE
@@ -93,3 +93,23 @@ target_link_libraries(test_cli PRIVATE
)
add_test(NAME test_cli COMMAND test_cli)
+
+# ─── Driver Page QML Integration Tests ──────────────────────────────────────
+qt_add_executable(test_driver_page
+ test_driver_page.cpp
+)
+
+ro_control_configure_test(test_driver_page)
+
+target_compile_definitions(test_driver_page PRIVATE
+ "RO_CONTROL_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\""
+)
+
+target_link_libraries(test_driver_page PRIVATE
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::QuickControls2
+ Qt6::Test
+)
+
+add_test(NAME test_driver_page COMMAND test_driver_page)
diff --git a/tests/test_driver_page.cpp b/tests/test_driver_page.cpp
new file mode 100644
index 0000000..ef84bd5
--- /dev/null
+++ b/tests/test_driver_page.cpp
@@ -0,0 +1,446 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class DetectorMock : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(bool gpuFound READ gpuFound WRITE setGpuFound NOTIFY infoChanged)
+ Q_PROPERTY(QString gpuName READ gpuName WRITE setGpuName NOTIFY infoChanged)
+ Q_PROPERTY(QString driverVersion READ driverVersion WRITE setDriverVersion
+ NOTIFY infoChanged)
+ Q_PROPERTY(bool driverLoaded READ driverLoaded WRITE setDriverLoaded NOTIFY
+ infoChanged)
+ Q_PROPERTY(bool nouveauActive READ nouveauActive WRITE setNouveauActive
+ NOTIFY infoChanged)
+ Q_PROPERTY(bool secureBootEnabled READ secureBootEnabled WRITE
+ setSecureBootEnabled NOTIFY infoChanged)
+ Q_PROPERTY(bool waylandSession READ waylandSession WRITE setWaylandSession
+ NOTIFY infoChanged)
+ Q_PROPERTY(QString sessionType READ sessionType WRITE setSessionType NOTIFY
+ infoChanged)
+ Q_PROPERTY(QString activeDriver READ activeDriver WRITE setActiveDriver NOTIFY
+ infoChanged)
+ Q_PROPERTY(QString verificationReport READ verificationReport WRITE
+ setVerificationReport NOTIFY infoChanged)
+
+public:
+ bool gpuFound() const { return m_gpuFound; }
+ QString gpuName() const { return m_gpuName; }
+ QString driverVersion() const { return m_driverVersion; }
+ bool driverLoaded() const { return m_driverLoaded; }
+ bool nouveauActive() const { return m_nouveauActive; }
+ bool secureBootEnabled() const { return m_secureBootEnabled; }
+ bool waylandSession() const { return m_waylandSession; }
+ QString sessionType() const { return m_sessionType; }
+ QString activeDriver() const { return m_activeDriver; }
+ QString verificationReport() const { return m_verificationReport; }
+
+ void setGpuFound(bool value) {
+ if (m_gpuFound == value) {
+ return;
+ }
+ m_gpuFound = value;
+ emit infoChanged();
+ }
+
+ void setGpuName(const QString &value) {
+ if (m_gpuName == value) {
+ return;
+ }
+ m_gpuName = value;
+ emit infoChanged();
+ }
+
+ void setDriverVersion(const QString &value) {
+ if (m_driverVersion == value) {
+ return;
+ }
+ m_driverVersion = value;
+ emit infoChanged();
+ }
+
+ void setDriverLoaded(bool value) {
+ if (m_driverLoaded == value) {
+ return;
+ }
+ m_driverLoaded = value;
+ emit infoChanged();
+ }
+
+ void setNouveauActive(bool value) {
+ if (m_nouveauActive == value) {
+ return;
+ }
+ m_nouveauActive = value;
+ emit infoChanged();
+ }
+
+ void setSecureBootEnabled(bool value) {
+ if (m_secureBootEnabled == value) {
+ return;
+ }
+ m_secureBootEnabled = value;
+ emit infoChanged();
+ }
+
+ void setWaylandSession(bool value) {
+ if (m_waylandSession == value) {
+ return;
+ }
+ m_waylandSession = value;
+ emit infoChanged();
+ }
+
+ void setSessionType(const QString &value) {
+ if (m_sessionType == value) {
+ return;
+ }
+ m_sessionType = value;
+ emit infoChanged();
+ }
+
+ void setActiveDriver(const QString &value) {
+ if (m_activeDriver == value) {
+ return;
+ }
+ m_activeDriver = value;
+ emit infoChanged();
+ }
+
+ void setVerificationReport(const QString &value) {
+ if (m_verificationReport == value) {
+ return;
+ }
+ m_verificationReport = value;
+ emit infoChanged();
+ }
+
+ Q_INVOKABLE void refresh() { emit infoChanged(); }
+
+signals:
+ void infoChanged();
+
+private:
+ bool m_gpuFound = false;
+ QString m_gpuName;
+ QString m_driverVersion;
+ bool m_driverLoaded = false;
+ bool m_nouveauActive = false;
+ bool m_secureBootEnabled = false;
+ bool m_waylandSession = false;
+ QString m_sessionType = QStringLiteral("unknown");
+ QString m_activeDriver = QStringLiteral("Not Installed / Unknown");
+ QString m_verificationReport = QStringLiteral("GPU: None");
+};
+
+class InstallerMock : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(bool proprietaryAgreementRequired READ proprietaryAgreementRequired
+ WRITE setProprietaryAgreementRequired NOTIFY
+ proprietaryAgreementChanged)
+ Q_PROPERTY(QString proprietaryAgreementText READ proprietaryAgreementText
+ WRITE setProprietaryAgreementText NOTIFY
+ proprietaryAgreementChanged)
+ Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged)
+
+public:
+ bool proprietaryAgreementRequired() const {
+ return m_proprietaryAgreementRequired;
+ }
+ QString proprietaryAgreementText() const { return m_proprietaryAgreementText; }
+ bool busy() const { return m_busy; }
+
+ void setProprietaryAgreementRequired(bool value) {
+ if (m_proprietaryAgreementRequired == value) {
+ return;
+ }
+ m_proprietaryAgreementRequired = value;
+ emit proprietaryAgreementChanged();
+ }
+
+ void setProprietaryAgreementText(const QString &value) {
+ if (m_proprietaryAgreementText == value) {
+ return;
+ }
+ m_proprietaryAgreementText = value;
+ emit proprietaryAgreementChanged();
+ }
+
+ void setBusy(bool value) {
+ if (m_busy == value) {
+ return;
+ }
+ m_busy = value;
+ emit busyChanged();
+ }
+
+ Q_INVOKABLE void refreshProprietaryAgreement() {}
+ Q_INVOKABLE void installProprietary(bool) {}
+ Q_INVOKABLE void installOpenSource() {}
+ Q_INVOKABLE void remove() {}
+ Q_INVOKABLE void deepClean() {}
+
+signals:
+ void progressMessage(const QString &message);
+ void proprietaryAgreementChanged();
+ void busyChanged();
+ void installFinished(bool success, const QString &message);
+ void removeFinished(bool success, const QString &message);
+
+private:
+ bool m_proprietaryAgreementRequired = false;
+ QString m_proprietaryAgreementText;
+ bool m_busy = false;
+};
+
+class UpdaterMock : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(bool updateAvailable READ updateAvailable WRITE setUpdateAvailable
+ NOTIFY updateAvailableChanged)
+ Q_PROPERTY(QString currentVersion READ currentVersion WRITE setCurrentVersion
+ NOTIFY currentVersionChanged)
+ Q_PROPERTY(QString latestVersion READ latestVersion WRITE setLatestVersion
+ NOTIFY latestVersionChanged)
+ Q_PROPERTY(QStringList availableVersions READ availableVersions WRITE
+ setAvailableVersions NOTIFY availableVersionsChanged)
+ Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged)
+
+public:
+ bool updateAvailable() const { return m_updateAvailable; }
+ QString currentVersion() const { return m_currentVersion; }
+ QString latestVersion() const { return m_latestVersion; }
+ QStringList availableVersions() const { return m_availableVersions; }
+ bool busy() const { return m_busy; }
+
+ void setUpdateAvailable(bool value) {
+ if (m_updateAvailable == value) {
+ return;
+ }
+ m_updateAvailable = value;
+ emit updateAvailableChanged();
+ }
+
+ void setCurrentVersion(const QString &value) {
+ if (m_currentVersion == value) {
+ return;
+ }
+ m_currentVersion = value;
+ emit currentVersionChanged();
+ }
+
+ void setLatestVersion(const QString &value) {
+ if (m_latestVersion == value) {
+ return;
+ }
+ m_latestVersion = value;
+ emit latestVersionChanged();
+ }
+
+ void setAvailableVersions(const QStringList &value) {
+ if (m_availableVersions == value) {
+ return;
+ }
+ m_availableVersions = value;
+ emit availableVersionsChanged();
+ }
+
+ void setBusy(bool value) {
+ if (m_busy == value) {
+ return;
+ }
+ m_busy = value;
+ emit busyChanged();
+ }
+
+ Q_INVOKABLE void checkForUpdate() {}
+ Q_INVOKABLE void applyUpdate() {}
+ Q_INVOKABLE void applyVersion(const QString &) {}
+ Q_INVOKABLE void refreshAvailableVersions() {}
+
+signals:
+ void updateAvailableChanged();
+ void currentVersionChanged();
+ void latestVersionChanged();
+ void availableVersionsChanged();
+ void busyChanged();
+ void progressMessage(const QString &message);
+ void updateFinished(bool success, const QString &message);
+
+private:
+ bool m_updateAvailable = false;
+ QString m_currentVersion;
+ QString m_latestVersion;
+ QStringList m_availableVersions;
+ bool m_busy = false;
+};
+
+class TestDriverPage : public QObject {
+ Q_OBJECT
+
+private slots:
+ void initTestCase() {
+ qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("offscreen"));
+ }
+
+ void testDriverInstalledLocallyUsesDetectorVersion();
+ void testOperationRunningStillTracksBackendBusyAfterManualStateChanges();
+
+private:
+ QObject *createPage(DetectorMock *detector, InstallerMock *installer,
+ UpdaterMock *updater, QQmlEngine *engine) const;
+};
+
+QObject *TestDriverPage::createPage(DetectorMock *detector,
+ InstallerMock *installer,
+ UpdaterMock *updater,
+ QQmlEngine *engine) const {
+ engine->rootContext()->setContextProperty("nvidiaDetector", detector);
+ engine->rootContext()->setContextProperty("nvidiaInstaller", installer);
+ engine->rootContext()->setContextProperty("nvidiaUpdater", updater);
+
+ const QString sourceRoot = QStringLiteral(RO_CONTROL_SOURCE_DIR);
+ const QString sourcePagePath =
+ QDir(sourceRoot).filePath(QStringLiteral("src/qml/pages/DriverPage.qml"));
+ QFile sourceFile(sourcePagePath);
+ if (!sourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qFatal("Failed to open DriverPage.qml from source tree");
+ }
+
+ QString pageSource = QString::fromUtf8(sourceFile.readAll());
+ const QString importLine = QStringLiteral("import QtQuick.Layouts\n");
+ const QString injectedImport =
+ QStringLiteral("import QtQuick.Layouts\nimport \"../components\"\n");
+ if (!pageSource.contains(importLine)) {
+ qFatal("DriverPage.qml did not contain the expected import anchor");
+ }
+ pageSource.replace(importLine, injectedImport);
+
+ QTemporaryDir tempDir;
+ if (!tempDir.isValid()) {
+ qFatal("Failed to create temporary directory for QML test");
+ }
+
+ const QString qmlRoot = tempDir.path() + QStringLiteral("/qml");
+ const QString pagesDir = qmlRoot + QStringLiteral("/pages");
+ const QString componentsDir = qmlRoot + QStringLiteral("/components");
+ if (!QDir().mkpath(pagesDir) || !QDir().mkpath(componentsDir)) {
+ qFatal("Failed to create temporary QML fixture directories");
+ }
+
+ const QString tempPagePath = pagesDir + QStringLiteral("/DriverPage.qml");
+ QFile tempPageFile(tempPagePath);
+ if (!tempPageFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qFatal("Failed to create temporary DriverPage.qml");
+ }
+ tempPageFile.write(pageSource.toUtf8());
+ tempPageFile.close();
+
+ const QStringList componentFiles = {QStringLiteral("InfoBadge.qml"),
+ QStringLiteral("SectionPanel.qml"),
+ QStringLiteral("StatusBanner.qml"),
+ QStringLiteral("StatCard.qml")};
+ for (const QString &fileName : componentFiles) {
+ const QString sourceComponentPath =
+ QDir(sourceRoot).filePath(QStringLiteral("src/qml/components/") +
+ fileName);
+ const QString targetComponentPath = componentsDir + QLatin1Char('/') + fileName;
+ if (!QFile::copy(sourceComponentPath, targetComponentPath)) {
+ qFatal("Failed to copy component fixture for DriverPage test");
+ }
+ }
+
+ QQmlComponent component(engine, QUrl::fromLocalFile(tempPagePath));
+
+ if (!component.isReady()) {
+ qFatal("%s", qPrintable(component.errorString()));
+ }
+
+ QVariantMap theme;
+ theme.insert(QStringLiteral("accentA"), QStringLiteral("#1f6feb"));
+ theme.insert(QStringLiteral("accentB"), QStringLiteral("#2da44e"));
+ theme.insert(QStringLiteral("accentC"), QStringLiteral("#bf8700"));
+ theme.insert(QStringLiteral("warning"), QStringLiteral("#9a6700"));
+ theme.insert(QStringLiteral("success"), QStringLiteral("#1a7f37"));
+ theme.insert(QStringLiteral("danger"), QStringLiteral("#cf222e"));
+ theme.insert(QStringLiteral("warningBg"), QStringLiteral("#fff8c5"));
+ theme.insert(QStringLiteral("successBg"), QStringLiteral("#dafbe1"));
+ theme.insert(QStringLiteral("dangerBg"), QStringLiteral("#ffebe9"));
+ theme.insert(QStringLiteral("infoBg"), QStringLiteral("#ddf4ff"));
+ theme.insert(QStringLiteral("card"), QStringLiteral("#ffffff"));
+ theme.insert(QStringLiteral("cardStrong"), QStringLiteral("#f6f8fa"));
+ theme.insert(QStringLiteral("border"), QStringLiteral("#d0d7de"));
+ theme.insert(QStringLiteral("text"), QStringLiteral("#1f2328"));
+ theme.insert(QStringLiteral("textMuted"), QStringLiteral("#656d76"));
+ theme.insert(QStringLiteral("textSoft"), QStringLiteral("#57606a"));
+
+ QVariantMap initialProperties;
+ initialProperties.insert(QStringLiteral("width"), 1280);
+ initialProperties.insert(QStringLiteral("height"), 900);
+ initialProperties.insert(QStringLiteral("theme"), theme);
+
+ QObject *object = component.createWithInitialProperties(initialProperties);
+ if (object == nullptr) {
+ qFatal("Failed to create DriverPage test component");
+ }
+
+ return object;
+}
+
+void TestDriverPage::testDriverInstalledLocallyUsesDetectorVersion() {
+ QQmlEngine engine;
+ DetectorMock detector;
+ InstallerMock installer;
+ UpdaterMock updater;
+
+ detector.setDriverVersion(QStringLiteral("580.126.18"));
+ updater.setCurrentVersion(QString());
+
+ QScopedPointer page(
+ createPage(&detector, &installer, &updater, &engine));
+
+ QTRY_VERIFY(page->property("driverInstalledLocally").toBool());
+ QCOMPARE(page->property("installedVersionLabel").toString(),
+ QStringLiteral("580.126.18"));
+}
+
+void TestDriverPage::
+ testOperationRunningStillTracksBackendBusyAfterManualStateChanges() {
+ QQmlEngine engine;
+ DetectorMock detector;
+ InstallerMock installer;
+ UpdaterMock updater;
+
+ QScopedPointer page(
+ createPage(&detector, &installer, &updater, &engine));
+
+ QVERIFY(!page->property("operationRunning").toBool());
+
+ QVERIFY(QMetaObject::invokeMethod(page.get(), "setOperationState",
+ Q_ARG(QVariant, QVariant(QStringLiteral("Updater"))),
+ Q_ARG(QVariant, QVariant(QStringLiteral("Checking"))),
+ Q_ARG(QVariant, QVariant(QStringLiteral("info"))),
+ Q_ARG(QVariant, QVariant(true))));
+ QTRY_VERIFY(page->property("operationRunning").toBool());
+
+ QVERIFY(QMetaObject::invokeMethod(page.get(), "finishOperation",
+ Q_ARG(QVariant, QVariant(QStringLiteral("Updater"))),
+ Q_ARG(QVariant, QVariant(true)),
+ Q_ARG(QVariant, QVariant(QStringLiteral("Done")))));
+ QTRY_VERIFY(!page->property("operationRunning").toBool());
+
+ updater.setBusy(true);
+ QTRY_VERIFY(page->property("operationRunning").toBool());
+
+ updater.setBusy(false);
+ QTRY_VERIFY(!page->property("operationRunning").toBool());
+}
+
+QTEST_MAIN(TestDriverPage)
+#include "test_driver_page.moc"
From 781a384f265362fc108b36d2e27402f28bc55b5f Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 21:06:44 +0300
Subject: [PATCH 20/22] feat: Implement CPU and RAM monitoring, and display
installed and latest driver versions.
---
CMakeLists.txt | 3 +-
i18n/README.md | 3 +-
i18n/ro-control_tr.ts | 785 ++++++++++++++++-------------
src/backend/monitor/cpumonitor.cpp | 56 +-
src/backend/monitor/rammonitor.cpp | 214 +++++---
src/qml/pages/MonitorPage.qml | 42 +-
tests/test_monitor.cpp | 28 +
7 files changed, 703 insertions(+), 428 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e4f6a45..4f4d2d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -138,8 +138,9 @@ qt_add_qml_module(ro-control
)
if(Qt6LinguistTools_FOUND)
+ # English strings ship directly from source code. Keep the English TS file
+ # for catalog maintenance, but only compile non-source locales into .qm.
set(TS_FILES
- i18n/ro-control_en.ts
i18n/ro-control_tr.ts
)
diff --git a/i18n/README.md b/i18n/README.md
index 85068c2..28df98a 100644
--- a/i18n/README.md
+++ b/i18n/README.md
@@ -15,6 +15,7 @@ added as new `ro-control_.ts` files.
- QML strings use `qsTr(...)`
- C++ strings use `tr(...)`
- `main.cpp` loads the best matching `.qm` file from the embedded `/i18n` resource
+- English uses the source strings directly and does not require an embedded `.qm`
- If no locale-specific translation is found, the app falls back to English
## Updating translations
@@ -23,7 +24,7 @@ added as new `ro-control_.ts` files.
2. Reconfigure the build directory with CMake.
3. Refresh translation sources with `lupdate`.
4. Translate in Qt Linguist.
-5. Build again so CMake generates updated `.qm` files.
+5. Build again so CMake generates updated `.qm` files for shipped locales.
Example workflow:
diff --git a/i18n/ro-control_tr.ts b/i18n/ro-control_tr.ts
index 539483e..15e9c42 100644
--- a/i18n/ro-control_tr.ts
+++ b/i18n/ro-control_tr.ts
@@ -24,10 +24,10 @@
Sürücü sürümü:
-
-
-
-
+
+
+
+ NoneYok
@@ -36,86 +36,98 @@
Secure Boot:
-
-
+
+ Repository Setup
-
+ Depo Kurulumu
-
-
+
+ Package Transaction
-
+ Paket İşlemi
-
-
+
+ Kernel Integration
-
+ Kernel Entegrasyonu
-
-
+
+ Session Finalization
-
+ Oturum Sonlandırma
-
-
+
+ Update Check
-
+ Güncelleme Kontrolü
-
-
+
+ General
-
+ Genel
+
+
+
+
+ Installed
+ Kurulu
+
+
+
+
+ Latest
+ En Güncel
-
-
+
+ GPU DetectionGPU Tespiti
-
-
+
+ DetectedTespit Edildi
-
-
+
+ MissingBulunamadı
-
-
+
+ No NVIDIA GPU was detected on this system.Bu sistemde NVIDIA GPU tespit edilemedi.
-
-
+
+ Active DriverAktif Sürücü
-
-
+
+ Session: Oturum:
-
-
+
+ UnknownBilinmiyor
-
-
+
+ Installed VersionKurulu Sürüm
@@ -128,324 +140,346 @@
Bekleyen paket güncellemesi bulunamadı.
-
-
+
+ Latest available online:
-
+ Çevrimiçi bulunan en güncel sürüm:
-
-
+
+ No pending online package update detected.
-
+ Bekleyen çevrimiçi paket güncellemesi tespit edilmedi.
-
-
+
+ Latest driver found online:
-
+ Çevrimiçi bulunan en güncel sürücü:
-
-
+
+ No online driver catalog has been loaded yet.
-
+ Henüz çevrimiçi sürücü kataloğu yüklenmedi.
-
-
+
+ Secure BootGüvenli Önyükleme (Secure Boot)
-
-
+
+ EnabledAçık
-
-
+
+ Disabled / UnknownKapalı / Bilinmiyor
-
-
+
+ Unsigned kernel modules may require manual signing.İmzasız kernel modüllerinin manuel olarak imzalanması gerekebilir.
-
-
+
+ No Secure Boot blocker is currently detected.Şu anda herhangi bir Secure Boot engeli tespit edilmedi.
-
-
+
+ VerificationDoğrulama
-
-
+
+ Review driver prerequisites before changing packages.Paketleri değiştirmeden önce sürücü ön koşullarını gözden geçirin.
-
-
+
+ GPU ReadyGPU Hazır
-
-
+
+ GPU MissingGPU Bulunamadı
-
-
+
+ Wayland SessionWayland Oturumu
-
-
+
+ X11 / Other SessionX11 / Diğer Oturum
-
-
+
+ Nouveau ActiveNouveau Aktif
-
-
+
+ Nouveau InactiveNouveau Pasif
-
-
+
+ Kernel Module LoadedKernel Modülü Yüklü
-
-
+
+ Kernel Module MissingKernel Modülü Eksik
-
-
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.Wayland oturumları otomatik olarak nvidia-drm.modeset=1 çekirdek argümanına ihtiyaç duyar.
-
-
+
+ X11 sessions require matching userspace packages after install or update.X11 oturumları, kurulum veya güncelleme sonrası eşleşen kullanıcı alanı paketleri gerektirir.
-
-
+
+ Driver ActionsSürücü İşlemleri
-
-
+
+ Use guided actions to install, switch or remove the current stack.Mevcut yığını kurmak, değiştirmek veya kaldırmak için rehberli işlemleri kullanın.
-
-
+
+ Source:
-
+ Kaynak:
-
-
-
-
-
-
+
+
+
+
+
+ Idle
-
+ Boşta
-
-
+
+ Phase:
-
+ Aşama:
-
-
+
+ Running
-
+ Çalışıyor
-
-
+
+
+ Elapsed:
+ Geçen Süre:
+
+
+
+
+ Last Log:
+ Son Günlük:
+
+
+
+ I accept the detected license / agreement termsTespit edilen lisans / sözleşme koşullarını kabul ediyorum
-
-
+
+ Install ProprietarySahipli Sürücüyü Kur
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Installer
-
+ Kurucu
-
-
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
- Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
+ Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
-
+
+ Install NouveauNouveau Kur (Açık Kaynak)
-
-
+
+ Switching to the open-source driver...
- Açık kaynak sürücüye geçiliyor...
+ Açık kaynak sürücüye geçiliyor...
-
-
+
+ Remove DriverSürücüyü Kaldır
-
-
+
+ Removing the NVIDIA driver...
- NVIDIA sürücüsü kaldırılıyor...
+ NVIDIA sürücüsü kaldırılıyor...
-
-
+
+ Cleaning legacy driver leftovers...
- Eski sürücü kalıntıları temizleniyor...
+ Eski sürücü kalıntıları temizleniyor...
-
-
+
+ Search the online package catalog, then download and install a matching driver build.
-
+ Çevrimiçi paket kataloğunu tarayın, ardından uygun sürücü derlemesini indirip kurun.
-
-
+
+ Remote Driver Available
-
+ Uzak Sürücü Mevcut
-
-
+
+ Catalog Not Ready
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Katalog Hazır Değil
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Updater
-
+ Güncelleyici
-
-
+
+ Searching the online NVIDIA package catalog...
-
+ Çevrimiçi NVIDIA paket kataloğu aranıyor...
-
-
+
+ Install Latest
-
+ En Günceli Kur
-
-
+
+ Updating NVIDIA driver to the latest online version...
-
+ NVIDIA sürücüsü çevrimiçi en güncel sürüme güncelleniyor...
-
-
+
+ Downloading and installing the latest online NVIDIA driver...
-
+ En güncel çevrimiçi NVIDIA sürücüsü indiriliyor ve kuruluyor...
-
-
+
+ Switching NVIDIA driver to selected online version:
-
+ NVIDIA sürücüsü seçilen çevrimiçi sürüme geçiriliyor:
-
-
+
+ Downloading and installing selected NVIDIA driver version:
-
+ Seçilen NVIDIA sürücü sürümü indiriliyor ve kuruluyor:
-
-
+
+ Online repository versions loaded:
-
+ Çevrimiçi depo sürümleri yüklendi:
-
-
+
+ No online repository version list has been loaded yet.
-
+ Henüz çevrimiçi depo sürüm listesi yüklenmedi.Starting update check...Güncelleme denetimi başlatılıyor...
-
-
+
+ Apply LatestEn Son Sürümü Uygula
@@ -454,8 +488,8 @@
NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
-
+
+ Apply SelectedSeçileni Uygula
@@ -468,20 +502,20 @@
Henüz hiçbir repo sürüm listesi yüklenmedi.
-
-
+
+ Activity LogAktivite Günlüğü
-
-
+
+ Operation output is streamed here in real time.İşlem çıktıları burada gerçek zamanlı olarak gösterilir.
-
-
+
+ Clear LogGünlüğü Temizle
@@ -510,20 +544,20 @@
Açık Kaynak Sürücüyü Kur (Nouveau)
-
-
+
+ Deep CleanDerin Temizlik
-
-
+
+ Rescan SystemSistemi Yeniden Tara
-
-
+
+ Update CenterGüncelleme Merkezi
@@ -532,26 +566,26 @@
Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
-
-
+
+ Installed: Kurulu:
-
-
+
+ Update AvailableGüncelleme Mevcut
-
-
+
+ Up to DateGüncel
-
-
+
+ Check for UpdatesGüncellemeleri Kontrol Et
@@ -668,216 +702,224 @@
Sistem İzleme
-
-
+
+ CPU LoadCPU Yükü
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ UnavailableKullanılamıyor
-
-
+
+ CPU telemetry is currently unavailable.CPU telemetrisi şu anda kullanılamıyor.
-
-
+
+ GPU LoadGPU Yükü
-
-
+
+ nvidia-smi did not return live GPU telemetry.nvidia-smi canlı GPU telemetrisi döndürmedi.
-
-
+
+ Memory UsageBellek Kullanımı
-
-
+
+ Used: Kullanılan:
-
-
+
+ RAM telemetry is currently unavailable.RAM telemetrisi şu anda kullanılamıyor.
-
-
+
+ Live Resource CurvesCanlı Kaynak Grafikleri
-
-
+
+ Quick pulse view for the most important machine resources.En önemli makine kaynakları için hızlı anlık görünüm.
-
-
+
+ CPUCPU
-
-
+
+ GPUEkran Kartı (GPU)
-
-
+
+ Health SummarySağlık Özeti
-
-
+
+ Fast interpretation of the raw telemetry values.Ham telemetri değerlerinin hızlı yorumlaması.
-
-
+
+ CPU BusyCPU Meşgul
-
-
+
+ CPU StableCPU Stabil
-
-
+
+ GPU OnlineGPU Çevrimiçi
-
-
+
+ GPU Telemetry MissingGPU Telemetrisi Bulunamadı
-
-
+
+ Memory PressureBellek Baskısı
-
-
+
+ Memory StableBellek Stabil
-
-
+
+ GPU temperature: GPU sıcaklığı:
-
-
+
+
+ , VRAM
+ , VRAM
+
+
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.GPU metrikleri kullanılamıyor. Sürücü kurulumunu ve nvidia-smi erişilebilirliğini kontrol edin.
-
-
+
+ Detailed SignalsDetaylı Sinyaller
-
-
+
+ Expanded raw values for support and diagnostics.Destek ve tanılama için genişletilmiş ham değerler.
-
-
+
+ CPU TemperatureCPU Sıcaklığı
-
-
-
-
-
-
-
-
+
+
+
+
+
+ UnknownBilinmiyor
-
-
+
+ GPU TemperatureGPU Sıcaklığı
-
-
+
+ VRAMVRAM Belleği
-
-
+
+ RAM FootprintRAM Kapladığı Alan
-
-
+
+ Actionsİşlemler
-
-
+
+ Trigger a manual refresh when you need a fresh sample.Taze bir örneğe ihtiyacınız olduğunda manuel yenilemeyi tetikleyin.
-
-
+
+ Refresh TelemetryTelemetriyi Yenile
-
-
+
+ NVIDIA Path OKNVIDIA Yolu Tamam
-
-
+
+ Check NVIDIA PathNVIDIA Yolunu Kontrol Et
@@ -890,8 +932,8 @@
CPU verisi alınamıyor
-
-
+
+ Temperature: Sıcaklık:
@@ -900,8 +942,8 @@
GPU (NVIDIA)
-
-
+
+ NVIDIA GPUNVIDIA GPU
@@ -918,8 +960,8 @@
VRAM:
-
-
+
+ RAMRAM
@@ -932,8 +974,8 @@
Yenile
-
-
+
+ Refresh interval: Yenileme aralığı:
@@ -1020,102 +1062,117 @@ Nouveau: %6
NvidiaInstaller
-
+
+ Starting command (attempt %1): %2
+ Komut başlatılıyor (deneme %1): %2
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+ Komut tamamlandı (deneme %1, çıkış %2, %3 ms): %4
+
+
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
-
+ License agreement acceptance is required before installation.Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
-
+ Checking RPM Fusion repositories...RPM Fusion depoları kontrol ediliyor...
-
+ Platform version could not be detected.Platform sürümü tespit edilemedi.
-
+ Failed to enable RPM Fusion repositories: RPM Fusion depoları etkinleştirilemedi:
-
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
+ Installation failed: Kurulum başarısız:
-
+ Building the kernel module (akmods --force)...Kernel modülü derleniyor (akmods --force)...
-
+
+ Kernel module build failed:
+ Kernel modülü inşası başarısız:
+
+
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.Kapalı kaynak NVIDIA sürücüsü başarıyla kuruldu. Lütfen sistemi yeniden başlatın.
-
+ Switching to the open-source driver...Açık kaynak sürücüye geçiliyor...
-
+ Failed to remove proprietary packages: Kapalı kaynak paketler kaldırılamadı:
-
+ Open-source driver installation failed: Açık kaynak sürücü kurulumu başarısız:
-
+ The open-source driver (Nouveau) was installed. Please restart the system.Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
-
+ Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
-
+ Driver removed successfully.Sürücü başarıyla kaldırıldı.
-
+ Removal failed: Kaldırma başarısız:
-
+ Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
-
+ Deep clean failed: Derin temizlik başarısız:
-
+ DNF cache cleanup failed: DNF önbellek temizliği başarısız:
-
+ Deep clean completed.Derin temizlik tamamlandı.
@@ -1124,32 +1181,32 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 uygulanıyor...
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ UnknownBilinmiyor
-
+ Legacy NVIDIA cleanup completed.Eski NVIDIA temizliği tamamlandı.
-
+ Failed to apply the Wayland kernel parameter: Wayland kernel parametresi uygulanamadı:
-
+ X11 detected: checking NVIDIA userspace packages...X11 tespit edildi: NVIDIA userspace paketleri kontrol ediliyor...
-
+ Failed to install the X11 NVIDIA package: X11 NVIDIA paketi kurulamadı:
@@ -1161,7 +1218,7 @@ Nouveau: %6
NVIDIA sürücüsü güncelleniyor...
-
+ Update failed: Güncelleme başarısız:
@@ -1174,13 +1231,13 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
-
+ Driver updated successfully. Please restart the system.Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
-
-
+
+ dnf not found.dnf bulunamadı.
@@ -1189,109 +1246,129 @@ Nouveau: %6
Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
-
+ Online NVIDIA packages were found. You can download and install the driver now.
-
+ Çevrimiçi NVIDIA paketleri bulundu. Sürücüyü şimdi indirip kurabilirsiniz.
-
+ Online NVIDIA driver found. Latest remote version: %1
-
+ Çevrimiçi NVIDIA sürücüsü bulundu. En güncel uzak sürüm: %1
-
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.
-
+ Çevrimiçi NVIDIA paket kataloğu bulunamadı. RPM Fusion henüz yapılandırılmamış olabilir.
-
+ Update found (version details unavailable).Güncelleme bulundu (sürüm detayları kullanılamıyor).
-
+ Update found: %1Güncelleme bulundu: %1
-
+ Driver is up to date. No new version found.Sürücü güncel. Yeni sürüm bulunamadı.
-
+ Update check failed: %1Güncelleme kontrolü başarısız: %1
-
+
+ Starting command (attempt %1): %2
+ Komut başlatılıyor (deneme %1): %2
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+ Komut tamamlandı (deneme %1, çıkış %2, %3 ms): %4
+
+
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ Kernel module build failed: Kernel modülü inşası başarısız:
-
-
-
+
+
+ unknown errorbilinmeyen hata
-
+ Wayland detected: applying nvidia-drm.modeset=1...Wayland algılandı: nvidia-drm.modeset=1 uygulanıyor...
-
+ Failed to update the Wayland kernel parameter: Wayland çekirdek parametresi güncellenemedi:
-
+ No available versions found.Hiçbir uygun sürüm bulunamadı.
-
+ Available versions: %1Mevcut sürümler: %1
-
+ Starting update check...Güncelleme denetimi başlatılıyor...
-
+ Selected version not found in the repository.Seçili sürüm depoda bulunamadı.
-
+ Updating NVIDIA driver to the latest version...NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
+ Switching NVIDIA driver to selected version: %1NVIDIA sürücüsü seçili sürüme değiştiriliyor: %1
-
+
+ Driver is already at the latest available version.
+ Sürücü zaten mevcut en güncel sürümde.
+
+
+
+ Selected driver version is already installed.
+ Seçilen sürücü sürümü zaten kurulu.
+
+
+ Rebuilding kernel module...Kernel modülü yeniden derleniyor...
-
+ Latest version installed successfully. Please restart the system.En son sürüm başarıyla yüklendi. Lütfen sistemi yeniden başlatın.
-
+ Selected version applied successfully. Please restart the system.Seçili sürüm başarıyla uygulandı. Lütfen sistemi yeniden başlatın.
@@ -1318,13 +1395,13 @@ Nouveau: %6
Language
-
+ DilChanges the application language immediately and keeps the selection for the next launch.
-
+ Uygulama dilini hemen değiştirir ve seçimi sonraki açılış için korur.
@@ -1354,7 +1431,7 @@ Nouveau: %6
Language:
-
+ Dil:
diff --git a/src/backend/monitor/cpumonitor.cpp b/src/backend/monitor/cpumonitor.cpp
index 18da29f..1f519fc 100644
--- a/src/backend/monitor/cpumonitor.cpp
+++ b/src/backend/monitor/cpumonitor.cpp
@@ -1,9 +1,11 @@
// CPU istatistikleri
#include "cpumonitor.h"
+#include "system/commandrunner.h"
#include
#include
+#include
#include
#include
@@ -19,6 +21,11 @@ QString readFileText(const QString &path) {
return QString::fromUtf8(file.readAll()).trimmed();
}
+QString pathOverrideOrDefault(const char *envVarName, const QString &fallback) {
+ const QString overridePath = qEnvironmentVariable(envVarName).trimmed();
+ return overridePath.isEmpty() ? fallback : overridePath;
+}
+
int parseMilliCelsius(const QString &value) {
bool ok = false;
const int milliC = value.trimmed().toInt(&ok);
@@ -49,7 +56,8 @@ int readFirstValidTemperature(const QStringList &paths) {
}
int readCpuTemperatureFromThermalZones() {
- QDir thermalDir(QStringLiteral("/sys/class/thermal"));
+ QDir thermalDir(pathOverrideOrDefault("RO_CONTROL_THERMAL_ROOT",
+ QStringLiteral("/sys/class/thermal")));
const QFileInfoList entries = thermalDir.entryInfoList(
{QStringLiteral("thermal_zone*")}, QDir::Dirs | QDir::NoDotAndDotDot,
QDir::Name);
@@ -78,7 +86,8 @@ int readCpuTemperatureFromThermalZones() {
}
int readCpuTemperatureFromHwmon() {
- QDir hwmonDir(QStringLiteral("/sys/class/hwmon"));
+ QDir hwmonDir(pathOverrideOrDefault("RO_CONTROL_HWMON_ROOT",
+ QStringLiteral("/sys/class/hwmon")));
const QFileInfoList entries = hwmonDir.entryInfoList(
{QStringLiteral("hwmon*")}, QDir::Dirs | QDir::NoDotAndDotDot,
QDir::Name);
@@ -122,7 +131,48 @@ int readCpuTemperatureC() {
return thermalZoneTemperature;
}
- return readCpuTemperatureFromHwmon();
+ const int hwmonTemperature = readCpuTemperatureFromHwmon();
+ if (hwmonTemperature > 0) {
+ return hwmonTemperature;
+ }
+
+ CommandRunner runner;
+ const auto result = runner.run(QStringLiteral("sensors"));
+ if (!result.success()) {
+ return 0;
+ }
+
+ static const QRegularExpression preferredLinePattern(
+ QStringLiteral(
+ R"((package|tctl|tdie|cpu|core)[^:\n]*:\s*[+-]?([0-9]+(?:\.[0-9]+)?))"),
+ QRegularExpression::CaseInsensitiveOption);
+ static const QRegularExpression genericLinePattern(
+ QStringLiteral(R"(:\s*[+-]?([0-9]+(?:\.[0-9]+)?))"));
+
+ const QStringList lines = result.stdout.split(QLatin1Char('\n'));
+ for (const QString &line : lines) {
+ const auto preferredMatch = preferredLinePattern.match(line);
+ if (preferredMatch.hasMatch()) {
+ bool ok = false;
+ const double value = preferredMatch.captured(2).toDouble(&ok);
+ if (ok && value > 0.0) {
+ return static_cast(value);
+ }
+ }
+ }
+
+ for (const QString &line : lines) {
+ const auto genericMatch = genericLinePattern.match(line);
+ if (genericMatch.hasMatch()) {
+ bool ok = false;
+ const double value = genericMatch.captured(1).toDouble(&ok);
+ if (ok && value > 0.0) {
+ return static_cast(value);
+ }
+ }
+ }
+
+ return 0;
}
} // namespace
diff --git a/src/backend/monitor/rammonitor.cpp b/src/backend/monitor/rammonitor.cpp
index d761a77..8eac584 100644
--- a/src/backend/monitor/rammonitor.cpp
+++ b/src/backend/monitor/rammonitor.cpp
@@ -1,4 +1,5 @@
#include "rammonitor.h"
+#include "system/commandrunner.h"
#include
#include
@@ -6,6 +7,99 @@
#include
+namespace {
+
+struct RamSnapshot {
+ bool valid = false;
+ int totalMiB = 0;
+ int usedMiB = 0;
+ int usagePercent = 0;
+};
+
+QString meminfoPath() {
+ const QString overridePath = qEnvironmentVariable("RO_CONTROL_MEMINFO_PATH")
+ .trimmed();
+ return overridePath.isEmpty() ? QStringLiteral("/proc/meminfo") : overridePath;
+}
+
+RamSnapshot buildSnapshot(qint64 memTotalKiB, qint64 memAvailableKiB) {
+ if (memTotalKiB <= 0 || memAvailableKiB < 0 || memAvailableKiB > memTotalKiB) {
+ return {};
+ }
+
+ const qint64 usedKiB = memTotalKiB - memAvailableKiB;
+ RamSnapshot snapshot;
+ snapshot.valid = true;
+ snapshot.totalMiB = static_cast(memTotalKiB / 1024);
+ snapshot.usedMiB = static_cast(usedKiB / 1024);
+ snapshot.usagePercent =
+ std::clamp(static_cast((static_cast(usedKiB) /
+ static_cast(memTotalKiB)) *
+ 100.0),
+ 0, 100);
+ return snapshot;
+}
+
+RamSnapshot readSnapshotFromFree() {
+ CommandRunner runner;
+ const auto result =
+ runner.run(QStringLiteral("free"), {QStringLiteral("--mebi")});
+ if (!result.success()) {
+ return {};
+ }
+
+ const QStringList lines = result.stdout.split(QLatin1Char('\n'),
+ Qt::SkipEmptyParts);
+ for (const QString &line : lines) {
+ if (!line.startsWith(QStringLiteral("Mem:"))) {
+ continue;
+ }
+
+ const QStringList fields = line.split(
+ QRegularExpression(QStringLiteral(R"(\s+)")), Qt::SkipEmptyParts);
+ if (fields.size() < 3) {
+ return {};
+ }
+
+ bool totalOk = false;
+ const int totalMiB = fields.value(1).toInt(&totalOk);
+ if (!totalOk || totalMiB <= 0) {
+ return {};
+ }
+
+ int usedMiB = 0;
+ bool usedOk = false;
+ if (fields.size() >= 7) {
+ const int availableMiB = fields.value(6).toInt(&usedOk);
+ if (usedOk) {
+ usedMiB = std::clamp(totalMiB - availableMiB, 0, totalMiB);
+ }
+ }
+
+ if (!usedOk) {
+ usedMiB = fields.value(2).toInt(&usedOk);
+ if (!usedOk) {
+ return {};
+ }
+ usedMiB = std::clamp(usedMiB, 0, totalMiB);
+ }
+
+ RamSnapshot snapshot;
+ snapshot.valid = true;
+ snapshot.totalMiB = totalMiB;
+ snapshot.usedMiB = usedMiB;
+ snapshot.usagePercent = std::clamp(
+ static_cast((static_cast(usedMiB) /
+ static_cast(totalMiB)) * 100.0),
+ 0, 100);
+ return snapshot;
+ }
+
+ return {};
+}
+
+} // namespace
+
RamMonitor::RamMonitor(QObject *parent) : QObject(parent) {
m_timer.setInterval(1000);
m_timer.setTimerType(Qt::VeryCoarseTimer);
@@ -30,13 +124,6 @@ int RamMonitor::updateInterval() const { return m_timer.interval(); }
void RamMonitor::refresh() {
// TR: Linux RAM metrikleri /proc/meminfo uzerinden okunur.
// EN: Linux memory metrics are read from /proc/meminfo.
- QFile meminfo("/proc/meminfo");
- if (!meminfo.open(QIODevice::ReadOnly | QIODevice::Text)) {
- setAvailable(false);
- clearMetrics();
- return;
- }
-
qint64 memTotalKiB = -1;
qint64 memAvailableKiB = -1;
qint64 memFreeKiB = -1;
@@ -45,46 +132,49 @@ void RamMonitor::refresh() {
qint64 sReclaimableKiB = -1;
qint64 shmemKiB = -1;
- // TR: "Anahtar: deger" satirlarini guvenli sekilde ayriştir.
- QTextStream stream(&meminfo);
- while (!stream.atEnd()) {
- const QString line = stream.readLine().trimmed();
- if (line.isEmpty()) {
- continue;
- }
-
- const int colonIdx = line.indexOf(QLatin1Char(':'));
- if (colonIdx <= 0) {
- continue;
- }
-
- const QString key = line.left(colonIdx).trimmed();
- const QString valueStr = line.mid(colonIdx + 1).trimmed();
-
- // The value might be "19842104 kB". We just need the number.
- const int spaceIdx = valueStr.indexOf(QLatin1Char(' '));
- const QString numStr = spaceIdx > 0 ? valueStr.left(spaceIdx) : valueStr;
-
- bool ok = false;
- const qint64 value = numStr.toLongLong(&ok);
- if (!ok) {
- continue;
- }
-
- if (key == QStringLiteral("MemTotal")) {
- memTotalKiB = value;
- } else if (key == QStringLiteral("MemAvailable")) {
- memAvailableKiB = value;
- } else if (key == QStringLiteral("MemFree")) {
- memFreeKiB = value;
- } else if (key == QStringLiteral("Buffers")) {
- buffersKiB = value;
- } else if (key == QStringLiteral("Cached")) {
- cachedKiB = value;
- } else if (key == QStringLiteral("SReclaimable")) {
- sReclaimableKiB = value;
- } else if (key == QStringLiteral("Shmem")) {
- shmemKiB = value;
+ QFile meminfo(meminfoPath());
+ if (meminfo.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ // TR: "Anahtar: deger" satirlarini guvenli sekilde ayriştir.
+ QTextStream stream(&meminfo);
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine().trimmed();
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ const int colonIdx = line.indexOf(QLatin1Char(':'));
+ if (colonIdx <= 0) {
+ continue;
+ }
+
+ const QString key = line.left(colonIdx).trimmed();
+ const QString valueStr = line.mid(colonIdx + 1).trimmed();
+
+ // The value might be "19842104 kB". We just need the number.
+ const int spaceIdx = valueStr.indexOf(QLatin1Char(' '));
+ const QString numStr = spaceIdx > 0 ? valueStr.left(spaceIdx) : valueStr;
+
+ bool ok = false;
+ const qint64 value = numStr.toLongLong(&ok);
+ if (!ok) {
+ continue;
+ }
+
+ if (key == QStringLiteral("MemTotal")) {
+ memTotalKiB = value;
+ } else if (key == QStringLiteral("MemAvailable")) {
+ memAvailableKiB = value;
+ } else if (key == QStringLiteral("MemFree")) {
+ memFreeKiB = value;
+ } else if (key == QStringLiteral("Buffers")) {
+ buffersKiB = value;
+ } else if (key == QStringLiteral("Cached")) {
+ cachedKiB = value;
+ } else if (key == QStringLiteral("SReclaimable")) {
+ sReclaimableKiB = value;
+ } else if (key == QStringLiteral("Shmem")) {
+ shmemKiB = value;
+ }
}
}
@@ -107,27 +197,29 @@ void RamMonitor::refresh() {
return;
}
- const qint64 usedKiB = memTotalKiB - memAvailableKiB;
- const int nextTotalMiB = static_cast(memTotalKiB / 1024);
- const int nextUsedMiB = static_cast(usedKiB / 1024);
- const int nextUsagePercent =
- std::clamp(static_cast((static_cast(usedKiB) /
- static_cast(memTotalKiB)) *
- 100.0),
- 0, 100);
+ RamSnapshot snapshot = buildSnapshot(memTotalKiB, memAvailableKiB);
+ if (!snapshot.valid) {
+ snapshot = readSnapshotFromFree();
+ }
+
+ if (!snapshot.valid) {
+ setAvailable(false);
+ clearMetrics();
+ return;
+ }
- if (m_totalMiB != nextTotalMiB) {
- m_totalMiB = nextTotalMiB;
+ if (m_totalMiB != snapshot.totalMiB) {
+ m_totalMiB = snapshot.totalMiB;
emit totalMiBChanged();
}
- if (m_usedMiB != nextUsedMiB) {
- m_usedMiB = nextUsedMiB;
+ if (m_usedMiB != snapshot.usedMiB) {
+ m_usedMiB = snapshot.usedMiB;
emit usedMiBChanged();
}
- if (m_usagePercent != nextUsagePercent) {
- m_usagePercent = nextUsagePercent;
+ if (m_usagePercent != snapshot.usagePercent) {
+ m_usagePercent = snapshot.usagePercent;
emit usagePercentChanged();
}
diff --git a/src/qml/pages/MonitorPage.qml b/src/qml/pages/MonitorPage.qml
index 54dbe26..1c13550 100644
--- a/src/qml/pages/MonitorPage.qml
+++ b/src/qml/pages/MonitorPage.qml
@@ -10,10 +10,12 @@ Item {
property bool compactMode: false
property bool showAdvancedInfo: true
readonly property bool cpuTemperatureAvailable: cpuMonitor.temperatureC > 0
- readonly property bool gpuTelemetryAvailable: gpuMonitor.available || gpuMonitor.gpuName.length > 0
+ readonly property bool gpuTelemetryAvailable: gpuMonitor.available
readonly property bool gpuTemperatureAvailable: gpuMonitor.temperatureC > 0
readonly property bool gpuMemoryAvailable: gpuMonitor.memoryTotalMiB > 0
readonly property bool ramTelemetryAvailable: ramMonitor.available || ramMonitor.totalMiB > 0
+ readonly property bool gpuDetected: nvidiaDetector.gpuFound
+ readonly property bool gpuDriverActive: nvidiaDetector.driverLoaded || nvidiaDetector.nouveauActive
function formatTemperature(value) {
return value > 0 ? value + " C" : qsTr("Unavailable");
@@ -23,6 +25,34 @@ Item {
return totalMiB > 0 ? usedMiB + " / " + totalMiB + " MiB" : qsTr("Unavailable");
}
+ function gpuLoadValueText() {
+ if (page.gpuTelemetryAvailable)
+ return gpuMonitor.utilizationPercent + "%";
+ if (page.gpuDetected)
+ return qsTr("No Live Data");
+ return qsTr("Unavailable");
+ }
+
+ function gpuSubtitleText() {
+ if (page.gpuTelemetryAvailable)
+ return gpuMonitor.gpuName.length > 0 ? gpuMonitor.gpuName : qsTr("NVIDIA GPU");
+ if (!page.gpuDetected)
+ return qsTr("No NVIDIA GPU was detected on this system.");
+ if (!page.gpuDriverActive)
+ return qsTr("GPU detected, but no active driver is loaded.");
+ return qsTr("Live GPU telemetry is unavailable. Check nvidia-smi and driver access.");
+ }
+
+ function gpuSummaryText() {
+ if (page.gpuTelemetryAvailable)
+ return qsTr("GPU temperature: ") + page.formatTemperature(gpuMonitor.temperatureC) + qsTr(", VRAM ") + page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) + ".";
+ if (!page.gpuDetected)
+ return qsTr("No NVIDIA GPU is currently detected on this system.");
+ if (!page.gpuDriverActive)
+ return qsTr("GPU telemetry is unavailable because the NVIDIA driver is not active.");
+ return qsTr("GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.");
+ }
+
ScrollView {
id: pageScroll
anchors.fill: parent
@@ -55,10 +85,8 @@ Item {
Layout.fillWidth: true
theme: page.theme
title: qsTr("GPU Load")
- value: page.gpuTelemetryAvailable ? gpuMonitor.utilizationPercent + "%" : qsTr("Unavailable")
- subtitle: page.gpuTelemetryAvailable
- ? (gpuMonitor.gpuName.length > 0 ? gpuMonitor.gpuName : qsTr("NVIDIA GPU"))
- : qsTr("nvidia-smi did not return live GPU telemetry.")
+ value: page.gpuLoadValueText()
+ subtitle: page.gpuSubtitleText()
accentColor: page.theme.accentB
emphasized: page.gpuTemperatureAvailable && gpuMonitor.temperatureC >= 80
}
@@ -166,9 +194,7 @@ Item {
Layout.fillWidth: true
wrapMode: Text.Wrap
color: page.theme.textSoft
- text: page.gpuTelemetryAvailable
- ? qsTr("GPU temperature: ") + page.formatTemperature(gpuMonitor.temperatureC) + qsTr(", VRAM ") + page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) + "."
- : qsTr("GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.")
+ text: page.gpuSummaryText()
}
Label {
diff --git a/tests/test_monitor.cpp b/tests/test_monitor.cpp
index 30228eb..448217a 100644
--- a/tests/test_monitor.cpp
+++ b/tests/test_monitor.cpp
@@ -136,6 +136,34 @@ private slots:
ram.start();
QVERIFY(ram.running());
}
+
+ void testRamUsesOverriddenMeminfoPath() {
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const QString meminfoPath = tempDir.filePath(QStringLiteral("meminfo"));
+ QFile meminfo(meminfoPath);
+ QVERIFY(meminfo.open(QIODevice::WriteOnly | QIODevice::Text));
+ meminfo.write("MemTotal: 32768000 kB\n");
+ meminfo.write("MemAvailable: 23552000 kB\n");
+ meminfo.write("MemFree: 3072000 kB\n");
+ meminfo.write("Buffers: 204800 kB\n");
+ meminfo.write("Cached: 8192000 kB\n");
+ meminfo.close();
+
+ qputenv("RO_CONTROL_MEMINFO_PATH", meminfoPath.toUtf8());
+
+ RamMonitor ram;
+ ram.stop();
+ ram.refresh();
+
+ QVERIFY(ram.available());
+ QCOMPARE(ram.totalMiB(), 32000);
+ QCOMPARE(ram.usedMiB(), 9000);
+ QCOMPARE(ram.usagePercent(), 28);
+
+ qunsetenv("RO_CONTROL_MEMINFO_PATH");
+ }
};
QTEST_MAIN(TestMonitor)
From d2be68c3aa9f6c7d6385a73b64d7686d57fd72d1 Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 21:18:37 +0300
Subject: [PATCH 21/22] feat: Add ActionButton and DetailRow components, refine
UI styling for badges and banners, and update project metadata.
---
CMakeLists.txt | 2 +
CONTRIBUTING.md | 10 ++
README.md | 48 ++++++++-
README.tr.md | 48 ++++++++-
SUPPORT.md | 8 ++
...github.projectroasd.rocontrol.metainfo.xml | 23 +++-
docs/ARCHITECTURE.md | 15 ++-
docs/BUILDING.md | 6 +-
docs/FEDORA.md | 4 +-
docs/RELEASE.md | 1 +
src/qml/Main.qml | 45 ++++----
src/qml/components/ActionButton.qml | 52 +++++++++
src/qml/components/DetailRow.qml | 41 +++++++
src/qml/components/InfoBadge.qml | 10 +-
src/qml/components/SectionPanel.qml | 20 ++--
src/qml/components/SidebarMenu.qml | 39 ++++---
src/qml/components/StatCard.qml | 25 +++--
src/qml/components/StatusBanner.qml | 7 +-
src/qml/components/qmldir | 2 +
src/qml/pages/DriverPage.qml | 30 ++++--
src/qml/pages/MonitorPage.qml | 62 +++++------
src/qml/pages/SettingsPage.qml | 101 +++++++++---------
tests/test_driver_page.cpp | 10 +-
23 files changed, 428 insertions(+), 181 deletions(-)
create mode 100644 src/qml/components/ActionButton.qml
create mode 100644 src/qml/components/DetailRow.qml
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4f4d2d6..3be7524 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -128,6 +128,8 @@ qt_add_qml_module(ro-control
src/qml/pages/MonitorPage.qml
src/qml/pages/SettingsPage.qml
src/qml/components/InfoBadge.qml
+ src/qml/components/ActionButton.qml
+ src/qml/components/DetailRow.qml
src/qml/components/SectionPanel.qml
src/qml/components/StatusBanner.qml
src/qml/components/StatCard.qml
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 93a0cf6..f09979b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -143,6 +143,14 @@ cd build && ctest --output-on-failure
**PRs to `main` will be rejected.** All contributions go through `dev` first.
+Recommended PR checklist:
+
+- Keep scope focused to one feature/fix/theme
+- Update docs when user-visible behavior changes
+- Update translations when adding or changing UI strings
+- Include before/after screenshots for UI changes
+- Run `ctest --test-dir build --output-on-failure` locally
+
---
## Translations
@@ -186,3 +194,5 @@ Use the [bug report template](.github/ISSUE_TEMPLATE/bug_report.md) and include:
- Steps to reproduce
- Expected vs actual behavior
- Relevant terminal output, `coredumpctl info ro-control`, or recent journal entries
+
+For general setup questions, diagnostics help, or uncertainty about whether something is a bug, start with [SUPPORT.md](SUPPORT.md).
diff --git a/README.md b/README.md
index d71027e..7acf684 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,27 @@
ro-Control is a native KDE Plasma desktop application built with **C++20** and **Qt6/QML** that simplifies NVIDIA GPU driver management and system monitoring on Linux. It provides a modern, Plasma-native interface for installing, updating, and monitoring graphics drivers — with full PolicyKit integration for secure privilege escalation.
+## Project Status
+
+ro-Control is an active desktop utility for Fedora-first NVIDIA driver workflows.
+The current codebase focuses on:
+
+- Native Qt/QML desktop UX instead of wrapper scripts
+- Safe driver lifecycle operations through PolicyKit and DNF
+- Practical diagnostics for GPU, CPU, and RAM telemetry
+- English source strings with complete Turkish runtime localization
+
+It does **not** currently implement hybrid graphics switching, fan control, or overclocking.
+
+## Why This Repository Exists
+
+ro-Control is intended to be the NVIDIA operations and diagnostics surface for the broader **Project Ro ASD / ro-ASD OS** ecosystem. The repository is structured so it can be:
+
+- Shown on the organization profile as a flagship desktop utility
+- Built and packaged independently from the operating system image
+- Used both interactively from the GUI and programmatically from the CLI
+- Extended cleanly through separate backend, frontend, packaging, and translation layers
+
## Features
### 🚀 Driver Management
@@ -29,14 +50,13 @@ ro-Control is a native KDE Plasma desktop application built with **C++20** and *
- **Secure Boot** — Detection and warnings for unsigned kernel modules
### 📊 Live System Monitor
-- Real-time GPU temperature, load, and VRAM usage
-- CPU load and temperature tracking
-- RAM usage monitoring
+- Real-time GPU temperature, load, and VRAM usage when `nvidia-smi` is available
+- CPU load tracking with temperature probing via sysfs, hwmon, and `sensors`
+- RAM usage monitoring via `/proc/meminfo` with `free` fallback
- Color-coded progress indicators
### 🖥 Display & System
- **Wayland support** — Automatic `nvidia-drm.modeset=1` GRUB configuration
-- **Hybrid graphics** — Switch between NVIDIA, Intel, and On-Demand modes
- **PolicyKit integration** — Secure privilege escalation without running as root
## Development
@@ -65,6 +85,24 @@ The easiest way to develop ro-Control rapidly on Fedora is using the provided `d
- `ro-control driver install|remove|update|deep-clean` for scripted driver management
- Installed `man ro-control` page and Bash/Zsh/Fish shell completions
+### ✅ Test Coverage
+- Backend unit tests for detector, updater, monitor, CLI, and system integration flows
+- QML integration coverage for `DriverPage` state synchronization
+- Translation release target for shipped locales
+
+## Current Scope
+
+Supported well today:
+- Fedora-oriented NVIDIA driver install, update, and cleanup workflows
+- Driver-state inspection and diagnostics export
+- Monitor dashboard for live CPU/GPU/RAM status
+- App packaging metadata, shell completions, and man page support
+
+Deliberately out of scope for now:
+- Windows support
+- Non-Qt frontends
+- Advanced GPU tuning or gaming overlay features
+
## Screenshots
Preview assets are available under [`docs/screenshots/`](docs/screenshots/).
@@ -151,6 +189,8 @@ ro-Control/
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a pull request.
For release flow details, see [docs/RELEASE.md](docs/RELEASE.md).
For localization scaffolding, see [i18n/README.md](i18n/README.md).
+For architecture details, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
+For usage questions and issue routing, see [SUPPORT.md](SUPPORT.md).
Quick contribution flow:
diff --git a/README.tr.md b/README.tr.md
index 2aac2b4..789249e 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -20,6 +20,27 @@
ro-Control, **C++20** ve **Qt6/QML** ile geliştirilmiş, Linux üzerinde NVIDIA GPU sürücü yönetimini ve sistem izlemeyi kolaylaştıran native bir KDE Plasma masaüstü uygulamasıdır. Sürücülerin kurulumu, güncellenmesi ve izlenmesi için modern, Plasma'ya uyumlu bir arayüz sunar; güvenli yetki yükseltme için PolicyKit entegrasyonu içerir.
+## Proje Durumu
+
+ro-Control, Fedora odaklı NVIDIA sürücü iş akışları için aktif geliştirilen bir masaüstü yardımcı uygulamasıdır.
+Mevcut kod tabanı özellikle şu alanlara odaklanır:
+
+- Script sarmalayıcıları yerine yerel Qt/QML masaüstü deneyimi
+- PolicyKit ve DNF üzerinden güvenli sürücü yaşam döngüsü işlemleri
+- GPU, CPU ve RAM telemetrisi için pratik tanılama araçları
+- İngilizce kaynak metinler ve tam Türkçe çalışma zamanı yerelleştirmesi
+
+Şu anda **hibrit grafik geçişi**, fan kontrolü veya overclock özellikleri sunmaz.
+
+## Bu Depo Neden Var
+
+ro-Control, daha geniş **Project Ro ASD / ro-ASD OS** ekosistemi içinde NVIDIA işlemleri ve tanılama yüzeyi olarak konumlanır. Depo yapısı şu amaçlara uygundur:
+
+- Organizasyon profilinde öne çıkan bir masaüstü aracı olarak sergilenmek
+- İşletim sistemi imajından bağımsız şekilde derlenip paketlenebilmek
+- Hem GUI hem de CLI üzerinden kullanılabilmek
+- Backend, frontend, paketleme ve çeviri katmanlarında temiz şekilde genişletilebilmek
+
## Özellikler
### 🚀 Sürücü Yönetimi
@@ -29,14 +50,13 @@ ro-Control, **C++20** ve **Qt6/QML** ile geliştirilmiş, Linux üzerinde NVIDIA
- **Güvenli Önyükleme** — İmzasız kernel modülleri için tespit ve uyarı
### 📊 Canlı Sistem Monitörü
-- Gerçek zamanlı GPU sıcaklığı, yük ve VRAM kullanımı
-- CPU yükü ve sıcaklık takibi
-- RAM kullanım izleme
+- `nvidia-smi` erişilebildiğinde gerçek zamanlı GPU sıcaklığı, yük ve VRAM kullanımı
+- sysfs, hwmon ve `sensors` üzerinden CPU yükü ve sıcaklık takibi
+- `/proc/meminfo` ve gerektiğinde `free` fallback'i ile RAM kullanım izleme
- Renk kodlu ilerleme göstergeleri
### 🖥 Ekran & Sistem
- **Wayland desteği** — Otomatik `nvidia-drm.modeset=1` GRUB yapılandırması
-- **Hibrit grafik** — NVIDIA, Intel ve On-Demand modları arasında geçiş
- **PolicyKit entegrasyonu** — Root olarak çalıştırmadan güvenli yetki yükseltme
### 🌍 Çok Dil Desteği
@@ -52,6 +72,24 @@ ro-Control, **C++20** ve **Qt6/QML** ile geliştirilmiş, Linux üzerinde NVIDIA
- `ro-control driver install|remove|update|deep-clean` scriptlenebilir sürücü yönetimi sunar
- Kurulumla birlikte `man ro-control` sayfası ve Bash/Zsh/Fish completion dosyaları gelir
+### ✅ Test Kapsamı
+- Detector, updater, monitor, CLI ve sistem entegrasyonu için backend testleri
+- `DriverPage` durum senkronizasyonu için QML entegrasyon testi
+- Dağıtıma giren diller için translation release target doğrulaması
+
+## Mevcut Kapsam
+
+Bugün iyi desteklenenler:
+- Fedora odaklı NVIDIA sürücü kurulum, güncelleme ve temizlik iş akışları
+- Sürücü durumu denetimi ve tanılama çıktıları
+- CPU/GPU/RAM canlı durumunu gösteren monitor paneli
+- Paketleme metadatası, shell completion dosyaları ve man page desteği
+
+Şimdilik kapsam dışı olanlar:
+- Windows desteği
+- Qt dışı frontendler
+- İleri seviye GPU tuning veya oyun içi overlay özellikleri
+
## Ekran Görüntüleri
Önizleme görselleri [`docs/screenshots/`](docs/screenshots/) altında bulunur.
@@ -138,6 +176,8 @@ ro-Control/
Katkılarınızı bekliyoruz! Pull request göndermeden önce lütfen [CONTRIBUTING.md](CONTRIBUTING.md) dosyasını okuyun.
Sürüm akış detayları için [docs/RELEASE.md](docs/RELEASE.md) dosyasına bakın.
Yerelleştirme altyapısı için [i18n/README.md](i18n/README.md) dosyasını inceleyin.
+Mimari detaylar için [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) dosyasına bakın.
+Kullanım desteği ve issue yönlendirmesi için [SUPPORT.md](SUPPORT.md) dosyasına bakın.
Hızlı katkı akışı:
diff --git a/SUPPORT.md b/SUPPORT.md
index e52c21f..c14d792 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -6,6 +6,7 @@ Use the channels below based on your need:
- Usage questions and setup help: GitHub Discussions (recommended)
- Confirmed bugs: GitHub Issues using the Bug Report template
- Security concerns: see `SECURITY.md`
+- Project overview and scope: `README.md`
## Before Opening an Issue
@@ -16,12 +17,19 @@ Please include:
- Steps to reproduce
- Relevant logs/screenshots
+Useful commands:
+- `ro-control status`
+- `ro-control diagnostics --json`
+- `lspci | grep -i nvidia`
+- `nvidia-smi`
+
## Project Documentation
Read these first:
- `README.md`
- `docs/BUILDING.md`
- `docs/ARCHITECTURE.md`
+- `docs/FEDORA.md`
## Language
diff --git a/data/icons/io.github.projectroasd.rocontrol.metainfo.xml b/data/icons/io.github.projectroasd.rocontrol.metainfo.xml
index d53a247..b988bbb 100644
--- a/data/icons/io.github.projectroasd.rocontrol.metainfo.xml
+++ b/data/icons/io.github.projectroasd.rocontrol.metainfo.xml
@@ -10,16 +10,27 @@
- ro-Control helps users detect NVIDIA GPUs, install or update
- drivers via DNF, and monitor system metrics from a Qt desktop UI.
+ ro-Control is a Fedora-oriented desktop utility for NVIDIA driver
+ management and live system diagnostics. It helps users detect GPUs,
+ install or update drivers through DNF, and inspect GPU, CPU, and RAM
+ telemetry from a native Qt interface.
- ro-Control, kullanıcıların NVIDIA GPU'ları tespit etmesine,
- sürücüleri DNF ile kurup güncellemesine ve sistem metriklerini Qt tabanlı
- bir masaüstü arayüzünden izlemesine yardımcı olur.
+ ro-Control, Fedora odaklı bir NVIDIA sürücü yönetimi ve canlı sistem
+ tanılama masaüstü aracıdır. Kullanıcıların GPU'ları tespit etmesine,
+ sürücüleri DNF üzerinden kurup güncellemesine ve GPU, CPU, RAM
+ telemetrisini yerel bir Qt arayüzünden izlemesine yardımcı olur.
+
+ NVIDIA
+ Drivers
+ Diagnostics
+ Telemetry
+ Fedora
+
+
SystemSettings
@@ -46,5 +57,7 @@
https://github.com/Project-Ro-ASD/ro-Control
+ https://github.com/Project-Ro-ASD/ro-Controlhttps://github.com/Project-Ro-ASD/ro-Control/issues
+ https://github.com/Project-Ro-ASD/ro-Control/blob/main/SUPPORT.md
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index cf9b412..e600f59 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -21,7 +21,8 @@ ro-Control follows a strict **C++ Backend / QML Frontend** separation. The two l
│ Shell commands / D-Bus
┌───────────────────▼─────────────────────────────┐
│ Linux System │
-│ sysfs · nvidia-smi · dnf · pkexec · GRUB │
+│ sysfs · hwmon · sensors · nvidia-smi · dnf │
+│ pkexec · GRUB · /proc │
└─────────────────────────────────────────────────┘
```
@@ -51,9 +52,9 @@ Divided into three modules:
#### `monitor/` — Live Statistics
| File | Responsibility |
|------|---------------|
-| `gpumonitor.cpp` | Poll GPU temperature, load, VRAM via `nvidia-smi` or sysfs |
-| `cpumonitor.cpp` | Poll CPU load and temperature via `/proc/stat` and hwmon |
-| `rammonitor.cpp` | Poll RAM usage via `/proc/meminfo` |
+| `gpumonitor.cpp` | Poll GPU temperature, load, VRAM via `nvidia-smi` |
+| `cpumonitor.cpp` | Poll CPU load via `/proc/stat` and probe temperatures via thermal zones, hwmon, and `sensors` |
+| `rammonitor.cpp` | Poll RAM usage via `/proc/meminfo` with `free --mebi` fallback |
#### `system/` — System Integration
| File | Responsibility |
@@ -128,6 +129,12 @@ The PolicyKit action definition and helper entrypoint live in `data/polkit/` and
CMake 3.22+ with `qt_add_qml_module` for QML resource embedding. All QML files are compiled into the binary at build time — no loose `.qml` files needed at runtime.
+## Test Layout
+
+- `test_detector`, `test_updater`, `test_monitor`, `test_system_integration`, `test_cli`: backend and CLI regression coverage
+- `test_driver_page`: QML integration coverage for frontend/backend state bindings
+- `ro-control_lrelease`: release target that compiles shipped locale catalogs
+
See [BUILDING.md](BUILDING.md) for full build instructions.
---
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index afc4110..e1d6fc3 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -46,7 +46,7 @@ sudo dnf install \
Runtime tools used by diagnostics and driver operations:
```bash
-sudo dnf install dnf polkit pciutils mokutil kmod
+sudo dnf install dnf polkit pciutils mokutil kmod lm_sensors procps-ng
```
---
@@ -80,7 +80,7 @@ cmake --build build -j$(nproc)
```bash
lupdate src -ts i18n/ro-control_en.ts i18n/ro-control_tr.ts
-cmake --build build
+cmake --build build --target ro-control_lrelease
```
---
@@ -172,7 +172,7 @@ gcc --version # Should be 13+
rm -rf build/.qt build/CMakeFiles
cmake -S . -B build
lupdate src -ts i18n/ro-control_en.ts i18n/ro-control_tr.ts
-cmake --build build
+cmake --build build --target ro-control_lrelease
```
---
diff --git a/docs/FEDORA.md b/docs/FEDORA.md
index b07d5e0..2698f43 100644
--- a/docs/FEDORA.md
+++ b/docs/FEDORA.md
@@ -38,7 +38,7 @@ sudo dnf install -y \
Runtime tools used by diagnostics and driver workflows:
```bash
-sudo dnf install -y dnf polkit pciutils mokutil kmod
+sudo dnf install -y dnf polkit pciutils mokutil kmod lm_sensors procps-ng
```
## 3) Build and run
@@ -72,3 +72,5 @@ sudo cmake --install build
```
If GPU telemetry is unavailable, verify `nvidia-smi` is present and working.
+If CPU temperature is unavailable, verify `lm_sensors` is installed and sensors are exposed on the host.
+If RAM telemetry is unavailable, verify `/proc/meminfo` and `free --mebi` are accessible on the host.
diff --git a/docs/RELEASE.md b/docs/RELEASE.md
index bb29e41..1bafd18 100644
--- a/docs/RELEASE.md
+++ b/docs/RELEASE.md
@@ -21,6 +21,7 @@ Use this checklist for every production release.
- [ ] Update `CHANGELOG.md` with final release notes.
- [ ] Ensure AppStream metadata is up to date.
- [ ] Refresh translation sources and verify `.ts` files are complete.
+- [ ] Run `cmake --build build --target ro-control_lrelease` and confirm shipped locales compile cleanly.
- [ ] Smoke-test the app in English and Turkish locales.
## 4. Packaging
diff --git a/src/qml/Main.qml b/src/qml/Main.qml
index 9df0974..86f3dad 100644
--- a/src/qml/Main.qml
+++ b/src/qml/Main.qml
@@ -120,32 +120,32 @@ ApplicationWindow {
ColumnLayout {
anchors.fill: parent
- anchors.margins: root.compactMode ? 18 : 24
- spacing: root.compactMode ? 14 : 18
+ anchors.margins: root.compactMode ? 18 : 28
+ spacing: root.compactMode ? 14 : 20
Rectangle {
Layout.fillWidth: true
- radius: 28
+ radius: 30
color: root.theme.card
border.width: 1
border.color: root.theme.border
- implicitHeight: heroLayout.implicitHeight + 40
+ implicitHeight: heroLayout.implicitHeight + 44
RowLayout {
id: heroLayout
- x: 20
- y: 20
- width: parent.width - 40
- spacing: 18
+ x: 24
+ y: 22
+ width: parent.width - 48
+ spacing: 20
ColumnLayout {
Layout.fillWidth: true
- spacing: 6
+ spacing: 8
Label {
text: root.pageTitles[sidebar.currentIndex]
- font.pixelSize: 28
- font.bold: true
+ font.pixelSize: 30
+ font.weight: Font.Bold
color: root.theme.text
}
@@ -157,16 +157,21 @@ ApplicationWindow {
}
}
- InfoBadge {
- text: root.darkMode ? qsTr("System Dark") : qsTr("System Light")
- backgroundColor: root.theme.infoBg
- foregroundColor: root.theme.text
- }
+ Flow {
+ Layout.alignment: Qt.AlignTop
+ spacing: 10
+
+ InfoBadge {
+ text: root.darkMode ? qsTr("System Dark") : qsTr("System Light")
+ backgroundColor: root.theme.infoBg
+ foregroundColor: root.theme.text
+ }
- InfoBadge {
- text: root.compactMode ? qsTr("Compact Layout") : qsTr("Comfort Layout")
- backgroundColor: root.theme.cardStrong
- foregroundColor: root.theme.text
+ InfoBadge {
+ text: root.compactMode ? qsTr("Compact Layout") : qsTr("Comfort Layout")
+ backgroundColor: root.theme.cardStrong
+ foregroundColor: root.theme.text
+ }
}
}
}
diff --git a/src/qml/components/ActionButton.qml b/src/qml/components/ActionButton.qml
new file mode 100644
index 0000000..3e72578
--- /dev/null
+++ b/src/qml/components/ActionButton.qml
@@ -0,0 +1,52 @@
+import QtQuick
+import QtQuick.Controls
+
+Button {
+ id: control
+
+ required property var theme
+ property string tone: "neutral"
+
+ readonly property color fillColor: !enabled ? Qt.rgba(0, 0, 0, 0)
+ : tone === "primary" ? theme.accentA
+ : tone === "success" ? theme.success
+ : tone === "warning" ? theme.warning
+ : tone === "danger" ? theme.danger
+ : theme.cardStrong
+ readonly property color borderColor: !enabled ? theme.border
+ : tone === "primary" ? Qt.tint(theme.accentA, "#33ffffff")
+ : tone === "success" ? Qt.tint(theme.success, "#22ffffff")
+ : tone === "warning" ? Qt.tint(theme.warning, "#18ffffff")
+ : tone === "danger" ? Qt.tint(theme.danger, "#18ffffff")
+ : theme.border
+ readonly property color textColor: !enabled ? theme.textSoft
+ : tone === "neutral" ? theme.text
+ : "#ffffff"
+
+ implicitHeight: 46
+ implicitWidth: Math.max(160, contentItem.implicitWidth + leftPadding + rightPadding)
+ leftPadding: 18
+ rightPadding: 18
+ topPadding: 12
+ bottomPadding: 12
+
+ contentItem: Text {
+ text: control.text
+ color: control.textColor
+ font.pixelSize: 15
+ font.weight: Font.DemiBold
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle {
+ radius: 14
+ color: control.down && control.enabled ? Qt.darker(control.fillColor, 1.08)
+ : control.hovered && control.enabled ? Qt.tint(control.fillColor, "#08ffffff")
+ : control.fillColor
+ border.width: 1
+ border.color: control.borderColor
+ opacity: control.enabled ? 1.0 : 0.6
+ }
+}
diff --git a/src/qml/components/DetailRow.qml b/src/qml/components/DetailRow.qml
new file mode 100644
index 0000000..bde9ec5
--- /dev/null
+++ b/src/qml/components/DetailRow.qml
@@ -0,0 +1,41 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Rectangle {
+ id: row
+
+ required property var theme
+ property string label: ""
+ property string value: ""
+
+ radius: 12
+ color: theme.cardStrong
+ border.width: 1
+ border.color: theme.border
+ implicitHeight: contentRow.implicitHeight + 20
+
+ RowLayout {
+ id: contentRow
+ anchors.fill: parent
+ anchors.margins: 10
+ spacing: 12
+
+ Label {
+ Layout.preferredWidth: Math.min(180, row.width * 0.42)
+ text: row.label
+ color: row.theme.textMuted
+ font.pixelSize: 13
+ font.weight: Font.DemiBold
+ elide: Text.ElideRight
+ }
+
+ Label {
+ Layout.fillWidth: true
+ text: row.value
+ color: row.theme.text
+ font.pixelSize: 14
+ wrapMode: Text.Wrap
+ }
+ }
+}
diff --git a/src/qml/components/InfoBadge.qml b/src/qml/components/InfoBadge.qml
index 1cdd2dc..fa82b72 100644
--- a/src/qml/components/InfoBadge.qml
+++ b/src/qml/components/InfoBadge.qml
@@ -10,15 +10,17 @@ Rectangle {
radius: 999
color: backgroundColor
- implicitHeight: 30
- implicitWidth: badgeLabel.implicitWidth + 22
+ border.width: 1
+ border.color: Qt.tint(backgroundColor, "#22000000")
+ implicitHeight: 36
+ implicitWidth: badgeLabel.implicitWidth + 28
Label {
id: badgeLabel
anchors.centerIn: parent
text: badge.text
- font.pixelSize: 12
- font.bold: true
+ font.pixelSize: 13
+ font.weight: Font.DemiBold
color: badge.foregroundColor
}
}
diff --git a/src/qml/components/SectionPanel.qml b/src/qml/components/SectionPanel.qml
index 6a16e12..a50cba6 100644
--- a/src/qml/components/SectionPanel.qml
+++ b/src/qml/components/SectionPanel.qml
@@ -10,27 +10,27 @@ Rectangle {
property string subtitle: ""
default property alias content: bodyColumn.data
- radius: 22
+ radius: 24
color: theme.card
border.width: 1
border.color: theme.border
- implicitHeight: innerColumn.implicitHeight + 36
+ implicitHeight: innerColumn.implicitHeight + 40
ColumnLayout {
id: innerColumn
- x: 18
- y: 18
- width: parent.width - 36
- spacing: 14
+ x: 20
+ y: 20
+ width: parent.width - 40
+ spacing: 16
ColumnLayout {
- spacing: 4
+ spacing: 6
Layout.fillWidth: true
Label {
text: panel.title
- font.pixelSize: 18
- font.bold: true
+ font.pixelSize: 17
+ font.weight: Font.DemiBold
color: panel.theme.text
visible: text.length > 0
Layout.fillWidth: true
@@ -48,7 +48,7 @@ Rectangle {
ColumnLayout {
id: bodyColumn
- spacing: 10
+ spacing: 12
Layout.fillWidth: true
}
}
diff --git a/src/qml/components/SidebarMenu.qml b/src/qml/components/SidebarMenu.qml
index 55a9e5a..765e4a5 100644
--- a/src/qml/components/SidebarMenu.qml
+++ b/src/qml/components/SidebarMenu.qml
@@ -4,7 +4,7 @@ import QtQuick.Layouts
Rectangle {
id: sidebar
- width: 220
+ width: 248
required property var theme
color: theme.sidebarBg
clip: true
@@ -20,9 +20,9 @@ Rectangle {
Label {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottomMargin: 16
+ anchors.bottomMargin: 20
text: "v" + Qt.application.version
- font.pixelSize: 11
+ font.pixelSize: 12
color: theme.sidebarHint
z: 1
}
@@ -36,28 +36,30 @@ Rectangle {
// Başlık
Item {
Layout.fillWidth: true
- implicitHeight: 70
+ implicitHeight: 82
Label {
- anchors.centerIn: parent
+ anchors.left: parent.left
+ anchors.leftMargin: 22
+ anchors.verticalCenter: parent.verticalCenter
text: qsTr("ro-Control")
- font.pixelSize: 22
- font.bold: true
+ font.pixelSize: 25
+ font.weight: Font.Bold
color: theme.sidebarText
}
}
Rectangle {
Layout.fillWidth: true
- Layout.leftMargin: 16
- Layout.rightMargin: 16
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
height: 1
color: theme.sidebarBorder
}
Item {
Layout.fillWidth: true
- implicitHeight: 12
+ implicitHeight: 16
}
Repeater {
@@ -69,22 +71,25 @@ Rectangle {
required property string modelData
Layout.fillWidth: true
- Layout.leftMargin: 8
- Layout.rightMargin: 8
- implicitHeight: 44
- radius: 8
+ Layout.leftMargin: 10
+ Layout.rightMargin: 10
+ implicitHeight: 52
+ radius: 14
color: sidebar.currentIndex === menuItem.index ? theme.sidebarActive
: mouseArea.containsMouse ? theme.sidebarHover
: "transparent"
+ border.width: sidebar.currentIndex === menuItem.index ? 1 : 0
+ border.color: sidebar.currentIndex === menuItem.index ? theme.sidebarBorder : "transparent"
Label {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
- anchors.leftMargin: 16
+ anchors.leftMargin: 22
anchors.right: parent.right
- anchors.rightMargin: 8
+ anchors.rightMargin: 10
text: menuItem.modelData
- font.pixelSize: 14
+ font.pixelSize: 15
+ font.weight: sidebar.currentIndex === menuItem.index ? Font.DemiBold : Font.Medium
color: sidebar.currentIndex === menuItem.index ? theme.sidebarAccent : theme.sidebarMuted
elide: Text.ElideRight
}
diff --git a/src/qml/components/StatCard.qml b/src/qml/components/StatCard.qml
index a610236..fb71949 100644
--- a/src/qml/components/StatCard.qml
+++ b/src/qml/components/StatCard.qml
@@ -11,18 +11,21 @@ Rectangle {
property string accentColor: theme.accentB
property bool emphasized: false
property bool busy: false
+ property int minimumBodyHeight: 142
+ readonly property int valueLength: value.length
+ readonly property int valuePixelSize: valueLength > 22 ? 26 : valueLength > 12 ? 34 : 42
radius: 24
color: emphasized ? Qt.tint(theme.cardStrong, "#15000000") : theme.card
border.width: 1
border.color: theme.border
- implicitHeight: cardLayout.implicitHeight + 36
+ implicitHeight: Math.max(minimumBodyHeight, cardLayout.implicitHeight + 40)
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
- height: 6
+ height: 7
radius: 24
color: card.accentColor
opacity: 0.9
@@ -33,21 +36,26 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
- anchors.margins: 18
- spacing: 8
+ anchors.margins: 20
+ spacing: 10
Label {
text: card.title
color: card.theme.textMuted
- font.pixelSize: 13
- font.bold: true
+ font.pixelSize: 14
+ font.weight: Font.DemiBold
}
Label {
text: card.value
color: card.theme.text
- font.pixelSize: 28
- font.bold: true
+ font.pixelSize: card.valuePixelSize
+ font.weight: Font.Bold
+ wrapMode: Text.Wrap
+ Layout.fillWidth: true
+ maximumLineCount: 2
+ minimumPixelSize: 22
+ fontSizeMode: Text.Fit
}
Label {
@@ -56,6 +64,7 @@ Rectangle {
wrapMode: Text.Wrap
Layout.fillWidth: true
visible: text.length > 0
+ font.pixelSize: 13
}
BusyIndicator {
diff --git a/src/qml/components/StatusBanner.qml b/src/qml/components/StatusBanner.qml
index 4bace54..40b4471 100644
--- a/src/qml/components/StatusBanner.qml
+++ b/src/qml/components/StatusBanner.qml
@@ -22,20 +22,20 @@ Rectangle {
: tone === "error" ? "#5b1820"
: "#12304f"
- radius: 18
+ radius: 20
color: bannerColor
border.width: 1
border.color: borderTone
visible: text.length > 0
- implicitHeight: bannerLayout.implicitHeight + 24
+ implicitHeight: bannerLayout.implicitHeight + 26
RowLayout {
id: bannerLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
- anchors.margins: 12
+ anchors.margins: 14
spacing: 12
Rectangle {
@@ -50,6 +50,7 @@ Rectangle {
text: banner.text
wrapMode: Text.Wrap
color: banner.textTone
+ font.pixelSize: 14
}
}
}
diff --git a/src/qml/components/qmldir b/src/qml/components/qmldir
index d0be567..d384f11 100644
--- a/src/qml/components/qmldir
+++ b/src/qml/components/qmldir
@@ -1,3 +1,5 @@
+ActionButton 1.0 ActionButton.qml
+DetailRow 1.0 DetailRow.qml
InfoBadge 1.0 InfoBadge.qml
SectionPanel 1.0 SectionPanel.qml
SidebarMenu 1.0 SidebarMenu.qml
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 08b0543..3ee12b6 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -389,8 +389,10 @@ Item {
columnSpacing: 10
rowSpacing: 10
- Button {
+ ActionButton {
Layout.fillWidth: true
+ theme: page.theme
+ tone: "primary"
text: qsTr("Install Proprietary")
enabled: !nvidiaInstaller.busy && (!nvidiaInstaller.proprietaryAgreementRequired || eulaAccept.checked)
onClicked: {
@@ -399,8 +401,9 @@ Item {
}
}
- Button {
+ ActionButton {
Layout.fillWidth: true
+ theme: page.theme
text: qsTr("Install Nouveau")
enabled: !nvidiaInstaller.busy
onClicked: {
@@ -409,8 +412,10 @@ Item {
}
}
- Button {
+ ActionButton {
Layout.fillWidth: true
+ theme: page.theme
+ tone: "danger"
text: qsTr("Remove Driver")
enabled: !nvidiaInstaller.busy
onClicked: {
@@ -419,8 +424,9 @@ Item {
}
}
- Button {
+ ActionButton {
Layout.fillWidth: true
+ theme: page.theme
text: qsTr("Deep Clean")
enabled: !nvidiaInstaller.busy
onClicked: {
@@ -433,7 +439,8 @@ Item {
RowLayout {
Layout.fillWidth: true
- Button {
+ ActionButton {
+ theme: page.theme
text: qsTr("Rescan System")
enabled: !nvidiaInstaller.busy && !nvidiaUpdater.busy
onClicked: {
@@ -485,7 +492,8 @@ Item {
Layout.fillWidth: true
spacing: 10
- Button {
+ ActionButton {
+ theme: page.theme
text: qsTr("Check for Updates")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy
onClicked: {
@@ -494,7 +502,9 @@ Item {
}
}
- Button {
+ ActionButton {
+ theme: page.theme
+ tone: "primary"
text: page.driverInstalledLocally ? qsTr("Apply Latest") : qsTr("Install Latest")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && (nvidiaUpdater.updateAvailable || page.canInstallLatestRemoteDriver)
onClicked: {
@@ -519,7 +529,8 @@ Item {
textRole: "versionTitle"
}
- Button {
+ ActionButton {
+ theme: page.theme
text: qsTr("Apply Selected")
enabled: !nvidiaUpdater.busy && !nvidiaInstaller.busy && versionPicker.currentIndex >= 0 && page.remoteDriverCatalogAvailable
onClicked: {
@@ -565,7 +576,8 @@ Item {
}
}
- Button {
+ ActionButton {
+ theme: page.theme
text: qsTr("Clear Log")
onClicked: {
logArea.text = ""
diff --git a/src/qml/pages/MonitorPage.qml b/src/qml/pages/MonitorPage.qml
index 1c13550..9971c87 100644
--- a/src/qml/pages/MonitorPage.qml
+++ b/src/qml/pages/MonitorPage.qml
@@ -213,50 +213,36 @@ Item {
subtitle: qsTr("Expanded raw values for support and diagnostics.")
visible: page.showAdvancedInfo
- GridLayout {
+ ColumnLayout {
Layout.fillWidth: true
- columns: 2
- columnSpacing: 10
- rowSpacing: 8
-
- Label {
- text: qsTr("CPU Temperature")
- color: page.theme.textMuted
- }
-
- Label {
- text: page.formatTemperature(cpuMonitor.temperatureC)
- color: page.theme.text
- }
-
- Label {
- text: qsTr("GPU Temperature")
- color: page.theme.textMuted
- }
-
- Label {
- text: page.gpuTelemetryAvailable ? page.formatTemperature(gpuMonitor.temperatureC) : qsTr("Unknown")
- color: page.theme.text
- }
+ spacing: 8
- Label {
- text: qsTr("VRAM")
- color: page.theme.textMuted
+ DetailRow {
+ Layout.fillWidth: true
+ theme: page.theme
+ label: qsTr("CPU Temperature")
+ value: page.formatTemperature(cpuMonitor.temperatureC)
}
- Label {
- text: page.gpuTelemetryAvailable ? page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) : qsTr("Unknown")
- color: page.theme.text
+ DetailRow {
+ Layout.fillWidth: true
+ theme: page.theme
+ label: qsTr("GPU Temperature")
+ value: page.gpuTelemetryAvailable ? page.formatTemperature(gpuMonitor.temperatureC) : qsTr("Unknown")
}
- Label {
- text: qsTr("RAM Footprint")
- color: page.theme.textMuted
+ DetailRow {
+ Layout.fillWidth: true
+ theme: page.theme
+ label: qsTr("VRAM")
+ value: page.gpuTelemetryAvailable ? page.formatMemoryUsage(gpuMonitor.memoryUsedMiB, gpuMonitor.memoryTotalMiB) : qsTr("Unknown")
}
- Label {
- text: page.ramTelemetryAvailable ? page.formatMemoryUsage(ramMonitor.usedMiB, ramMonitor.totalMiB) : qsTr("Unknown")
- color: page.theme.text
+ DetailRow {
+ Layout.fillWidth: true
+ theme: page.theme
+ label: qsTr("RAM Footprint")
+ value: page.ramTelemetryAvailable ? page.formatMemoryUsage(ramMonitor.usedMiB, ramMonitor.totalMiB) : qsTr("Unknown")
}
}
}
@@ -271,7 +257,9 @@ Item {
Layout.fillWidth: true
spacing: 10
- Button {
+ ActionButton {
+ theme: page.theme
+ tone: "primary"
text: qsTr("Refresh Telemetry")
onClicked: {
cpuMonitor.refresh();
diff --git a/src/qml/pages/SettingsPage.qml b/src/qml/pages/SettingsPage.qml
index 635f50f..7571b01 100644
--- a/src/qml/pages/SettingsPage.qml
+++ b/src/qml/pages/SettingsPage.qml
@@ -162,50 +162,36 @@ Item {
title: qsTr("Diagnostics")
subtitle: qsTr("Useful runtime context before filing issues or performing support work.")
- GridLayout {
+ ColumnLayout {
Layout.fillWidth: true
- columns: 2
- columnSpacing: 10
- rowSpacing: 8
-
- Label {
- text: qsTr("Application")
- color: settingsPage.theme.textMuted
- }
-
- Label {
- text: Qt.application.name + " " + Qt.application.version
- color: settingsPage.theme.text
- }
-
- Label {
- text: qsTr("GPU")
- color: settingsPage.theme.textMuted
- }
-
- Label {
- text: nvidiaDetector.gpuFound ? nvidiaDetector.gpuName : qsTr("Not detected")
- color: settingsPage.theme.text
- }
+ spacing: 8
- Label {
- text: qsTr("Driver")
- color: settingsPage.theme.textMuted
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Application")
+ value: Qt.application.name + " " + Qt.application.version
}
- Label {
- text: nvidiaDetector.activeDriver
- color: settingsPage.theme.text
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("GPU")
+ value: nvidiaDetector.gpuFound ? nvidiaDetector.gpuName : qsTr("Not detected")
}
- Label {
- text: qsTr("Session")
- color: settingsPage.theme.textMuted
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Driver")
+ value: nvidiaDetector.activeDriver
}
- Label {
- text: nvidiaDetector.sessionType.length > 0 ? nvidiaDetector.sessionType : qsTr("Unknown")
- color: settingsPage.theme.text
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Session")
+ value: nvidiaDetector.sessionType.length > 0 ? nvidiaDetector.sessionType : qsTr("Unknown")
}
}
@@ -246,24 +232,37 @@ Item {
title: qsTr("About")
subtitle: qsTr("Project identity and current shell mode.")
- Label {
- text: qsTr("Application: ") + Qt.application.name + " (" + Qt.application.version + ")"
- color: settingsPage.theme.text
- }
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 8
- Label {
- text: qsTr("Theme: ") + (settingsPage.darkMode ? qsTr("System Dark") : qsTr("System Light"))
- color: settingsPage.theme.text
- }
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Application")
+ value: Qt.application.name + " (" + Qt.application.version + ")"
+ }
- Label {
- text: qsTr("Layout density: ") + (settingsPage.compactMode ? qsTr("Compact") : qsTr("Comfort"))
- color: settingsPage.theme.text
- }
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Theme")
+ value: settingsPage.darkMode ? qsTr("System Dark") : qsTr("System Light")
+ }
- Label {
- text: qsTr("Advanced diagnostics: ") + (settingsPage.showAdvancedInfo ? qsTr("Visible") : qsTr("Hidden"))
- color: settingsPage.theme.text
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Layout density")
+ value: settingsPage.compactMode ? qsTr("Compact") : qsTr("Comfort")
+ }
+
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Advanced diagnostics")
+ value: settingsPage.showAdvancedInfo ? qsTr("Visible") : qsTr("Hidden")
+ }
}
}
}
diff --git a/tests/test_driver_page.cpp b/tests/test_driver_page.cpp
index ef84bd5..040b13f 100644
--- a/tests/test_driver_page.cpp
+++ b/tests/test_driver_page.cpp
@@ -342,7 +342,8 @@ QObject *TestDriverPage::createPage(DetectorMock *detector,
tempPageFile.write(pageSource.toUtf8());
tempPageFile.close();
- const QStringList componentFiles = {QStringLiteral("InfoBadge.qml"),
+ const QStringList componentFiles = {QStringLiteral("ActionButton.qml"),
+ QStringLiteral("InfoBadge.qml"),
QStringLiteral("SectionPanel.qml"),
QStringLiteral("StatusBanner.qml"),
QStringLiteral("StatCard.qml")};
@@ -356,6 +357,13 @@ QObject *TestDriverPage::createPage(DetectorMock *detector,
}
}
+ const QString sourceQmldirPath =
+ QDir(sourceRoot).filePath(QStringLiteral("src/qml/components/qmldir"));
+ const QString targetQmldirPath = componentsDir + QStringLiteral("/qmldir");
+ if (!QFile::copy(sourceQmldirPath, targetQmldirPath)) {
+ qFatal("Failed to copy qmldir fixture for DriverPage test");
+ }
+
QQmlComponent component(engine, QUrl::fromLocalFile(tempPagePath));
if (!component.isReady()) {
From 37995a8f801b16ebac97554154ea4c31a36dd52c Mon Sep 17 00:00:00 2001
From: Sopwit <131982697+Sopwit@users.noreply.github.com>
Date: Sun, 22 Mar 2026 21:47:22 +0300
Subject: [PATCH 22/22] feat: harden distro UX, driver flows, and localization
---
.github/CODEOWNERS | 6 +
CMakeLists.txt | 3 +
README.md | 6 +-
README.tr.md | 6 +-
SECURITY.md | 42 +-
data/completions/_ro-control | 4 +-
data/completions/ro-control.fish | 4 +-
docs/ARCHITECTURE.md | 10 +-
docs/man/ro-control.1 | 4 +-
i18n/ro-control_de.ts | 1573 +++++++++++++++++++
i18n/ro-control_es.ts | 1573 +++++++++++++++++++
i18n/ro-control_tr.ts | 1001 ++++++------
src/backend/nvidia/detector.cpp | 28 +-
src/backend/nvidia/detector.h | 2 +
src/backend/nvidia/installer.cpp | 160 +-
src/backend/nvidia/updater.cpp | 54 +-
src/backend/nvidia/updater.h | 7 +-
src/backend/system/languagemanager.cpp | 42 +-
src/backend/system/languagemanager.h | 3 +
src/backend/system/uipreferencesmanager.cpp | 106 ++
src/backend/system/uipreferencesmanager.h | 44 +
src/cli/cli.cpp | 29 +-
src/main.cpp | 21 +-
src/qml/Main.qml | 16 +-
src/qml/pages/DriverPage.qml | 21 +-
src/qml/pages/SettingsPage.qml | 145 +-
tests/CMakeLists.txt | 15 +
tests/test_detector.cpp | 1 +
tests/test_preferences.cpp | 94 ++
tests/test_updater.cpp | 18 +-
30 files changed, 4374 insertions(+), 664 deletions(-)
create mode 100644 .github/CODEOWNERS
create mode 100644 i18n/ro-control_de.ts
create mode 100644 i18n/ro-control_es.ts
create mode 100644 src/backend/system/uipreferencesmanager.cpp
create mode 100644 src/backend/system/uipreferencesmanager.h
create mode 100644 tests/test_preferences.cpp
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..c691592
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,6 @@
+* @Project-Ro-ASD
+
+/src/backend/ @Project-Ro-ASD
+/src/qml/ @Project-Ro-ASD
+/docs/ @Project-Ro-ASD
+/packaging/ @Project-Ro-ASD
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3be7524..f54f960 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,6 +74,7 @@ set(BACKEND_SOURCES
src/backend/system/sessionutil.cpp
src/backend/system/capabilityprobe.cpp
src/backend/system/languagemanager.cpp
+ src/backend/system/uipreferencesmanager.cpp
)
set(APP_SOURCES
@@ -143,6 +144,8 @@ if(Qt6LinguistTools_FOUND)
# English strings ship directly from source code. Keep the English TS file
# for catalog maintenance, but only compile non-source locales into .qm.
set(TS_FILES
+ i18n/ro-control_de.ts
+ i18n/ro-control_es.ts
i18n/ro-control_tr.ts
)
diff --git a/README.md b/README.md
index 7acf684..fea0e2b 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ The current codebase focuses on:
- Safe driver lifecycle operations through PolicyKit and DNF
- Practical diagnostics for GPU, CPU, and RAM telemetry
- English source strings with complete Turkish runtime localization
+- Persistent interface preferences with explicit `System / Light / Dark` theme selection
It does **not** currently implement hybrid graphics switching, fan control, or overclocking.
@@ -58,6 +59,7 @@ ro-Control is intended to be the NVIDIA operations and diagnostics surface for t
### 🖥 Display & System
- **Wayland support** — Automatic `nvidia-drm.modeset=1` GRUB configuration
- **PolicyKit integration** — Secure privilege escalation without running as root
+- **Persistent shell preferences** — Saved theme mode, density, and diagnostics visibility
## Development
@@ -74,7 +76,7 @@ The easiest way to develop ro-Control rapidly on Fedora is using the provided `d
### 🌍 Internationalization
- Runtime locale loading with Qt translations (`.ts` / `.qm`)
-- English source strings with Turkish translation included
+- Shipped runtime locales: English and Turkish
- Extensible translation workflow for additional languages
### 🧰 CLI Support
@@ -86,7 +88,7 @@ The easiest way to develop ro-Control rapidly on Fedora is using the provided `d
- Installed `man ro-control` page and Bash/Zsh/Fish shell completions
### ✅ Test Coverage
-- Backend unit tests for detector, updater, monitor, CLI, and system integration flows
+- Backend unit tests for detector, updater, monitor, preferences, CLI, and system integration flows
- QML integration coverage for `DriverPage` state synchronization
- Translation release target for shipped locales
diff --git a/README.tr.md b/README.tr.md
index 789249e..a83f8a7 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -29,6 +29,7 @@ Mevcut kod tabanı özellikle şu alanlara odaklanır:
- PolicyKit ve DNF üzerinden güvenli sürücü yaşam döngüsü işlemleri
- GPU, CPU ve RAM telemetrisi için pratik tanılama araçları
- İngilizce kaynak metinler ve tam Türkçe çalışma zamanı yerelleştirmesi
+- Kalıcı arayüz tercihleri ve açık `Sistem / Açık / Koyu` tema seçimi
Şu anda **hibrit grafik geçişi**, fan kontrolü veya overclock özellikleri sunmaz.
@@ -58,10 +59,11 @@ ro-Control, daha geniş **Project Ro ASD / ro-ASD OS** ekosistemi içinde NVIDIA
### 🖥 Ekran & Sistem
- **Wayland desteği** — Otomatik `nvidia-drm.modeset=1` GRUB yapılandırması
- **PolicyKit entegrasyonu** — Root olarak çalıştırmadan güvenli yetki yükseltme
+- **Kalıcı kabuk tercihleri** — Kayıtlı tema modu, yoğunluk ve tanılama görünürlüğü
### 🌍 Çok Dil Desteği
- Qt çeviri sistemi (`.ts` / `.qm`) ile çalışma zamanı yerelleştirme
-- İngilizce kaynak metinler ve dahil edilmiş Türkçe çeviri
+- Dağıtıma giren çalışma zamanı dilleri: İngilizce ve Türkçe
- Yeni diller için genişletilebilir iş akışı
### 🧰 CLI Desteği
@@ -73,7 +75,7 @@ ro-Control, daha geniş **Project Ro ASD / ro-ASD OS** ekosistemi içinde NVIDIA
- Kurulumla birlikte `man ro-control` sayfası ve Bash/Zsh/Fish completion dosyaları gelir
### ✅ Test Kapsamı
-- Detector, updater, monitor, CLI ve sistem entegrasyonu için backend testleri
+- Detector, updater, monitor, preferences, CLI ve sistem entegrasyonu için backend testleri
- `DriverPage` durum senkronizasyonu için QML entegrasyon testi
- Dağıtıma giren diller için translation release target doğrulaması
diff --git a/SECURITY.md b/SECURITY.md
index 6cd9c93..35ffb9f 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,33 +1,31 @@
# Security Policy
-## Supported Versions
+## Supported Releases
-Security fixes are provided for the latest stable release and the `main` branch.
+Security fixes are applied to the latest development branch and the most recent tagged release that matches it.
| Version | Supported |
-| --- | --- |
-| Latest release | :white_check_mark: |
-| `main` branch | :white_check_mark: |
-| Older releases | :x: |
+|---------|-----------|
+| Latest tag | Yes |
+| `main` / active release line | Yes |
+| Older releases | No |
## Reporting a Vulnerability
-Please do not open public issues for security vulnerabilities.
+Do not open public GitHub issues for security-sensitive bugs.
-Report vulnerabilities by email:
-- `security@project-ro.dev` (preferred)
+Use GitHub Security Advisories for private reporting:
+- Repository Security tab
+- `Report a vulnerability`
-Include the following details in your report:
-- A clear description of the issue and impact
-- Steps to reproduce or a proof of concept
-- Affected version/commit
-- Any suggested mitigation
+Include the following where possible:
+- affected ro-Control version
+- distro and desktop session
+- exact steps to reproduce
+- whether privilege escalation or package operations are involved
+- logs, screenshots, or proof-of-concept details
-## Response Timeline
-
-We aim to:
-- Acknowledge reports within 72 hours
-- Provide an initial assessment within 7 days
-- Share remediation or workaround details as soon as possible
-
-Thank you for helping keep ro-Control and its users safe.
+You can expect:
+- an acknowledgement after triage
+- confirmation if the issue is reproducible
+- a fix or mitigation plan for supported versions
diff --git a/data/completions/_ro-control b/data/completions/_ro-control
index c072b88..2e7bb1a 100644
--- a/data/completions/_ro-control
+++ b/data/completions/_ro-control
@@ -28,8 +28,8 @@ global_opts=(
local -a install_opts
install_opts=(
'--proprietary[Use the proprietary NVIDIA driver install path]'
- '--open-source[Use the open-source Nouveau install path]'
- '--accept-license[Confirm proprietary driver license acceptance]'
+ '--open-source[Use the NVIDIA open kernel module install path]'
+ '--accept-license[Confirm NVIDIA license review for the proprietary install path]'
)
if (( CURRENT == 2 )); then
diff --git a/data/completions/ro-control.fish b/data/completions/ro-control.fish
index 8b69ee9..72dac0c 100644
--- a/data/completions/ro-control.fish
+++ b/data/completions/ro-control.fish
@@ -19,5 +19,5 @@ complete -c ro-control -n "__fish_seen_subcommand_from driver; and not __fish_se
complete -c ro-control -n "__fish_seen_subcommand_from driver; and not __fish_seen_subcommand_from install remove update deep-clean" -a deep-clean -d "Remove legacy NVIDIA leftovers"
complete -c ro-control -n "__fish_seen_subcommand_from install" -l proprietary -d "Use the proprietary NVIDIA driver install path"
-complete -c ro-control -n "__fish_seen_subcommand_from install" -l open-source -d "Use the open-source Nouveau install path"
-complete -c ro-control -n "__fish_seen_subcommand_from install" -l accept-license -d "Confirm proprietary driver license acceptance"
+complete -c ro-control -n "__fish_seen_subcommand_from install" -l open-source -d "Use the NVIDIA open kernel module install path"
+complete -c ro-control -n "__fish_seen_subcommand_from install" -l accept-license -d "Confirm NVIDIA license review for the proprietary install path"
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index e600f59..229b36a 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -61,7 +61,9 @@ Divided into three modules:
|------|---------------|
| `commandrunner.cpp` | Execute shell commands, capture stdout/stderr |
| `dnfmanager.cpp` | Wrap DNF commands for install/remove/update |
+| `languagemanager.cpp` | Load runtime locale catalogs and expose shipped language choices |
| `polkit.cpp` | Privilege escalation via `pkexec` / PolicyKit D-Bus |
+| `uipreferencesmanager.cpp` | Persist theme mode, density, and diagnostics visibility |
---
@@ -131,7 +133,7 @@ CMake 3.22+ with `qt_add_qml_module` for QML resource embedding. All QML files a
## Test Layout
-- `test_detector`, `test_updater`, `test_monitor`, `test_system_integration`, `test_cli`: backend and CLI regression coverage
+- `test_detector`, `test_updater`, `test_monitor`, `test_preferences`, `test_system_integration`, `test_cli`: backend and CLI regression coverage
- `test_driver_page`: QML integration coverage for frontend/backend state bindings
- `ro-control_lrelease`: release target that compiles shipped locale catalogs
@@ -157,8 +159,10 @@ ro-Control/
│ │ ├── system/
│ │ │ ├── commandrunner.h / commandrunner.cpp
│ │ │ ├── dnfmanager.h / dnfmanager.cpp
+│ │ │ ├── languagemanager.h / languagemanager.cpp
│ │ │ ├── polkit.h / polkit.cpp
-│ │ │ └── sessionutil.h / sessionutil.cpp
+│ │ │ ├── sessionutil.h / sessionutil.cpp
+│ │ │ └── uipreferencesmanager.h / uipreferencesmanager.cpp
│ │ └── cli/
│ │ └── cli.h / cli.cpp
│ ├── qml/
@@ -166,6 +170,8 @@ ro-Control/
│ │ │ ├── ro-control-logo.png
│ │ │ └── ro-control-logo.svg
│ │ ├── components/
+│ │ │ ├── ActionButton.qml
+│ │ │ ├── DetailRow.qml
│ │ │ ├── InfoBadge.qml
│ │ │ ├── SectionPanel.qml
│ │ │ ├── SidebarMenu.qml
diff --git a/docs/man/ro-control.1 b/docs/man/ro-control.1
index fae4acc..df8934f 100644
--- a/docs/man/ro-control.1
+++ b/docs/man/ro-control.1
@@ -57,10 +57,10 @@ output as JSON.
Use the proprietary NVIDIA driver install path.
.TP
.B \-\-open-source
-Use the open-source Nouveau install path.
+Use the NVIDIA open kernel module install path.
.TP
.B \-\-accept-license
-Confirm that the NVIDIA proprietary license has been reviewed.
+Confirm that the NVIDIA license has been reviewed for the proprietary install path.
.SH EXIT STATUS
.TP
.B 0
diff --git a/i18n/ro-control_de.ts b/i18n/ro-control_de.ts
new file mode 100644
index 0000000..d44e420
--- /dev/null
+++ b/i18n/ro-control_de.ts
@@ -0,0 +1,1573 @@
+
+
+
+
+ DriverPage
+
+ Driver Management
+ Driver Management
+
+
+ GPU:
+ GPU:
+
+
+ Not detected
+ Not detected
+
+
+ Active driver:
+ Active driver:
+
+
+ Driver version:
+ Driver version:
+
+
+
+
+ None
+ None
+
+
+ Secure Boot:
+ Secure Boot:
+
+
+
+ Repository Setup
+
+
+
+
+ Package Transaction
+
+
+
+
+ Kernel Integration
+
+
+
+
+ Session Finalization
+
+
+
+
+ Update Check
+
+
+
+
+ General
+
+
+
+
+ Installed
+
+
+
+
+ Latest
+
+
+
+
+ GPU Detection
+
+
+
+
+ Detected
+
+
+
+
+ Missing
+
+
+
+
+ No NVIDIA GPU was detected on this system.
+
+
+
+
+ Active Driver
+
+
+
+
+ Session:
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Installed Version
+
+
+
+
+ Latest available online:
+
+
+
+
+ No pending online package update detected.
+
+
+
+
+ Latest driver found online:
+
+
+
+
+ No online driver catalog has been loaded yet.
+
+
+
+
+ Secure Boot
+
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unsigned kernel modules may require manual signing.
+
+
+
+
+ No Secure Boot blocker is currently detected.
+
+
+
+
+ Verification
+
+
+
+
+ Review driver prerequisites before changing packages.
+
+
+
+
+ GPU Ready
+
+
+
+
+ GPU Missing
+
+
+
+
+ Wayland Session
+
+
+
+
+ X11 / Other Session
+
+
+
+
+ Kernel Module Loaded
+
+
+
+
+ Kernel Module Missing
+
+
+
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
+
+
+
+
+ X11 sessions require matching userspace packages after install or update.
+
+
+
+
+ Driver Actions
+
+
+
+
+ Use guided actions to install, switch or remove the current stack.
+
+
+
+
+ Source:
+
+
+
+
+
+
+ Idle
+
+
+
+
+ Phase:
+
+
+
+
+ Running
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Installer
+
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+
+ Remove Driver
+
+
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+ Search the online package catalog, then download and install a matching driver build.
+
+
+
+
+ Remote Driver Available
+
+
+
+
+ Catalog Not Ready
+
+
+
+
+
+
+
+
+
+
+ Updater
+
+
+
+
+ Searching the online NVIDIA package catalog...
+
+
+
+
+ Install Latest
+
+
+
+
+ Updating NVIDIA driver to the latest online version...
+
+
+
+
+ Downloading and installing the latest online NVIDIA driver...
+
+
+
+
+ Switching NVIDIA driver to selected online version:
+
+
+
+
+ Downloading and installing selected NVIDIA driver version:
+
+
+
+
+ Online repository versions loaded:
+
+
+
+
+ No online repository version list has been loaded yet.
+
+
+
+
+ Apply Latest
+
+
+
+
+ Fallback Open Driver Active
+
+
+
+
+ Fallback Open Driver Inactive
+
+
+
+
+ Elapsed:
+
+
+
+
+ Last Log:
+
+
+
+
+ I reviewed the NVIDIA license terms
+
+
+
+
+ Official NVIDIA license: <a href="%1">%1</a>
+
+
+
+
+ Install NVIDIA Driver
+ NVIDIA-Treiber installieren
+
+
+
+ Install Open Kernel Modules
+ Offene Kernelmodule installieren
+
+
+
+ Switching to NVIDIA open kernel modules...
+
+
+
+
+ Apply Selected
+ Auswahl anwenden
+
+
+
+ Activity Log
+ Aktivitaetsprotokoll
+
+
+
+ Operation output is streamed here in real time.
+
+
+
+
+ Clear Log
+ Protokoll leeren
+
+
+ Session type:
+ Session type:
+
+
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+
+
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+
+
+ I accept the license / agreement terms
+ I accept the license / agreement terms
+
+
+ Install Proprietary Driver
+ Install Proprietary Driver
+
+
+ Install Open-Source Driver (Nouveau)
+ Install Open-Source Driver (Nouveau)
+
+
+
+ Deep Clean
+ Deep Clean
+
+
+
+ Rescan System
+
+
+
+
+ Update Center
+
+
+
+
+ Installed:
+
+
+
+
+ Update Available
+
+
+
+
+ Up to Date
+
+
+
+
+ Check for Updates
+ Check for Updates
+
+
+ Apply Update
+ Apply Update
+
+
+ Latest version:
+ Latest version:
+
+
+ Rescan
+ Rescan
+
+
+ Installed NVIDIA version:
+ Installed NVIDIA version:
+
+
+
+ Main
+
+
+ ro-Control
+ ro-Control
+
+
+
+ Driver Control Center
+ Treiberzentrale
+
+
+
+ System Monitor
+ Systemmonitor
+
+
+
+ Preferences
+ Einstellungen
+
+
+
+ Install, verify and update NVIDIA drivers with guided system checks.
+
+
+
+
+ Track live CPU, GPU and memory telemetry in one place.
+
+
+
+
+ Tune the interface and review diagnostic context before support work.
+
+
+
+
+ Follow System
+
+
+
+
+ Dark Theme
+
+
+
+
+ Light Theme
+
+
+
+ System Dark
+ System Dark
+
+
+ System Light
+ System Light
+
+
+
+ Compact Layout
+
+
+
+
+ Comfort Layout
+
+
+
+ Theme: System (Dark)
+ Theme: System (Dark)
+
+
+ Theme: System (Light)
+ Theme: System (Light)
+
+
+ Driver
+ Driver
+
+
+ Monitor
+ Monitor
+
+
+ Settings
+ Settings
+
+
+
+ MonitorPage
+
+ System Monitoring
+ System Monitoring
+
+
+
+ CPU Load
+
+
+
+
+
+
+
+
+ Unavailable
+
+
+
+
+ CPU telemetry is currently unavailable.
+
+
+
+
+ GPU Load
+
+
+
+
+ Memory Usage
+
+
+
+
+ Used:
+
+
+
+
+ RAM telemetry is currently unavailable.
+
+
+
+
+ Live Resource Curves
+
+
+
+
+ Quick pulse view for the most important machine resources.
+
+
+
+
+ CPU
+ CPU
+
+
+
+ GPU
+
+
+
+
+ Health Summary
+
+
+
+
+ Fast interpretation of the raw telemetry values.
+
+
+
+
+ CPU Busy
+
+
+
+
+ CPU Stable
+
+
+
+
+ GPU Online
+
+
+
+
+ GPU Telemetry Missing
+
+
+
+
+ Memory Pressure
+
+
+
+
+ Memory Stable
+
+
+
+
+ GPU temperature:
+
+
+
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.
+
+
+
+
+ Detailed Signals
+
+
+
+
+ Expanded raw values for support and diagnostics.
+
+
+
+
+ CPU Temperature
+
+
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ GPU Temperature
+
+
+
+
+ No Live Data
+
+
+
+
+ No NVIDIA GPU was detected on this system.
+
+
+
+
+ GPU detected, but no active driver is loaded.
+
+
+
+
+ Live GPU telemetry is unavailable. Check nvidia-smi and driver access.
+
+
+
+
+ , VRAM
+
+
+
+
+ No NVIDIA GPU is currently detected on this system.
+
+
+
+
+ GPU telemetry is unavailable because the NVIDIA driver is not active.
+
+
+
+
+ VRAM
+
+
+
+
+ RAM Footprint
+
+
+
+
+ Actions
+
+
+
+
+ Trigger a manual refresh when you need a fresh sample.
+
+
+
+
+ Refresh Telemetry
+
+
+
+
+ NVIDIA Path OK
+
+
+
+
+ Check NVIDIA Path
+
+
+
+ Usage:
+ Usage:
+
+
+ CPU data unavailable
+ CPU data unavailable
+
+
+
+ Temperature:
+ Temperature:
+
+
+ GPU (NVIDIA)
+ GPU (NVIDIA)
+
+
+
+ NVIDIA GPU
+ NVIDIA GPU
+
+
+ Failed to read data via nvidia-smi
+ Failed to read data via nvidia-smi
+
+
+ Load:
+ Load:
+
+
+ VRAM:
+ VRAM:
+
+
+
+ RAM
+ RAM
+
+
+ RAM data unavailable
+ RAM data unavailable
+
+
+ Refresh
+ Refresh
+
+
+
+ Refresh interval:
+ Refresh interval:
+
+
+
+ NvidiaDetector
+
+ Proprietary (NVIDIA)
+ Proprietary (NVIDIA)
+
+
+ Open Source (Nouveau)
+ Open Source (Nouveau)
+
+
+
+ Not Installed / Unknown
+ Not Installed / Unknown
+
+
+
+
+ None
+ None
+
+
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+NVIDIA Module: %5
+Nouveau: %6
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+NVIDIA Module: %5
+Nouveau: %6
+
+
+
+ NVIDIA Open Kernel Modules
+
+
+
+
+ NVIDIA Driver
+
+
+
+
+ Fallback Open Driver
+
+
+
+
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+Active Stack: %5
+Fallback Open Driver: %6
+
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled
+
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unknown
+ Unknown
+
+
+ Loaded
+ Loaded
+
+
+ Not loaded
+ Not loaded
+
+
+
+ Active
+ Active
+
+
+
+ Inactive
+ Inactive
+
+
+
+ NvidiaInstaller
+
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+
+
+ License agreement acceptance is required before installation.
+ License agreement acceptance is required before installation.
+
+
+
+ Starting command (attempt %1): %2
+
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+
+
+
+
+ The proprietary NVIDIA driver is subject to NVIDIA's software license. Review the official NVIDIA license before installation: %1
+
+
+
+
+ NVIDIA license review confirmation is required before installation.
+
+
+
+
+ Checking RPM Fusion repositories...
+ Checking RPM Fusion repositories...
+
+
+
+ Platform version could not be detected.
+ Platform version could not be detected.
+
+
+
+ Failed to enable RPM Fusion repositories:
+ Failed to enable RPM Fusion repositories:
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+
+ Installation failed:
+ Installation failed:
+
+
+
+
+ Building the kernel module (akmods --force)...
+ Building the kernel module (akmods --force)...
+
+
+
+
+ Kernel module build failed:
+
+
+
+
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+
+
+
+ Switching to NVIDIA open kernel modules...
+
+
+
+
+ Failed to remove conflicting NVIDIA kernel packages:
+
+
+
+
+ Open NVIDIA kernel module installation failed:
+
+
+
+
+ unknown error
+
+
+
+
+ NVIDIA open kernel modules were installed successfully. Please restart the system.
+
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+ Failed to remove proprietary packages:
+ Failed to remove proprietary packages:
+
+
+ Open-source driver installation failed:
+ Open-source driver installation failed:
+
+
+ The open-source driver (Nouveau) was installed. Please restart the system.
+ The open-source driver (Nouveau) was installed. Please restart the system.
+
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+ Driver removed successfully.
+ Driver removed successfully.
+
+
+
+ Removal failed:
+ Removal failed:
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+ Deep clean failed:
+
+
+
+
+ DNF cache cleanup failed:
+
+
+
+
+ Deep clean completed.
+ Deep clean completed.
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Another driver operation is already running.
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Legacy NVIDIA cleanup completed.
+
+
+
+
+ Failed to apply the Wayland kernel parameter:
+ Failed to apply the Wayland kernel parameter:
+
+
+
+ X11 detected: checking NVIDIA userspace packages...
+ X11 detected: checking NVIDIA userspace packages...
+
+
+
+ Failed to install the X11 NVIDIA package:
+ Failed to install the X11 NVIDIA package:
+
+
+
+ NvidiaUpdater
+
+ Updating the NVIDIA driver...
+ Updating the NVIDIA driver...
+
+
+
+ Update failed:
+ Update failed:
+
+
+ Rebuilding the kernel module...
+ Rebuilding the kernel module...
+
+
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+
+
+
+ Driver updated successfully. Please restart the system.
+ Driver updated successfully. Please restart the system.
+
+
+
+
+ dnf not found.
+
+
+
+
+ Online NVIDIA packages were found. You can download and install the driver now.
+
+
+
+
+ Online NVIDIA driver found. Latest remote version: %1
+
+
+
+
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.
+
+
+
+
+ Update found (version details unavailable).
+
+
+
+
+ Update found: %1
+
+
+
+
+ Driver is up to date. No new version found.
+
+
+
+
+ Update check failed: %1
+
+
+
+
+ Starting command (attempt %1): %2
+
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+
+
+
+
+ Another driver operation is already running.
+
+
+
+
+ Kernel module build failed:
+
+
+
+
+
+
+ unknown error
+
+
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Failed to update the Wayland kernel parameter:
+
+
+
+
+ No available versions found.
+
+
+
+
+ Available versions: %1
+
+
+
+
+ Starting update check...
+
+
+
+
+ Selected version not found in the repository.
+
+
+
+
+ Updating NVIDIA driver to the latest version...
+
+
+
+
+ Switching NVIDIA driver to selected version: %1
+
+
+
+
+ Driver is already at the latest available version.
+
+
+
+
+ Selected driver version is already installed.
+
+
+
+
+ Rebuilding kernel module...
+
+
+
+
+ Latest version installed successfully. Please restart the system.
+
+
+
+
+ Selected version applied successfully. Please restart the system.
+
+
+
+
+ SettingsPage
+
+ Settings
+ Settings
+
+
+
+ Language
+
+
+
+
+ Changes the application language immediately and keeps the selection for the next launch.
+
+
+
+
+ Compact layout
+
+
+
+
+ Reduces spacing to fit more information on screen.
+
+
+
+
+ Show advanced diagnostics
+
+
+
+
+ Shows verification reports and expanded monitor metrics.
+
+
+
+
+ Language:
+
+
+
+
+ Appearance & Behavior
+ Darstellung und Verhalten
+
+
+
+ Control theme, density and operator-focused interface behavior.
+
+
+
+
+ Theme mode
+ Themenmodus
+
+
+
+ Choose whether the application follows the OS theme or uses an explicit light or dark shell.
+
+
+
+
+ Theme: Follow System
+
+
+
+
+ Theme: Dark
+
+
+
+
+ Theme: Light
+
+
+
+
+ Compact Active
+
+
+
+
+ Comfort Active
+
+
+
+
+ Advanced Visible
+
+
+
+
+ Advanced Hidden
+
+
+
+
+ Restore the recommended interface defaults if the shell starts to feel cluttered.
+
+
+
+
+ Reset Interface Defaults
+
+
+
+
+ Diagnostics
+
+
+
+
+ Useful runtime context before filing issues or performing support work.
+
+
+
+
+
+ Application
+
+
+
+
+ GPU
+
+
+
+
+ Not detected
+ Not detected
+
+
+
+ Driver
+ Driver
+
+
+
+ Session
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Use the Driver page to refresh detection before copying any diagnostic context.
+
+
+
+
+ Workflow Guidance
+
+
+
+
+ Recommended order of operations when changing drivers.
+
+
+
+
+ 1. Verify GPU detection and session type.
+2. Install or switch the driver stack.
+3. Check repository updates.
+4. Restart after successful package operations.
+
+
+
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.
+
+
+
+
+ No Secure Boot blocker is currently reported by the detector.
+
+
+
+
+ About
+ About
+
+
+
+ Project identity and current shell mode.
+
+
+
+
+ Theme
+
+
+
+
+ Follow System
+ System folgen
+
+
+
+ Dark
+ Dunkel
+
+
+
+ Light
+ Hell
+
+
+
+ Effective language
+
+
+
+
+ Layout density
+
+
+
+
+ Advanced diagnostics
+
+
+
+ Application:
+ Application:
+
+
+ Theme:
+ Theme:
+
+
+ System Dark
+ System Dark
+
+
+ System Light
+ System Light
+
+
+
+ Compact
+
+
+
+
+ Comfort
+
+
+
+
+ Visible
+
+
+
+
+ Hidden
+
+
+
+
+ SidebarMenu
+
+
+ Driver Management
+ Treiberverwaltung
+
+
+
+ System Monitoring
+ Systemueberwachung
+
+
+
+ Settings
+ Einstellungen
+
+
+
+ ro-Control
+ ro-Control
+
+
+
diff --git a/i18n/ro-control_es.ts b/i18n/ro-control_es.ts
new file mode 100644
index 0000000..0b2ff9a
--- /dev/null
+++ b/i18n/ro-control_es.ts
@@ -0,0 +1,1573 @@
+
+
+
+
+ DriverPage
+
+ Driver Management
+ Driver Management
+
+
+ GPU:
+ GPU:
+
+
+ Not detected
+ Not detected
+
+
+ Active driver:
+ Active driver:
+
+
+ Driver version:
+ Driver version:
+
+
+
+
+ None
+ None
+
+
+ Secure Boot:
+ Secure Boot:
+
+
+
+ Repository Setup
+
+
+
+
+ Package Transaction
+
+
+
+
+ Kernel Integration
+
+
+
+
+ Session Finalization
+
+
+
+
+ Update Check
+
+
+
+
+ General
+
+
+
+
+ Installed
+
+
+
+
+ Latest
+
+
+
+
+ GPU Detection
+
+
+
+
+ Detected
+
+
+
+
+ Missing
+
+
+
+
+ No NVIDIA GPU was detected on this system.
+
+
+
+
+ Active Driver
+
+
+
+
+ Session:
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Installed Version
+
+
+
+
+ Latest available online:
+
+
+
+
+ No pending online package update detected.
+
+
+
+
+ Latest driver found online:
+
+
+
+
+ No online driver catalog has been loaded yet.
+
+
+
+
+ Secure Boot
+
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unsigned kernel modules may require manual signing.
+
+
+
+
+ No Secure Boot blocker is currently detected.
+
+
+
+
+ Verification
+
+
+
+
+ Review driver prerequisites before changing packages.
+
+
+
+
+ GPU Ready
+
+
+
+
+ GPU Missing
+
+
+
+
+ Wayland Session
+
+
+
+
+ X11 / Other Session
+
+
+
+
+ Kernel Module Loaded
+
+
+
+
+ Kernel Module Missing
+
+
+
+
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.
+
+
+
+
+ X11 sessions require matching userspace packages after install or update.
+
+
+
+
+ Driver Actions
+
+
+
+
+ Use guided actions to install, switch or remove the current stack.
+
+
+
+
+ Source:
+
+
+
+
+
+
+ Idle
+
+
+
+
+ Phase:
+
+
+
+
+ Running
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Installer
+
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+
+ Remove Driver
+
+
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+ Search the online package catalog, then download and install a matching driver build.
+
+
+
+
+ Remote Driver Available
+
+
+
+
+ Catalog Not Ready
+
+
+
+
+
+
+
+
+
+
+ Updater
+
+
+
+
+ Searching the online NVIDIA package catalog...
+
+
+
+
+ Install Latest
+
+
+
+
+ Updating NVIDIA driver to the latest online version...
+
+
+
+
+ Downloading and installing the latest online NVIDIA driver...
+
+
+
+
+ Switching NVIDIA driver to selected online version:
+
+
+
+
+ Downloading and installing selected NVIDIA driver version:
+
+
+
+
+ Online repository versions loaded:
+
+
+
+
+ No online repository version list has been loaded yet.
+
+
+
+
+ Apply Latest
+
+
+
+
+ Fallback Open Driver Active
+
+
+
+
+ Fallback Open Driver Inactive
+
+
+
+
+ Elapsed:
+
+
+
+
+ Last Log:
+
+
+
+
+ I reviewed the NVIDIA license terms
+
+
+
+
+ Official NVIDIA license: <a href="%1">%1</a>
+
+
+
+
+ Install NVIDIA Driver
+ Instalar controlador NVIDIA
+
+
+
+ Install Open Kernel Modules
+ Instalar modulos de kernel abiertos
+
+
+
+ Switching to NVIDIA open kernel modules...
+
+
+
+
+ Apply Selected
+ Aplicar seleccionada
+
+
+
+ Activity Log
+ Registro de actividad
+
+
+
+ Operation output is streamed here in real time.
+
+
+
+
+ Clear Log
+ Limpiar registro
+
+
+ Session type:
+ Session type:
+
+
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+ For Wayland sessions, nvidia-drm.modeset=1 is applied automatically.
+
+
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+ For X11 sessions, the xorg-x11-drv-nvidia package is verified and installed.
+
+
+ I accept the license / agreement terms
+ I accept the license / agreement terms
+
+
+ Install Proprietary Driver
+ Install Proprietary Driver
+
+
+ Install Open-Source Driver (Nouveau)
+ Install Open-Source Driver (Nouveau)
+
+
+
+ Deep Clean
+ Deep Clean
+
+
+
+ Rescan System
+
+
+
+
+ Update Center
+
+
+
+
+ Installed:
+
+
+
+
+ Update Available
+
+
+
+
+ Up to Date
+
+
+
+
+ Check for Updates
+ Check for Updates
+
+
+ Apply Update
+ Apply Update
+
+
+ Latest version:
+ Latest version:
+
+
+ Rescan
+ Rescan
+
+
+ Installed NVIDIA version:
+ Installed NVIDIA version:
+
+
+
+ Main
+
+
+ ro-Control
+ ro-Control
+
+
+
+ Driver Control Center
+ Centro de Controladores
+
+
+
+ System Monitor
+ Monitor del Sistema
+
+
+
+ Preferences
+ Preferencias
+
+
+
+ Install, verify and update NVIDIA drivers with guided system checks.
+
+
+
+
+ Track live CPU, GPU and memory telemetry in one place.
+
+
+
+
+ Tune the interface and review diagnostic context before support work.
+
+
+
+
+ Follow System
+
+
+
+
+ Dark Theme
+
+
+
+
+ Light Theme
+
+
+
+ System Dark
+ System Dark
+
+
+ System Light
+ System Light
+
+
+
+ Compact Layout
+
+
+
+
+ Comfort Layout
+
+
+
+ Theme: System (Dark)
+ Theme: System (Dark)
+
+
+ Theme: System (Light)
+ Theme: System (Light)
+
+
+ Driver
+ Driver
+
+
+ Monitor
+ Monitor
+
+
+ Settings
+ Settings
+
+
+
+ MonitorPage
+
+ System Monitoring
+ System Monitoring
+
+
+
+ CPU Load
+
+
+
+
+
+
+
+
+ Unavailable
+
+
+
+
+ CPU telemetry is currently unavailable.
+
+
+
+
+ GPU Load
+
+
+
+
+ Memory Usage
+
+
+
+
+ Used:
+
+
+
+
+ RAM telemetry is currently unavailable.
+
+
+
+
+ Live Resource Curves
+
+
+
+
+ Quick pulse view for the most important machine resources.
+
+
+
+
+ CPU
+ CPU
+
+
+
+ GPU
+
+
+
+
+ Health Summary
+
+
+
+
+ Fast interpretation of the raw telemetry values.
+
+
+
+
+ CPU Busy
+
+
+
+
+ CPU Stable
+
+
+
+
+ GPU Online
+
+
+
+
+ GPU Telemetry Missing
+
+
+
+
+ Memory Pressure
+
+
+
+
+ Memory Stable
+
+
+
+
+ GPU temperature:
+
+
+
+
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.
+
+
+
+
+ Detailed Signals
+
+
+
+
+ Expanded raw values for support and diagnostics.
+
+
+
+
+ CPU Temperature
+
+
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ GPU Temperature
+
+
+
+
+ No Live Data
+
+
+
+
+ No NVIDIA GPU was detected on this system.
+
+
+
+
+ GPU detected, but no active driver is loaded.
+
+
+
+
+ Live GPU telemetry is unavailable. Check nvidia-smi and driver access.
+
+
+
+
+ , VRAM
+
+
+
+
+ No NVIDIA GPU is currently detected on this system.
+
+
+
+
+ GPU telemetry is unavailable because the NVIDIA driver is not active.
+
+
+
+
+ VRAM
+
+
+
+
+ RAM Footprint
+
+
+
+
+ Actions
+
+
+
+
+ Trigger a manual refresh when you need a fresh sample.
+
+
+
+
+ Refresh Telemetry
+
+
+
+
+ NVIDIA Path OK
+
+
+
+
+ Check NVIDIA Path
+
+
+
+ Usage:
+ Usage:
+
+
+ CPU data unavailable
+ CPU data unavailable
+
+
+
+ Temperature:
+ Temperature:
+
+
+ GPU (NVIDIA)
+ GPU (NVIDIA)
+
+
+
+ NVIDIA GPU
+ NVIDIA GPU
+
+
+ Failed to read data via nvidia-smi
+ Failed to read data via nvidia-smi
+
+
+ Load:
+ Load:
+
+
+ VRAM:
+ VRAM:
+
+
+
+ RAM
+ RAM
+
+
+ RAM data unavailable
+ RAM data unavailable
+
+
+ Refresh
+ Refresh
+
+
+
+ Refresh interval:
+ Refresh interval:
+
+
+
+ NvidiaDetector
+
+ Proprietary (NVIDIA)
+ Proprietary (NVIDIA)
+
+
+ Open Source (Nouveau)
+ Open Source (Nouveau)
+
+
+
+ Not Installed / Unknown
+ Not Installed / Unknown
+
+
+
+
+ None
+ None
+
+
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+NVIDIA Module: %5
+Nouveau: %6
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+NVIDIA Module: %5
+Nouveau: %6
+
+
+
+ NVIDIA Open Kernel Modules
+
+
+
+
+ NVIDIA Driver
+
+
+
+
+ Fallback Open Driver
+
+
+
+
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+Active Stack: %5
+Fallback Open Driver: %6
+
+
+
+
+ Enabled
+ Enabled
+
+
+
+ Disabled
+
+
+
+
+ Disabled / Unknown
+ Disabled / Unknown
+
+
+
+ Unknown
+ Unknown
+
+
+ Loaded
+ Loaded
+
+
+ Not loaded
+ Not loaded
+
+
+
+ Active
+ Active
+
+
+
+ Inactive
+ Inactive
+
+
+
+ NvidiaInstaller
+
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+ You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
+
+
+ License agreement acceptance is required before installation.
+ License agreement acceptance is required before installation.
+
+
+
+ Starting command (attempt %1): %2
+
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+
+
+
+
+ The proprietary NVIDIA driver is subject to NVIDIA's software license. Review the official NVIDIA license before installation: %1
+
+
+
+
+ NVIDIA license review confirmation is required before installation.
+
+
+
+
+ Checking RPM Fusion repositories...
+ Checking RPM Fusion repositories...
+
+
+
+ Platform version could not be detected.
+ Platform version could not be detected.
+
+
+
+ Failed to enable RPM Fusion repositories:
+ Failed to enable RPM Fusion repositories:
+
+
+
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...
+
+
+
+ Installation failed:
+ Installation failed:
+
+
+
+
+ Building the kernel module (akmods --force)...
+ Building the kernel module (akmods --force)...
+
+
+
+
+ Kernel module build failed:
+
+
+
+
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.
+
+
+
+ Switching to NVIDIA open kernel modules...
+
+
+
+
+ Failed to remove conflicting NVIDIA kernel packages:
+
+
+
+
+ Open NVIDIA kernel module installation failed:
+
+
+
+
+ unknown error
+
+
+
+
+ NVIDIA open kernel modules were installed successfully. Please restart the system.
+
+
+
+ Switching to the open-source driver...
+ Switching to the open-source driver...
+
+
+ Failed to remove proprietary packages:
+ Failed to remove proprietary packages:
+
+
+ Open-source driver installation failed:
+ Open-source driver installation failed:
+
+
+ The open-source driver (Nouveau) was installed. Please restart the system.
+ The open-source driver (Nouveau) was installed. Please restart the system.
+
+
+
+ Removing the NVIDIA driver...
+ Removing the NVIDIA driver...
+
+
+
+ Driver removed successfully.
+ Driver removed successfully.
+
+
+
+ Removal failed:
+ Removal failed:
+
+
+
+ Cleaning legacy driver leftovers...
+ Cleaning legacy driver leftovers...
+
+
+
+ Deep clean failed:
+
+
+
+
+ DNF cache cleanup failed:
+
+
+
+
+ Deep clean completed.
+ Deep clean completed.
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Another driver operation is already running.
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Legacy NVIDIA cleanup completed.
+
+
+
+
+ Failed to apply the Wayland kernel parameter:
+ Failed to apply the Wayland kernel parameter:
+
+
+
+ X11 detected: checking NVIDIA userspace packages...
+ X11 detected: checking NVIDIA userspace packages...
+
+
+
+ Failed to install the X11 NVIDIA package:
+ Failed to install the X11 NVIDIA package:
+
+
+
+ NvidiaUpdater
+
+ Updating the NVIDIA driver...
+ Updating the NVIDIA driver...
+
+
+
+ Update failed:
+ Update failed:
+
+
+ Rebuilding the kernel module...
+ Rebuilding the kernel module...
+
+
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+ Wayland detected: refreshing nvidia-drm.modeset=1...
+
+
+
+ Driver updated successfully. Please restart the system.
+ Driver updated successfully. Please restart the system.
+
+
+
+
+ dnf not found.
+
+
+
+
+ Online NVIDIA packages were found. You can download and install the driver now.
+
+
+
+
+ Online NVIDIA driver found. Latest remote version: %1
+
+
+
+
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.
+
+
+
+
+ Update found (version details unavailable).
+
+
+
+
+ Update found: %1
+
+
+
+
+ Driver is up to date. No new version found.
+
+
+
+
+ Update check failed: %1
+
+
+
+
+ Starting command (attempt %1): %2
+
+
+
+
+ Command finished (attempt %1, exit %2, %3 ms): %4
+
+
+
+
+ Another driver operation is already running.
+
+
+
+
+ Kernel module build failed:
+
+
+
+
+
+
+ unknown error
+
+
+
+
+ Wayland detected: applying nvidia-drm.modeset=1...
+ Wayland detected: applying nvidia-drm.modeset=1...
+
+
+
+ Failed to update the Wayland kernel parameter:
+
+
+
+
+ No available versions found.
+
+
+
+
+ Available versions: %1
+
+
+
+
+ Starting update check...
+
+
+
+
+ Selected version not found in the repository.
+
+
+
+
+ Updating NVIDIA driver to the latest version...
+
+
+
+
+ Switching NVIDIA driver to selected version: %1
+
+
+
+
+ Driver is already at the latest available version.
+
+
+
+
+ Selected driver version is already installed.
+
+
+
+
+ Rebuilding kernel module...
+
+
+
+
+ Latest version installed successfully. Please restart the system.
+
+
+
+
+ Selected version applied successfully. Please restart the system.
+
+
+
+
+ SettingsPage
+
+ Settings
+ Settings
+
+
+
+ Language
+
+
+
+
+ Changes the application language immediately and keeps the selection for the next launch.
+
+
+
+
+ Compact layout
+
+
+
+
+ Reduces spacing to fit more information on screen.
+
+
+
+
+ Show advanced diagnostics
+
+
+
+
+ Shows verification reports and expanded monitor metrics.
+
+
+
+
+ Language:
+
+
+
+
+ Appearance & Behavior
+ Apariencia y comportamiento
+
+
+
+ Control theme, density and operator-focused interface behavior.
+
+
+
+
+ Theme mode
+ Modo de tema
+
+
+
+ Choose whether the application follows the OS theme or uses an explicit light or dark shell.
+
+
+
+
+ Theme: Follow System
+
+
+
+
+ Theme: Dark
+
+
+
+
+ Theme: Light
+
+
+
+
+ Compact Active
+
+
+
+
+ Comfort Active
+
+
+
+
+ Advanced Visible
+
+
+
+
+ Advanced Hidden
+
+
+
+
+ Restore the recommended interface defaults if the shell starts to feel cluttered.
+
+
+
+
+ Reset Interface Defaults
+
+
+
+
+ Diagnostics
+
+
+
+
+ Useful runtime context before filing issues or performing support work.
+
+
+
+
+
+ Application
+
+
+
+
+ GPU
+
+
+
+
+ Not detected
+ Not detected
+
+
+
+ Driver
+ Driver
+
+
+
+ Session
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ Use the Driver page to refresh detection before copying any diagnostic context.
+
+
+
+
+ Workflow Guidance
+
+
+
+
+ Recommended order of operations when changing drivers.
+
+
+
+
+ 1. Verify GPU detection and session type.
+2. Install or switch the driver stack.
+3. Check repository updates.
+4. Restart after successful package operations.
+
+
+
+
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.
+
+
+
+
+ No Secure Boot blocker is currently reported by the detector.
+
+
+
+
+ About
+ About
+
+
+
+ Project identity and current shell mode.
+
+
+
+
+ Theme
+
+
+
+
+ Follow System
+ Seguir al sistema
+
+
+
+ Dark
+ Oscuro
+
+
+
+ Light
+ Claro
+
+
+
+ Effective language
+
+
+
+
+ Layout density
+
+
+
+
+ Advanced diagnostics
+
+
+
+ Application:
+ Application:
+
+
+ Theme:
+ Theme:
+
+
+ System Dark
+ System Dark
+
+
+ System Light
+ System Light
+
+
+
+ Compact
+
+
+
+
+ Comfort
+
+
+
+
+ Visible
+
+
+
+
+ Hidden
+
+
+
+
+ SidebarMenu
+
+
+ Driver Management
+ Gestion de Controladores
+
+
+
+ System Monitoring
+ Supervision del Sistema
+
+
+
+ Settings
+ Ajustes
+
+
+
+ ro-Control
+ ro-Control
+
+
+
diff --git a/i18n/ro-control_tr.ts b/i18n/ro-control_tr.ts
index 15e9c42..e96400f 100644
--- a/i18n/ro-control_tr.ts
+++ b/i18n/ro-control_tr.ts
@@ -24,10 +24,8 @@
Sürücü sürümü:
-
-
-
-
+
+ NoneYok
@@ -36,101 +34,120 @@
Secure Boot:
-
-
+ Repository SetupDepo Kurulumu
-
-
+ Package TransactionPaket İşlemi
-
-
+ Kernel IntegrationKernel Entegrasyonu
-
-
+ Session FinalizationOturum Sonlandırma
-
-
+ Update CheckGüncelleme Kontrolü
-
-
+ GeneralGenel
-
-
+ InstalledKurulu
-
-
+ LatestEn Güncel
-
-
+ GPU DetectionGPU Tespiti
-
-
+ DetectedTespit Edildi
-
-
+ MissingBulunamadı
-
-
+ No NVIDIA GPU was detected on this system.Bu sistemde NVIDIA GPU tespit edilemedi.
-
-
+ Active DriverAktif Sürücü
-
-
+ Session: Oturum:
-
-
+ UnknownBilinmiyor
-
-
+ Installed VersionKurulu Sürüm
+
+
+ Fallback Open Driver Active
+ Yedek Acik Surucu Etkin
+
+
+
+ Fallback Open Driver Inactive
+ Yedek Acik Surucu Pasif
+
+
+
+ I reviewed the NVIDIA license terms
+ NVIDIA lisans kosullarini inceledim
+
+
+
+ Official NVIDIA license: <a href="%1">%1</a>
+ Resmi NVIDIA lisansi: <a href="%1">%1</a>
+
+
+
+ Install NVIDIA Driver
+ NVIDIA Surucusunu Kur
+
+
+
+ Install Open Kernel Modules
+ Acik Kernel Modullerini Kur
+
+
+
+ Switching to NVIDIA open kernel modules...
+ NVIDIA acik kernel modullerine geciliyor...
+ Latest available: En son sürüm:
@@ -140,336 +157,263 @@
Bekleyen paket güncellemesi bulunamadı.
-
-
+ Latest available online: Çevrimiçi bulunan en güncel sürüm:
-
-
+ No pending online package update detected.Bekleyen çevrimiçi paket güncellemesi tespit edilmedi.
-
-
+ Latest driver found online: Çevrimiçi bulunan en güncel sürücü:
-
-
+ No online driver catalog has been loaded yet.Henüz çevrimiçi sürücü kataloğu yüklenmedi.
-
-
+ Secure BootGüvenli Önyükleme (Secure Boot)
-
-
+ EnabledAçık
-
-
+ Disabled / UnknownKapalı / Bilinmiyor
-
-
+ Unsigned kernel modules may require manual signing.İmzasız kernel modüllerinin manuel olarak imzalanması gerekebilir.
-
-
+ No Secure Boot blocker is currently detected.Şu anda herhangi bir Secure Boot engeli tespit edilmedi.
-
-
+ VerificationDoğrulama
-
-
+ Review driver prerequisites before changing packages.Paketleri değiştirmeden önce sürücü ön koşullarını gözden geçirin.
-
-
+ GPU ReadyGPU Hazır
-
-
+ GPU MissingGPU Bulunamadı
-
-
+ Wayland SessionWayland Oturumu
-
-
+ X11 / Other SessionX11 / Diğer Oturum
-
- Nouveau Active
- Nouveau Aktif
+ Nouveau Aktif
-
- Nouveau Inactive
- Nouveau Pasif
+ Nouveau Pasif
-
-
+ Kernel Module LoadedKernel Modülü Yüklü
-
-
+ Kernel Module MissingKernel Modülü Eksik
-
-
+ Wayland sessions automatically need the nvidia-drm.modeset=1 kernel argument.Wayland oturumları otomatik olarak nvidia-drm.modeset=1 çekirdek argümanına ihtiyaç duyar.
-
-
+ X11 sessions require matching userspace packages after install or update.X11 oturumları, kurulum veya güncelleme sonrası eşleşen kullanıcı alanı paketleri gerektirir.
-
-
+ Driver ActionsSürücü İşlemleri
-
-
+ Use guided actions to install, switch or remove the current stack.Mevcut yığını kurmak, değiştirmek veya kaldırmak için rehberli işlemleri kullanın.
-
-
+ Source: Kaynak:
-
-
-
-
-
-
+
+
+ IdleBoşta
-
-
+ Phase: Aşama:
-
-
+ RunningÇalışıyor
-
-
+ Elapsed: Geçen Süre:
-
-
+ Last Log: Son Günlük:
-
- I accept the detected license / agreement terms
- Tespit edilen lisans / sözleşme koşullarını kabul ediyorum
+ Tespit edilen lisans / sözleşme koşullarını kabul ediyorum
-
- Install Proprietary
- Sahipli Sürücüyü Kur
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Sahipli Sürücüyü Kur
+
+
+
+
+
+
+
+
+
+
+
+ InstallerKurucu
-
-
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
- Install Nouveau
- Nouveau Kur (Açık Kaynak)
+ Nouveau Kur (Açık Kaynak)
-
- Switching to the open-source driver...
- Açık kaynak sürücüye geçiliyor...
+ Açık kaynak sürücüye geçiliyor...
-
-
+ Remove DriverSürücüyü Kaldır
-
-
+ Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
-
-
+ Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
-
-
+ Search the online package catalog, then download and install a matching driver build.Çevrimiçi paket kataloğunu tarayın, ardından uygun sürücü derlemesini indirip kurun.
-
-
+ Remote Driver AvailableUzak Sürücü Mevcut
-
-
+ Catalog Not ReadyKatalog Hazır Değil
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ UpdaterGüncelleyici
-
-
+ Searching the online NVIDIA package catalog...Çevrimiçi NVIDIA paket kataloğu aranıyor...
-
-
+ Install LatestEn Günceli Kur
-
-
+ Updating NVIDIA driver to the latest online version...NVIDIA sürücüsü çevrimiçi en güncel sürüme güncelleniyor...
-
-
+ Downloading and installing the latest online NVIDIA driver...En güncel çevrimiçi NVIDIA sürücüsü indiriliyor ve kuruluyor...
-
-
+ Switching NVIDIA driver to selected online version: NVIDIA sürücüsü seçilen çevrimiçi sürüme geçiriliyor:
-
-
+ Downloading and installing selected NVIDIA driver version: Seçilen NVIDIA sürücü sürümü indiriliyor ve kuruluyor:
-
-
+ Online repository versions loaded: Çevrimiçi depo sürümleri yüklendi:
-
-
+ No online repository version list has been loaded yet.Henüz çevrimiçi depo sürüm listesi yüklenmedi.
@@ -478,8 +422,7 @@
Güncelleme denetimi başlatılıyor...
-
-
+ Apply LatestEn Son Sürümü Uygula
@@ -488,8 +431,7 @@
NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
-
+ Apply SelectedSeçileni Uygula
@@ -502,20 +444,17 @@
Henüz hiçbir repo sürüm listesi yüklenmedi.
-
-
+ Activity LogAktivite Günlüğü
-
-
+ Operation output is streamed here in real time.İşlem çıktıları burada gerçek zamanlı olarak gösterilir.
-
-
+ Clear LogGünlüğü Temizle
@@ -544,20 +483,17 @@
Açık Kaynak Sürücüyü Kur (Nouveau)
-
-
+ Deep CleanDerin Temizlik
-
-
+ Rescan SystemSistemi Yeniden Tara
-
-
+ Update CenterGüncelleme Merkezi
@@ -566,26 +502,22 @@
Repo sürümünü kontrol edin ve gerektiğinde belirli bir yapıyı sabitleyin.
-
-
+ Installed: Kurulu:
-
-
+ Update AvailableGüncelleme Mevcut
-
-
+ Up to DateGüncel
-
-
+ Check for UpdatesGüncellemeleri Kontrol Et
@@ -609,68 +541,70 @@
Main
- ro-Controlro-Control
-
-
+ Driver Control CenterSürücü Kontrol Merkezi
-
-
+ System MonitorSistem İzleyici
-
-
+ PreferencesTercihler
-
-
+ Install, verify and update NVIDIA drivers with guided system checks.NVIDIA sürücülerini, rehberli sistem kontrolleriyle kurun, doğrulayın ve güncelleyin.
-
-
+ Track live CPU, GPU and memory telemetry in one place.Canlı CPU, GPU ve bellek telemetrisini tek bir yerden izleyin.
-
-
+ Tune the interface and review diagnostic context before support work.Arayüz yoğunluğunu ayarlayın ve destek çalışmalarından önce sistem raporunu gözden geçirin.
-
-
+
+ Follow System
+ Sistemi İzle
+
+
+
+ Dark Theme
+ Koyu Tema
+
+
+
+ Light Theme
+ Açık Tema
+
+ System Dark
- Sistem (Koyu)
+ Sistem (Koyu)
-
- System Light
- Sistem (Açık)
+ Sistem (Açık)
-
-
+ Compact LayoutSıkı Görünüm
-
-
+ Comfort LayoutRahat Görünüm
@@ -702,224 +636,212 @@
Sistem İzleme
-
-
+ CPU LoadCPU Yükü
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ UnavailableKullanılamıyor
-
-
+ CPU telemetry is currently unavailable.CPU telemetrisi şu anda kullanılamıyor.
-
-
+ GPU LoadGPU Yükü
-
- nvidia-smi did not return live GPU telemetry.
- nvidia-smi canlı GPU telemetrisi döndürmedi.
+ nvidia-smi canlı GPU telemetrisi döndürmedi.
-
-
+ Memory UsageBellek Kullanımı
-
-
+ Used: Kullanılan:
-
-
+ RAM telemetry is currently unavailable.RAM telemetrisi şu anda kullanılamıyor.
-
-
+ Live Resource CurvesCanlı Kaynak Grafikleri
-
-
+ Quick pulse view for the most important machine resources.En önemli makine kaynakları için hızlı anlık görünüm.
-
-
+ CPUCPU
-
-
+ GPUEkran Kartı (GPU)
-
-
+ Health SummarySağlık Özeti
-
-
+ Fast interpretation of the raw telemetry values.Ham telemetri değerlerinin hızlı yorumlaması.
-
-
+ CPU BusyCPU Meşgul
-
-
+ CPU StableCPU Stabil
-
-
+ GPU OnlineGPU Çevrimiçi
-
-
+ GPU Telemetry MissingGPU Telemetrisi Bulunamadı
-
-
+ Memory PressureBellek Baskısı
-
-
+ Memory StableBellek Stabil
-
-
+ GPU temperature: GPU sıcaklığı:
-
-
+ , VRAM , VRAM
-
-
+ GPU metrics are unavailable. Check driver installation and nvidia-smi accessibility.GPU metrikleri kullanılamıyor. Sürücü kurulumunu ve nvidia-smi erişilebilirliğini kontrol edin.
-
-
+ Detailed SignalsDetaylı Sinyaller
-
-
+ Expanded raw values for support and diagnostics.Destek ve tanılama için genişletilmiş ham değerler.
-
-
+ CPU TemperatureCPU Sıcaklığı
-
-
-
-
-
-
+
+
+ UnknownBilinmiyor
-
-
+ GPU TemperatureGPU Sıcaklığı
-
-
+
+ No Live Data
+ Canlı Veri Yok
+
+
+
+ No NVIDIA GPU was detected on this system.
+ Bu sistemde NVIDIA GPU tespit edilemedi.
+
+
+
+ GPU detected, but no active driver is loaded.
+ GPU tespit edildi ancak etkin bir sürücü yüklenmedi.
+
+
+
+ Live GPU telemetry is unavailable. Check nvidia-smi and driver access.
+ Canlı GPU telemetrisi kullanılamıyor. nvidia-smi ve sürücü erişimini kontrol edin.
+
+
+
+ No NVIDIA GPU is currently detected on this system.
+ Bu sistemde şu anda herhangi bir NVIDIA GPU tespit edilmiyor.
+
+
+
+ GPU telemetry is unavailable because the NVIDIA driver is not active.
+ NVIDIA sürücüsü etkin olmadığı için GPU telemetrisi kullanılamıyor.
+
+
+ VRAMVRAM Belleği
-
-
+ RAM FootprintRAM Kapladığı Alan
-
-
+ Actionsİşlemler
-
-
+ Trigger a manual refresh when you need a fresh sample.Taze bir örneğe ihtiyacınız olduğunda manuel yenilemeyi tetikleyin.
-
-
+ Refresh TelemetryTelemetriyi Yenile
-
-
+ NVIDIA Path OKNVIDIA Yolu Tamam
-
-
+ Check NVIDIA PathNVIDIA Yolunu Kontrol Et
@@ -932,8 +854,7 @@
CPU verisi alınamıyor
-
-
+ Temperature: Sıcaklık:
@@ -942,8 +863,7 @@
GPU (NVIDIA)
-
-
+ NVIDIA GPUNVIDIA GPU
@@ -960,8 +880,7 @@
VRAM:
-
-
+ RAMRAM
@@ -974,8 +893,7 @@
Yenile
-
-
+ Refresh interval: Yenileme aralığı:
@@ -983,35 +901,32 @@
NvidiaDetector
- Proprietary (NVIDIA)
- Kapalı Kaynak (NVIDIA)
+ Kapalı Kaynak (NVIDIA)
- Open Source (Nouveau)
- Açık Kaynak (Nouveau)
+ Açık Kaynak (Nouveau)
-
+ Not Installed / UnknownKurulu Değil / Bilinmiyor
-
-
+
+ NoneYok
- GPU: %1
Driver Version: %2
Secure Boot: %3
Session: %4
NVIDIA Module: %5
Nouveau: %6
- GPU: %1
+ GPU: %1
Sürücü Sürümü: %2
Secure Boot: %3
Oturum: %4
@@ -1019,42 +934,70 @@ NVIDIA Modülü: %5
Nouveau: %6
-
+
+ NVIDIA Open Kernel Modules
+ NVIDIA Acik Kernel Modulleri
+
+
+
+ NVIDIA Driver
+ NVIDIA Surucusu
+
+
+
+ Fallback Open Driver
+ Yedek Acik Surucu
+
+
+
+ GPU: %1
+Driver Version: %2
+Secure Boot: %3
+Session: %4
+Active Stack: %5
+Fallback Open Driver: %6
+ GPU: %1
+Surucu Surumu: %2
+Secure Boot: %3
+Oturum: %4
+Etkin Yigin: %5
+Yedek Acik Surucu: %6
+
+
+ EnabledEtkin
-
+ DisabledDevre Dışı
-
+ Disabled / UnknownDevre Dışı / Bilinmiyor
-
+ UnknownBilinmiyor
- Loaded
- Yüklü
+ Yüklü
- Not loaded
- Yüklü değil
+ Yüklü değil
-
+ ActiveAktif
-
+ InactiveAktif değil
@@ -1062,117 +1005,148 @@ Nouveau: %6
NvidiaInstaller
-
+ Starting command (attempt %1): %2Komut başlatılıyor (deneme %1): %2
-
+ Command finished (attempt %1, exit %2, %3 ms): %4Komut tamamlandı (deneme %1, çıkış %2, %3 ms): %4
- You must accept the NVIDIA proprietary driver license terms before installation. Detected license: %1
- Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
+ Kurulumdan önce NVIDIA kapalı kaynak sürücü lisans koşullarını kabul etmeniz gerekir. Tespit edilen lisans: %1
- License agreement acceptance is required before installation.
- Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
+ Kurulumdan önce lisans sözleşmesinin kabul edilmesi gerekir.
-
+
+ The proprietary NVIDIA driver is subject to NVIDIA's software license. Review the official NVIDIA license before installation: %1
+ Kapali kaynak NVIDIA surucusu, NVIDIA yazilim lisansina tabidir. Kurulumdan once resmi NVIDIA lisansini inceleyin: %1
+
+
+
+ NVIDIA license review confirmation is required before installation.
+ Kurulumdan once NVIDIA lisansinin incelendiginin onaylanmasi gerekir.
+
+
+ Checking RPM Fusion repositories...RPM Fusion depoları kontrol ediliyor...
-
+ Platform version could not be detected.Platform sürümü tespit edilemedi.
-
+ Failed to enable RPM Fusion repositories: RPM Fusion depoları etkinleştirilemedi:
-
+ Installing the proprietary NVIDIA driver (akmod-nvidia)...Kapalı kaynak NVIDIA sürücüsü kuruluyor (akmod-nvidia)...
-
+ Installation failed: Kurulum başarısız:
-
+
+ Building the kernel module (akmods --force)...Kernel modülü derleniyor (akmods --force)...
-
+
+ Kernel module build failed: Kernel modülü inşası başarısız:
-
+ The proprietary NVIDIA driver was installed successfully. Please restart the system.Kapalı kaynak NVIDIA sürücüsü başarıyla kuruldu. Lütfen sistemi yeniden başlatın.
-
+
+ Switching to NVIDIA open kernel modules...
+ NVIDIA acik kernel modullerine geciliyor...
+
+
+
+ Failed to remove conflicting NVIDIA kernel packages:
+ Cakisan NVIDIA kernel paketleri kaldirilamadi:
+
+
+
+ Open NVIDIA kernel module installation failed:
+ Acik NVIDIA kernel modulu kurulumu basarisiz:
+
+
+
+ unknown error
+ bilinmeyen hata
+
+
+
+ NVIDIA open kernel modules were installed successfully. Please restart the system.
+ NVIDIA acik kernel modulleri basariyla kuruldu. Lutfen sistemi yeniden baslatin.
+
+ Switching to the open-source driver...
- Açık kaynak sürücüye geçiliyor...
+ Açık kaynak sürücüye geçiliyor...
- Failed to remove proprietary packages:
- Kapalı kaynak paketler kaldırılamadı:
+ Kapalı kaynak paketler kaldırılamadı:
- Open-source driver installation failed:
- Açık kaynak sürücü kurulumu başarısız:
+ Açık kaynak sürücü kurulumu başarısız:
- The open-source driver (Nouveau) was installed. Please restart the system.
- Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
+ Açık kaynak sürücü (Nouveau) kuruldu. Lütfen sistemi yeniden başlatın.
-
+ Removing the NVIDIA driver...NVIDIA sürücüsü kaldırılıyor...
-
+ Driver removed successfully.Sürücü başarıyla kaldırıldı.
-
+ Removal failed: Kaldırma başarısız:
-
+ Cleaning legacy driver leftovers...Eski sürücü kalıntıları temizleniyor...
-
+ Deep clean failed: Derin temizlik başarısız:
-
+ DNF cache cleanup failed: DNF önbellek temizliği başarısız:
-
+ Deep clean completed.Derin temizlik tamamlandı.
@@ -1181,32 +1155,31 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 uygulanıyor...
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
- Unknown
- Bilinmiyor
+ Bilinmiyor
-
+ Legacy NVIDIA cleanup completed.Eski NVIDIA temizliği tamamlandı.
-
+ Failed to apply the Wayland kernel parameter: Wayland kernel parametresi uygulanamadı:
-
+ X11 detected: checking NVIDIA userspace packages...X11 tespit edildi: NVIDIA userspace paketleri kontrol ediliyor...
-
+ Failed to install the X11 NVIDIA package: X11 NVIDIA paketi kurulamadı:
@@ -1218,7 +1191,7 @@ Nouveau: %6
NVIDIA sürücüsü güncelleniyor...
-
+ Update failed: Güncelleme başarısız:
@@ -1231,13 +1204,13 @@ Nouveau: %6
Wayland tespit edildi: nvidia-drm.modeset=1 yenileniyor...
-
+ Driver updated successfully. Please restart the system.Sürücü başarıyla güncellendi. Lütfen sistemi yeniden başlatın.
-
-
+
+ dnf not found.dnf bulunamadı.
@@ -1246,129 +1219,129 @@ Nouveau: %6
Sistemde kurulu bir NVIDIA sürücüsü bulunamadı.
-
+ Online NVIDIA packages were found. You can download and install the driver now.Çevrimiçi NVIDIA paketleri bulundu. Sürücüyü şimdi indirip kurabilirsiniz.
-
+ Online NVIDIA driver found. Latest remote version: %1Çevrimiçi NVIDIA sürücüsü bulundu. En güncel uzak sürüm: %1
-
+ No online NVIDIA package catalog was found. RPM Fusion may not be configured yet.Çevrimiçi NVIDIA paket kataloğu bulunamadı. RPM Fusion henüz yapılandırılmamış olabilir.
-
+ Update found (version details unavailable).Güncelleme bulundu (sürüm detayları kullanılamıyor).
-
+ Update found: %1Güncelleme bulundu: %1
-
+ Driver is up to date. No new version found.Sürücü güncel. Yeni sürüm bulunamadı.
-
+ Update check failed: %1Güncelleme kontrolü başarısız: %1
-
+ Starting command (attempt %1): %2Komut başlatılıyor (deneme %1): %2
-
+ Command finished (attempt %1, exit %2, %3 ms): %4Komut tamamlandı (deneme %1, çıkış %2, %3 ms): %4
-
+ Another driver operation is already running.Başka bir sürücü işlemi şu an yürütülüyor.
-
+ Kernel module build failed: Kernel modülü inşası başarısız:
-
-
-
+
+
+ unknown errorbilinmeyen hata
-
+ Wayland detected: applying nvidia-drm.modeset=1...Wayland algılandı: nvidia-drm.modeset=1 uygulanıyor...
-
+ Failed to update the Wayland kernel parameter: Wayland çekirdek parametresi güncellenemedi:
-
+ No available versions found.Hiçbir uygun sürüm bulunamadı.
-
+ Available versions: %1Mevcut sürümler: %1
-
+ Starting update check...Güncelleme denetimi başlatılıyor...
-
+ Selected version not found in the repository.Seçili sürüm depoda bulunamadı.
-
+ Updating NVIDIA driver to the latest version...NVIDIA sürücüsü en yeni sürüme güncelleniyor...
-
+ Switching NVIDIA driver to selected version: %1NVIDIA sürücüsü seçili sürüme değiştiriliyor: %1
-
+ Driver is already at the latest available version.Sürücü zaten mevcut en güncel sürümde.
-
+ Selected driver version is already installed.Seçilen sürücü sürümü zaten kurulu.
-
+ Rebuilding kernel module...Kernel modülü yeniden derleniyor...
-
+ Latest version installed successfully. Please restart the system.En son sürüm başarıyla yüklendi. Lütfen sistemi yeniden başlatın.
-
+ Selected version applied successfully. Please restart the system.Seçili sürüm başarıyla uygulandı. Lütfen sistemi yeniden başlatın.
@@ -1380,152 +1353,179 @@ Nouveau: %6
Ayarlar
-
- Interface
- Arayüz
+ Arayüz
-
- Tune the shell density and how much operational detail the app exposes.
- Arayüz yoğunluğunu ve uygulamanın ne kadar operasyonel ayrıntı göstereceğini ayarlayın.
+ Arayüz yoğunluğunu ve uygulamanın ne kadar operasyonel ayrıntı göstereceğini ayarlayın.
-
-
+ LanguageDil
-
-
+ Changes the application language immediately and keeps the selection for the next launch.Uygulama dilini hemen değiştirir ve seçimi sonraki açılış için korur.
-
-
+ Compact layoutSıkı görünüm
-
-
+ Reduces spacing to fit more information on screen.Ekrana daha fazla bilgi sığdırmak için boşlukları azaltır.
-
-
+ Show advanced diagnosticsGelişmiş tanılamayı göster
-
-
+ Shows verification reports and expanded monitor metrics.Doğrulama raporlarını ve genişletilmiş monitör metriklerini gösterir.
-
-
+ Language: Dil:
-
- System Dark Theme
- Koyu Sistem Teması
+ Koyu Sistem Teması
-
- System Light Theme
- Açık Sistem Teması
+ Açık Sistem Teması
+
+
+
+ Appearance & Behavior
+ Görünüm ve Davranış
-
-
+
+ Control theme, density and operator-focused interface behavior.
+ Tema, yoğunluk ve operatör odaklı arayüz davranışını yönetin.
+
+
+
+ Theme mode
+ Tema modu
+
+
+
+ Choose whether the application follows the OS theme or uses an explicit light or dark shell.
+ Uygulamanın işletim sistemi temasını izlemesini veya açık ya da koyu kabuğu açıkça kullanmasını seçin.
+
+
+
+ Theme: Follow System
+ Tema: Sistemi İzle
+
+
+
+ Theme: Dark
+ Tema: Koyu
+
+
+
+ Theme: Light
+ Tema: Açık
+
+
+ Compact ActiveSıkı Düzen Etkin
-
-
+ Comfort ActiveRahat Düzen Etkin
-
-
+
+ Advanced Visible
+ Gelişmiş Görünür
+
+
+
+ Advanced Hidden
+ Gelişmiş Gizli
+
+
+
+ Restore the recommended interface defaults if the shell starts to feel cluttered.
+ Kabuk karmaşık hissettirmeye başlarsa önerilen arayüz varsayılanlarını geri yükleyin.
+
+
+
+ Reset Interface Defaults
+ Arayüz Varsayılanlarını Sıfırla
+
+
+ DiagnosticsTanılama
-
-
+ Useful runtime context before filing issues or performing support work.Hata bildirmeden veya destek çalışması yapmadan önce yararlı çalışma zamanı bilgisi.
-
-
+
+ ApplicationUygulama
-
-
+ GPUEkran Kartı (GPU)
-
-
+ Not detectedTespit edilmedi
-
-
+ DriverSürücü
-
-
+ SessionOturum
-
-
+ UnknownBilinmiyor
-
-
+ Use the Driver page to refresh detection before copying any diagnostic context.Tanılama bağlamını kopyalamadan önce algılamayı yenilemek için Sürücü (Driver) sayfasını kullanın.
-
-
+ Workflow Guidanceİş Akışı Rehberi
-
-
+ Recommended order of operations when changing drivers.Sürücü değiştirilirken önerilen işlem sırası.
-
-
+ 1. Verify GPU detection and session type.
2. Install or switch the driver stack.
3. Check repository updates.
@@ -1536,86 +1536,101 @@ Nouveau: %6
4. Başarılı paket işlemlerinden sonra yeniden başlatın.
-
-
+ Secure Boot is enabled. Kernel module signing may still be required after package installation.Güvenli Önyükleme etkin. Paket kurulumundan sonra kernel modülü imzalanması gerekebilir.
-
-
+ No Secure Boot blocker is currently reported by the detector.Şu anda dedektör tarafından bildirilen herhangi bir (Secure Boot) engeli bulunmuyor.
-
-
+ AboutHakkında
-
-
+ Project identity and current shell mode.Proje kimliği ve mevcut kabuk modu.
-
-
+
+ Theme
+ Tema
+
+
+
+ Follow System
+ Sistemi İzle
+
+
+
+ Dark
+ Koyu
+
+
+
+ Light
+ Açık
+
+
+
+ Effective language
+ Etkin dil
+
+
+
+ Layout density
+ Düzen yoğunluğu
+
+
+
+ Advanced diagnostics
+ Gelişmiş tanılama
+
+ Application:
- Uygulama:
+ Uygulama:
-
- Theme:
- Tema:
+ Tema:
-
- System Dark
- Sistem Koyu
+ Sistem Koyu
-
- System Light
- Sistem Açık
+ Sistem Açık
-
- Layout density:
- Düzen Görünümü:
+ Düzen Görünümü:
-
-
+ CompactSıkı
-
-
+ ComfortRahat
-
- Advanced diagnostics:
- Gelişmiş Tanılama:
+ Gelişmiş Tanılama:
-
-
+ VisibleGörünür
-
-
+ HiddenGizli
@@ -1623,26 +1638,22 @@ Nouveau: %6
SidebarMenu
- Driver ManagementSürücü Yönetimi
- System MonitoringSistem İzleme
- SettingsAyarlar
-
-
+ ro-Controlro-Control
diff --git a/src/backend/nvidia/detector.cpp b/src/backend/nvidia/detector.cpp
index c32a96b..f86f59f 100644
--- a/src/backend/nvidia/detector.cpp
+++ b/src/backend/nvidia/detector.cpp
@@ -19,6 +19,8 @@ NvidiaDetector::GpuInfo NvidiaDetector::detect() const {
info.driverVersion = detectDriverVersion();
info.driverLoaded = isModuleLoaded(QStringLiteral("nvidia"));
info.nouveauActive = isModuleLoaded(QStringLiteral("nouveau"));
+ info.openKernelModulesInstalled =
+ isPackageInstalled(QStringLiteral("akmod-nvidia-open"));
info.secureBootEnabled = detectSecureBoot(&info.secureBootKnown);
info.sessionType = SessionUtil::detectSessionType();
@@ -36,10 +38,14 @@ QString NvidiaDetector::installedDriverVersion() const {
}
QString NvidiaDetector::activeDriver() const {
- if (m_info.driverLoaded)
- return tr("Proprietary (NVIDIA)");
+ if (m_info.driverLoaded) {
+ if (m_info.openKernelModulesInstalled) {
+ return tr("NVIDIA Open Kernel Modules");
+ }
+ return tr("NVIDIA Driver");
+ }
if (m_info.nouveauActive)
- return tr("Open Source (Nouveau)");
+ return tr("Fallback Open Driver");
return tr("Not Installed / Unknown");
}
@@ -49,13 +55,13 @@ QString NvidiaDetector::verificationReport() const {
m_info.driverVersion.isEmpty() ? tr("None") : m_info.driverVersion;
return tr("GPU: %1\nDriver Version: %2\nSecure Boot: %3\nSession: %4\n"
- "NVIDIA Module: %5\nNouveau: %6")
+ "Active Stack: %5\nFallback Open Driver: %6")
.arg(gpuText, versionText,
m_info.secureBootKnown
? (m_info.secureBootEnabled ? tr("Enabled") : tr("Disabled"))
: tr("Disabled / Unknown"),
m_info.sessionType.isEmpty() ? tr("Unknown") : m_info.sessionType,
- m_info.driverLoaded ? tr("Loaded") : tr("Not loaded"),
+ activeDriver(),
m_info.nouveauActive ? tr("Active") : tr("Inactive"));
}
@@ -129,6 +135,17 @@ QString NvidiaDetector::detectDriverVersion() const {
return {};
}
+bool NvidiaDetector::isPackageInstalled(const QString &packageName) const {
+ if (!CapabilityProbe::isToolAvailable(QStringLiteral("rpm"))) {
+ return false;
+ }
+
+ CommandRunner runner;
+ const auto result =
+ runner.run(QStringLiteral("rpm"), {QStringLiteral("-q"), packageName});
+ return result.success();
+}
+
bool NvidiaDetector::isModuleLoaded(const QString &moduleName) const {
QFile modules(QStringLiteral("/proc/modules"));
if (!modules.open(QIODevice::ReadOnly | QIODevice::Text))
@@ -170,4 +187,3 @@ bool NvidiaDetector::detectSecureBoot(bool *known) const {
return false;
}
-
diff --git a/src/backend/nvidia/detector.h b/src/backend/nvidia/detector.h
index d0ffd97..c7cab19 100644
--- a/src/backend/nvidia/detector.h
+++ b/src/backend/nvidia/detector.h
@@ -28,6 +28,7 @@ class NvidiaDetector : public QObject {
QString vbiosVersion;
bool driverLoaded = false;
bool nouveauActive = false;
+ bool openKernelModulesInstalled = false;
bool secureBootEnabled = false;
bool secureBootKnown = false;
QString sessionType;
@@ -63,6 +64,7 @@ class NvidiaDetector : public QObject {
private:
QString detectGpuName() const;
QString detectDriverVersion() const;
+ bool isPackageInstalled(const QString &packageName) const;
bool isModuleLoaded(const QString &moduleName) const;
bool detectSecureBoot(bool *known = nullptr) const;
diff --git a/src/backend/nvidia/installer.cpp b/src/backend/nvidia/installer.cpp
index 85dfa23..c10a024 100644
--- a/src/backend/nvidia/installer.cpp
+++ b/src/backend/nvidia/installer.cpp
@@ -10,6 +10,46 @@
namespace {
+const QStringList kSharedNvidiaUserspacePackages = {
+ QStringLiteral("xorg-x11-drv-nvidia"),
+ QStringLiteral("xorg-x11-drv-nvidia-libs"),
+ QStringLiteral("xorg-x11-drv-nvidia-cuda"),
+ QStringLiteral("xorg-x11-drv-nvidia-cuda-libs"),
+ QStringLiteral("nvidia-modprobe"),
+ QStringLiteral("nvidia-persistenced"),
+ QStringLiteral("nvidia-settings"),
+};
+
+const QStringList kKernelPackageCleanupTargets = {
+ QStringLiteral("akmod-nvidia"),
+ QStringLiteral("akmod-nvidia-open"),
+ QStringLiteral("xorg-x11-drv-nvidia-kmodsrc"),
+};
+
+const char kNvidiaLicenseUrl[] =
+ "https://www.nvidia.com/en-us/drivers/nvidia-license/";
+
+QString commandError(const CommandRunner::Result &result,
+ const QString &fallback = QString()) {
+ const QString stderrText = result.stderr.trimmed();
+ if (!stderrText.isEmpty()) {
+ return stderrText;
+ }
+
+ const QString stdoutText = result.stdout.trimmed();
+ if (!stdoutText.isEmpty()) {
+ return stdoutText;
+ }
+
+ return fallback;
+}
+
+QStringList buildDriverInstallTargets(const QString &kernelPackageName) {
+ QStringList packages{kernelPackageName};
+ packages << kSharedNvidiaUserspacePackages;
+ return packages;
+}
+
void emitProgressAsync(const QPointer &guard,
const QString &message) {
QMetaObject::invokeMethod(
@@ -110,40 +150,11 @@ void NvidiaInstaller::setProprietaryAgreement(bool required,
}
void NvidiaInstaller::refreshProprietaryAgreement() {
- CommandRunner runner;
- const auto info =
- runner.run(QStringLiteral("dnf"),
- {QStringLiteral("info"), QStringLiteral("akmod-nvidia")});
-
- if (!info.success()) {
- setProprietaryAgreement(false, QString());
- return;
- }
-
- QString licenseLine;
- const QStringList lines = info.stdout.split(QLatin1Char('\n'));
- for (const QString &line : lines) {
- if (line.startsWith(QStringLiteral("License"), Qt::CaseInsensitive)) {
- licenseLine = line;
- break;
- }
- }
-
- const QString lowered = licenseLine.toLower();
- const bool requiresAgreement =
- lowered.contains(QStringLiteral("eula")) ||
- lowered.contains(QStringLiteral("proprietary")) ||
- lowered.contains(QStringLiteral("nvidia"));
-
- if (requiresAgreement) {
- setProprietaryAgreement(
- true, tr("You must accept the NVIDIA proprietary driver license terms "
- "before installation. Detected license: %1")
- .arg(licenseLine.isEmpty() ? tr("Unknown") : licenseLine));
- return;
- }
-
- setProprietaryAgreement(false, QString());
+ setProprietaryAgreement(
+ true,
+ tr("The proprietary NVIDIA driver is subject to NVIDIA's software "
+ "license. Review the official NVIDIA license before installation: %1")
+ .arg(QString::fromLatin1(kNvidiaLicenseUrl)));
}
void NvidiaInstaller::install() { installProprietary(false); }
@@ -153,7 +164,7 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
if (m_proprietaryAgreementRequired && !agreementAccepted) {
emit installFinished(false,
- tr("License agreement acceptance is required before "
+ tr("NVIDIA license review confirmation is required before "
"installation."));
return;
}
@@ -218,9 +229,12 @@ void NvidiaInstaller::installProprietary(bool agreementAccepted) {
guard, NvidiaInstaller::tr(
"Installing the proprietary NVIDIA driver (akmod-nvidia)..."));
- result = runner.runAsRoot(QStringLiteral("dnf"),
- {QStringLiteral("install"), QStringLiteral("-y"),
- QStringLiteral("akmod-nvidia")});
+ QStringList installArgs{QStringLiteral("install"), QStringLiteral("-y"),
+ QStringLiteral("--refresh"),
+ QStringLiteral("--best"),
+ QStringLiteral("--allowerasing")};
+ installArgs << buildDriverInstallTargets(QStringLiteral("akmod-nvidia"));
+ result = runner.runAsRoot(QStringLiteral("dnf"), installArgs);
if (!result.success()) {
const QString error =
@@ -294,19 +308,18 @@ void NvidiaInstaller::installOpenSource() {
CommandRunner runner;
attachRunnerLogging(runner, guard);
- emitProgressAsync(guard,
- NvidiaInstaller::tr("Switching to the open-source driver..."));
+ emitProgressAsync(
+ guard, NvidiaInstaller::tr("Switching to NVIDIA open kernel modules..."));
auto result = runner.runAsRoot(
QStringLiteral("dnf"),
- {QStringLiteral("remove"), QStringLiteral("-y"),
- QStringLiteral("akmod-nvidia"),
- QStringLiteral("xorg-x11-drv-nvidia*")});
+ QStringList{QStringLiteral("remove"), QStringLiteral("-y")} +
+ kKernelPackageCleanupTargets);
if (!result.success()) {
const QString error =
- NvidiaInstaller::tr("Failed to remove proprietary packages: ") +
- result.stderr.trimmed();
+ NvidiaInstaller::tr("Failed to remove conflicting NVIDIA kernel packages: ") +
+ commandError(result);
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -318,15 +331,38 @@ void NvidiaInstaller::installOpenSource() {
return;
}
- result = runner.runAsRoot(QStringLiteral("dnf"),
- {QStringLiteral("install"), QStringLiteral("-y"),
- QStringLiteral("xorg-x11-drv-nouveau"),
- QStringLiteral("mesa-dri-drivers")});
+ QStringList installArgs{QStringLiteral("install"), QStringLiteral("-y"),
+ QStringLiteral("--refresh"),
+ QStringLiteral("--best"),
+ QStringLiteral("--allowerasing")};
+ installArgs
+ << buildDriverInstallTargets(QStringLiteral("akmod-nvidia-open"));
+ result = runner.runAsRoot(QStringLiteral("dnf"), installArgs);
if (!result.success()) {
const QString error =
- NvidiaInstaller::tr("Open-source driver installation failed: ") +
- result.stderr.trimmed();
+ NvidiaInstaller::tr("Open NVIDIA kernel module installation failed: ") +
+ commandError(result);
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, error]() {
+ if (guard) {
+ emit guard->installFinished(false, error);
+ }
+ },
+ Qt::QueuedConnection);
+ return;
+ }
+
+ emitProgressAsync(
+ guard,
+ NvidiaInstaller::tr("Building the kernel module (akmods --force)..."));
+ result = runner.runAsRoot(QStringLiteral("akmods"),
+ {QStringLiteral("--force")});
+ if (!result.success()) {
+ const QString error =
+ NvidiaInstaller::tr("Kernel module build failed: ") +
+ commandError(result, NvidiaInstaller::tr("unknown error"));
QMetaObject::invokeMethod(
guard,
[guard, error]() {
@@ -338,6 +374,20 @@ void NvidiaInstaller::installOpenSource() {
return;
}
+ QString sessionError;
+ const QString sessionType = SessionUtil::detectSessionType();
+ if (!guard->applySessionSpecificSetup(runner, sessionType, &sessionError)) {
+ QMetaObject::invokeMethod(
+ guard,
+ [guard, sessionError]() {
+ if (guard) {
+ emit guard->installFinished(false, sessionError);
+ }
+ },
+ Qt::QueuedConnection);
+ return;
+ }
+
runner.runAsRoot(QStringLiteral("dracut"), {QStringLiteral("--force")});
QMetaObject::invokeMethod(
@@ -346,8 +396,8 @@ void NvidiaInstaller::installOpenSource() {
if (guard) {
emit guard->installFinished(
true,
- NvidiaInstaller::tr("The open-source driver (Nouveau) was installed. "
- "Please restart the system."));
+ NvidiaInstaller::tr("NVIDIA open kernel modules were installed "
+ "successfully. Please restart the system."));
}
},
Qt::QueuedConnection);
@@ -371,6 +421,7 @@ void NvidiaInstaller::remove() {
QStringLiteral("dnf"),
{QStringLiteral("remove"), QStringLiteral("-y"),
QStringLiteral("akmod-nvidia"),
+ QStringLiteral("akmod-nvidia-open"),
QStringLiteral("xorg-x11-drv-nvidia*")});
const bool success = result.success();
@@ -404,7 +455,8 @@ void NvidiaInstaller::deepClean() {
const auto removeResult = runner.runAsRoot(
QStringLiteral("dnf"),
{QStringLiteral("remove"), QStringLiteral("-y"),
- QStringLiteral("*nvidia*"), QStringLiteral("*akmod*")});
+ QStringLiteral("*nvidia*"), QStringLiteral("*akmod*"),
+ QStringLiteral("*nvidia-open*")});
if (!removeResult.success()) {
const QString error =
diff --git a/src/backend/nvidia/updater.cpp b/src/backend/nvidia/updater.cpp
index 5ff3c74..60da4a3 100644
--- a/src/backend/nvidia/updater.cpp
+++ b/src/backend/nvidia/updater.cpp
@@ -1,6 +1,7 @@
#include "updater.h"
#include "detector.h"
#include "system/commandrunner.h"
+#include "system/capabilityprobe.h"
#include "system/sessionutil.h"
#include "versionparser.h"
@@ -12,8 +13,7 @@
namespace {
-const QStringList kVersionLockedDriverPackages = {
- QStringLiteral("akmod-nvidia"),
+const QStringList kSharedVersionLockedDriverPackages = {
QStringLiteral("xorg-x11-drv-nvidia"),
QStringLiteral("xorg-x11-drv-nvidia-libs"),
QStringLiteral("xorg-x11-drv-nvidia-cuda"),
@@ -67,13 +67,30 @@ QString firstNonEmptyLine(const QString &text) {
return {};
}
-QString queryLatestRemoteVersion(CommandRunner &runner) {
+QString detectInstalledKernelPackageName() {
+ if (!CapabilityProbe::isToolAvailable(QStringLiteral("rpm"))) {
+ return QStringLiteral("akmod-nvidia");
+ }
+
+ CommandRunner runner;
+ const auto openResult = runner.run(
+ QStringLiteral("rpm"),
+ {QStringLiteral("-q"), QStringLiteral("akmod-nvidia-open")});
+ if (openResult.success()) {
+ return QStringLiteral("akmod-nvidia-open");
+ }
+
+ return QStringLiteral("akmod-nvidia");
+}
+
+QString queryLatestRemoteVersion(CommandRunner &runner,
+ const QString &kernelPackageName) {
const auto result = runner.run(
QStringLiteral("dnf"),
{QStringLiteral("--refresh"), QStringLiteral("repoquery"),
QStringLiteral("--latest-limit"), QStringLiteral("1"),
QStringLiteral("--qf"), QStringLiteral("%{epoch}:%{version}-%{release}"),
- QStringLiteral("akmod-nvidia")});
+ kernelPackageName});
if (!result.success()) {
return {};
@@ -85,6 +102,7 @@ QString queryLatestRemoteVersion(CommandRunner &runner) {
UpdateStatusSnapshot collectUpdateStatus() {
UpdateStatusSnapshot snapshot;
NvidiaDetector detector;
+ const QString kernelPackageName = detectInstalledKernelPackageName();
snapshot.currentVersion = detector.installedDriverVersion();
if (QStandardPaths::findExecutable(QStringLiteral("dnf")).isEmpty()) {
@@ -97,16 +115,16 @@ UpdateStatusSnapshot collectUpdateStatus() {
runner.run(QStringLiteral("dnf"),
{QStringLiteral("--refresh"), QStringLiteral("list"),
QStringLiteral("--showduplicates"),
- QStringLiteral("akmod-nvidia")});
+ kernelPackageName});
if (listResult.success()) {
snapshot.availableVersions =
NvidiaVersionParser::parseAvailablePackageVersions(
- listResult.stdout, QStringLiteral("akmod-nvidia"));
+ listResult.stdout, kernelPackageName);
snapshot.remoteCatalogAvailable = !snapshot.availableVersions.isEmpty();
}
- snapshot.latestVersion = queryLatestRemoteVersion(runner);
+ snapshot.latestVersion = queryLatestRemoteVersion(runner, kernelPackageName);
if (snapshot.latestVersion.isEmpty() && !snapshot.availableVersions.isEmpty()) {
snapshot.latestVersion = snapshot.availableVersions.constLast();
}
@@ -130,11 +148,11 @@ UpdateStatusSnapshot collectUpdateStatus() {
const auto checkResult =
runner.run(QStringLiteral("dnf"), {QStringLiteral("check-update"),
- QStringLiteral("akmod-nvidia")});
+ kernelPackageName});
if (checkResult.exitCode == 100) {
const QString checkUpdateVersion = NvidiaVersionParser::parseCheckUpdateVersion(
- checkResult.stdout, QStringLiteral("akmod-nvidia"));
+ checkResult.stdout, kernelPackageName);
if (!checkUpdateVersion.isEmpty()) {
snapshot.latestVersion = checkUpdateVersion;
}
@@ -279,13 +297,20 @@ QString NvidiaUpdater::detectSessionType() const {
return SessionUtil::detectSessionType();
}
+QString NvidiaUpdater::detectInstalledKernelPackageName() const {
+ return ::detectInstalledKernelPackageName();
+}
+
QStringList
NvidiaUpdater::buildDriverTargets(const QString &version,
- const QString &sessionType) const {
+ const QString &sessionType,
+ const QString &kernelPackageName) const {
Q_UNUSED(sessionType);
QStringList targets;
+ QStringList versionLockedPackages{kernelPackageName};
+ versionLockedPackages << kSharedVersionLockedDriverPackages;
targets << NvidiaVersionParser::buildVersionedPackageSpecs(
- kVersionLockedDriverPackages, version);
+ versionLockedPackages, version);
targets << kFloatingDriverPackages;
return targets;
@@ -293,7 +318,7 @@ NvidiaUpdater::buildDriverTargets(const QString &version,
QStringList NvidiaUpdater::buildTransactionArguments(
const QString &requestedVersion, const QString &installedVersion,
- const QString &sessionType) const {
+ const QString &sessionType, const QString &kernelPackageName) const {
const QString normalizedRequestedVersion = requestedVersion.trimmed();
const QString normalizedInstalledVersion = installedVersion.trimmed();
const QString targetVersion =
@@ -318,7 +343,7 @@ QStringList NvidiaUpdater::buildTransactionArguments(
args << QStringLiteral("--allowerasing");
}
- args << buildDriverTargets(targetVersion, sessionType);
+ args << buildDriverTargets(targetVersion, sessionType, kernelPackageName);
return args;
}
@@ -448,6 +473,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
NvidiaDetector detector;
const QString installedVersion = detector.installedDriverVersion();
const QString sessionType = SessionUtil::detectSessionType();
+ const QString kernelPackageName = guard->detectInstalledKernelPackageName();
if (!trimmedVersion.isEmpty() && !knownVersions.contains(trimmedVersion)) {
QMetaObject::invokeMethod(
@@ -473,7 +499,7 @@ void NvidiaUpdater::applyVersion(const QString &version) {
const QStringList args =
guard->buildTransactionArguments(trimmedVersion, installedVersion,
- sessionType);
+ sessionType, kernelPackageName);
auto result = runner.runAsRoot(QStringLiteral("dnf"), args);
if (!result.success()) {
diff --git a/src/backend/nvidia/updater.h b/src/backend/nvidia/updater.h
index 884063b..53303a8 100644
--- a/src/backend/nvidia/updater.h
+++ b/src/backend/nvidia/updater.h
@@ -50,11 +50,14 @@ class NvidiaUpdater : public QObject {
void setLatestVersion(const QString &version);
void setAvailableVersions(const QStringList &versions);
bool transactionChanged(const CommandRunner::Result &result) const;
+ QString detectInstalledKernelPackageName() const;
QStringList buildTransactionArguments(const QString &requestedVersion,
const QString &installedVersion,
- const QString &sessionType) const;
+ const QString &sessionType,
+ const QString &kernelPackageName) const;
QStringList buildDriverTargets(const QString &version,
- const QString &sessionType) const;
+ const QString &sessionType,
+ const QString &kernelPackageName) const;
bool finalizeDriverChange(CommandRunner &runner, const QString &sessionType,
QString *errorMessage);
QString detectSessionType() const;
diff --git a/src/backend/system/languagemanager.cpp b/src/backend/system/languagemanager.cpp
index 8d434e1..764be87 100644
--- a/src/backend/system/languagemanager.cpp
+++ b/src/backend/system/languagemanager.cpp
@@ -9,10 +9,19 @@
namespace {
-const std::pair kSupportedLanguages[] = {
- {QStringLiteral("system"), QStringLiteral("System Default")},
- {QStringLiteral("en"), QStringLiteral("English")},
- {QStringLiteral("tr"), QStringLiteral("Turkce")},
+struct LanguageEntry {
+ const char *code;
+ const char *label;
+ const char *nativeLabel;
+ bool shipped;
+};
+
+constexpr LanguageEntry kSupportedLanguages[] = {
+ {"system", "System Default", "System Default", true},
+ {"en", "English", "English", true},
+ {"de", "German", "Deutsch", true},
+ {"es", "Spanish", "Espanol", true},
+ {"tr", "Turkish", "Turkce", true},
};
} // namespace
@@ -30,16 +39,29 @@ LanguageManager::LanguageManager(QCoreApplication *application, QQmlEngine *engi
QString LanguageManager::currentLanguage() const { return m_currentLanguage; }
+QString LanguageManager::effectiveLanguage() const {
+ return effectiveLanguageCode(m_currentLanguage);
+}
+
QString LanguageManager::currentLanguageLabel() const {
- return displayNameForLanguage(effectiveLanguageCode(m_currentLanguage));
+ if (m_currentLanguage == QStringLiteral("system")) {
+ return QStringLiteral("%1 (%2)")
+ .arg(displayNameForLanguage(m_currentLanguage),
+ displayNameForLanguage(effectiveLanguage()));
+ }
+
+ return displayNameForLanguage(m_currentLanguage);
}
QVariantList LanguageManager::availableLanguages() const {
QVariantList languages;
for (const auto &entry : kSupportedLanguages) {
QVariantMap language;
- language.insert(QStringLiteral("code"), entry.first);
- language.insert(QStringLiteral("label"), entry.second);
+ language.insert(QStringLiteral("code"), QString::fromLatin1(entry.code));
+ language.insert(QStringLiteral("label"), QString::fromLatin1(entry.label));
+ language.insert(QStringLiteral("nativeLabel"),
+ QString::fromLatin1(entry.nativeLabel));
+ language.insert(QStringLiteral("shipped"), entry.shipped);
languages.append(language);
}
@@ -69,8 +91,8 @@ QString LanguageManager::displayNameForLanguage(
const QString &languageCode) const {
const QString normalizedLanguage = normalizeLanguageCode(languageCode);
for (const auto &entry : kSupportedLanguages) {
- if (entry.first == normalizedLanguage) {
- return entry.second;
+ if (QString::fromLatin1(entry.code) == normalizedLanguage) {
+ return QString::fromLatin1(entry.nativeLabel);
}
}
@@ -80,7 +102,7 @@ QString LanguageManager::displayNameForLanguage(
QString LanguageManager::normalizeLanguageCode(const QString &languageCode) const {
const QString normalizedLanguage = languageCode.trimmed().toLower();
for (const auto &entry : kSupportedLanguages) {
- if (entry.first == normalizedLanguage) {
+ if (QString::fromLatin1(entry.code) == normalizedLanguage) {
return normalizedLanguage;
}
}
diff --git a/src/backend/system/languagemanager.h b/src/backend/system/languagemanager.h
index 179cedb..6651304 100644
--- a/src/backend/system/languagemanager.h
+++ b/src/backend/system/languagemanager.h
@@ -14,6 +14,8 @@ class LanguageManager : public QObject {
Q_PROPERTY(QString currentLanguage READ currentLanguage WRITE setCurrentLanguage
NOTIFY currentLanguageChanged)
+ Q_PROPERTY(QString effectiveLanguage READ effectiveLanguage NOTIFY
+ currentLanguageChanged)
Q_PROPERTY(QString currentLanguageLabel READ currentLanguageLabel NOTIFY
currentLanguageChanged)
Q_PROPERTY(QVariantList availableLanguages READ availableLanguages CONSTANT)
@@ -23,6 +25,7 @@ class LanguageManager : public QObject {
QTranslator *translator, QObject *parent = nullptr);
QString currentLanguage() const;
+ QString effectiveLanguage() const;
QString currentLanguageLabel() const;
QVariantList availableLanguages() const;
diff --git a/src/backend/system/uipreferencesmanager.cpp b/src/backend/system/uipreferencesmanager.cpp
new file mode 100644
index 0000000..5f27e2a
--- /dev/null
+++ b/src/backend/system/uipreferencesmanager.cpp
@@ -0,0 +1,106 @@
+#include "uipreferencesmanager.h"
+
+#include
+#include
+
+namespace {
+
+struct ThemeModeEntry {
+ const char *code;
+ const char *label;
+};
+
+constexpr ThemeModeEntry kThemeModes[] = {
+ {"system", "Follow System"},
+ {"light", "Light"},
+ {"dark", "Dark"},
+};
+
+} // namespace
+
+UiPreferencesManager::UiPreferencesManager(QObject *parent) : QObject(parent) {
+ QSettings settings;
+ m_themeMode = normalizeThemeMode(
+ settings.value(QStringLiteral("ui/themeMode"), m_themeMode).toString());
+ m_compactMode =
+ settings.value(QStringLiteral("ui/compactMode"), m_compactMode).toBool();
+ m_showAdvancedInfo = settings
+ .value(QStringLiteral("ui/showAdvancedInfo"),
+ m_showAdvancedInfo)
+ .toBool();
+}
+
+QString UiPreferencesManager::themeMode() const { return m_themeMode; }
+
+QVariantList UiPreferencesManager::availableThemeModes() const {
+ QVariantList modes;
+ for (const auto &entry : kThemeModes) {
+ QVariantMap mode;
+ mode.insert(QStringLiteral("code"), QString::fromLatin1(entry.code));
+ mode.insert(QStringLiteral("label"),
+ QCoreApplication::translate("UiPreferencesManager",
+ entry.label));
+ modes.append(mode);
+ }
+ return modes;
+}
+
+bool UiPreferencesManager::compactMode() const { return m_compactMode; }
+
+bool UiPreferencesManager::showAdvancedInfo() const {
+ return m_showAdvancedInfo;
+}
+
+void UiPreferencesManager::setThemeMode(const QString &themeMode) {
+ const QString normalizedThemeMode = normalizeThemeMode(themeMode);
+ if (normalizedThemeMode == m_themeMode) {
+ return;
+ }
+
+ m_themeMode = normalizedThemeMode;
+ persistValue(QStringLiteral("ui/themeMode"), m_themeMode);
+ emit themeModeChanged();
+}
+
+void UiPreferencesManager::setCompactMode(bool compactMode) {
+ if (compactMode == m_compactMode) {
+ return;
+ }
+
+ m_compactMode = compactMode;
+ persistValue(QStringLiteral("ui/compactMode"), m_compactMode);
+ emit compactModeChanged();
+}
+
+void UiPreferencesManager::setShowAdvancedInfo(bool showAdvancedInfo) {
+ if (showAdvancedInfo == m_showAdvancedInfo) {
+ return;
+ }
+
+ m_showAdvancedInfo = showAdvancedInfo;
+ persistValue(QStringLiteral("ui/showAdvancedInfo"), m_showAdvancedInfo);
+ emit showAdvancedInfoChanged();
+}
+
+void UiPreferencesManager::resetToDefaults() {
+ setThemeMode(QStringLiteral("system"));
+ setCompactMode(false);
+ setShowAdvancedInfo(true);
+}
+
+QString UiPreferencesManager::normalizeThemeMode(const QString &themeMode) const {
+ const QString normalizedThemeMode = themeMode.trimmed().toLower();
+ for (const auto &entry : kThemeModes) {
+ if (normalizedThemeMode == QLatin1String(entry.code)) {
+ return normalizedThemeMode;
+ }
+ }
+
+ return QStringLiteral("system");
+}
+
+void UiPreferencesManager::persistValue(const QString &key,
+ const QVariant &value) const {
+ QSettings settings;
+ settings.setValue(key, value);
+}
diff --git a/src/backend/system/uipreferencesmanager.h b/src/backend/system/uipreferencesmanager.h
new file mode 100644
index 0000000..9dad79a
--- /dev/null
+++ b/src/backend/system/uipreferencesmanager.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include
+#include
+#include
+
+class UiPreferencesManager : public QObject {
+ Q_OBJECT
+
+ Q_PROPERTY(QString themeMode READ themeMode WRITE setThemeMode NOTIFY
+ themeModeChanged)
+ Q_PROPERTY(QVariantList availableThemeModes READ availableThemeModes CONSTANT)
+ Q_PROPERTY(bool compactMode READ compactMode WRITE setCompactMode NOTIFY
+ compactModeChanged)
+ Q_PROPERTY(bool showAdvancedInfo READ showAdvancedInfo WRITE
+ setShowAdvancedInfo NOTIFY showAdvancedInfoChanged)
+
+public:
+ explicit UiPreferencesManager(QObject *parent = nullptr);
+
+ QString themeMode() const;
+ QVariantList availableThemeModes() const;
+
+ bool compactMode() const;
+ bool showAdvancedInfo() const;
+
+ Q_INVOKABLE void setThemeMode(const QString &themeMode);
+ Q_INVOKABLE void setCompactMode(bool compactMode);
+ Q_INVOKABLE void setShowAdvancedInfo(bool showAdvancedInfo);
+ Q_INVOKABLE void resetToDefaults();
+
+signals:
+ void themeModeChanged();
+ void compactModeChanged();
+ void showAdvancedInfoChanged();
+
+private:
+ QString normalizeThemeMode(const QString &themeMode) const;
+ void persistValue(const QString &key, const QVariant &value) const;
+
+ QString m_themeMode = QStringLiteral("system");
+ bool m_compactMode = false;
+ bool m_showAdvancedInfo = true;
+};
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 292a667..927be42 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -65,10 +66,10 @@ QString buildHelpText(const QString &applicationName,
stream << "Driver install options:\n";
stream << " --proprietary Install the proprietary akmod-nvidia "
"stack.\n";
- stream << " --open-source Install the open-source Nouveau "
- "stack.\n";
- stream << " --accept-license Confirm proprietary driver license "
- "acceptance.\n\n";
+ stream << " --open-source Install the NVIDIA open kernel "
+ "module stack.\n";
+ stream << " --accept-license Confirm NVIDIA license review for "
+ "the proprietary install path.\n\n";
stream << "Global options:\n";
stream << " -h, --help Show help and exit.\n";
stream << " -v, --version Show version and exit.\n";
@@ -112,11 +113,11 @@ void configureParser(QCommandLineParser &parser, const QString &applicationName,
QStringLiteral("Use the proprietary NVIDIA driver install path.")));
parser.addOption(QCommandLineOption(
{QStringLiteral("open-source")},
- QStringLiteral("Use the open-source Nouveau install path.")));
+ QStringLiteral("Use the NVIDIA open kernel module install path.")));
parser.addOption(QCommandLineOption(
{QStringLiteral("accept-license")},
QStringLiteral(
- "Confirm that the proprietary NVIDIA license was reviewed.")));
+ "Confirm that the NVIDIA license was reviewed.")));
parser.addPositionalArgument(QStringLiteral("command"),
QStringLiteral("CLI command to execute."));
parser.addPositionalArgument(
@@ -347,7 +348,23 @@ DiagnosticsSnapshot collectDiagnostics(const QString &applicationName,
snapshot.verificationReport = detector.verificationReport();
NvidiaUpdater updater;
+ QEventLoop updaterLoop;
+ bool updaterStarted = false;
+ QObject::connect(&updater, &NvidiaUpdater::busyChanged, &updater,
+ [&]() {
+ if (updater.busy()) {
+ updaterStarted = true;
+ return;
+ }
+
+ if (updaterStarted) {
+ updaterLoop.quit();
+ }
+ });
updater.checkForUpdate();
+ if (updater.busy()) {
+ updaterLoop.exec();
+ }
snapshot.currentDriverVersion = updater.currentVersion();
snapshot.latestDriverVersion = updater.latestVersion();
snapshot.updateAvailable = updater.updateAvailable();
diff --git a/src/main.cpp b/src/main.cpp
index 80db609..b0ae8bf 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
#include
@@ -21,6 +22,7 @@
#include "backend/nvidia/installer.h"
#include "backend/nvidia/updater.h"
#include "backend/system/languagemanager.h"
+#include "backend/system/uipreferencesmanager.h"
#include "cli/cli.h"
namespace {
@@ -79,18 +81,21 @@ CliExecutionResult executeCliCommand(const RoControlCli::ParsedCommand &command,
bool finished = false;
bool success = false;
QString finalMessage;
+ QEventLoop loop;
QObject::connect(&installer, &NvidiaInstaller::installFinished, &installer,
[&](bool ok, const QString &message) {
finished = true;
success = ok;
finalMessage = message;
+ loop.quit();
});
QObject::connect(&installer, &NvidiaInstaller::removeFinished, &installer,
[&](bool ok, const QString &message) {
finished = true;
success = ok;
finalMessage = message;
+ loop.quit();
});
if (command.action ==
@@ -103,9 +108,10 @@ CliExecutionResult executeCliCommand(const RoControlCli::ParsedCommand &command,
installer.remove();
} else {
installer.deepClean();
- finished = true;
- success = true;
- finalMessage = QStringLiteral("Legacy NVIDIA cleanup completed.");
+ }
+
+ if (!finished) {
+ loop.exec();
}
if (!finalMessage.isEmpty()) {
@@ -128,16 +134,22 @@ CliExecutionResult executeCliCommand(const RoControlCli::ParsedCommand &command,
bool finished = false;
bool success = false;
QString finalMessage;
+ QEventLoop loop;
QObject::connect(&updater, &NvidiaUpdater::updateFinished, &updater,
[&](bool ok, const QString &message) {
finished = true;
success = ok;
finalMessage = message;
+ loop.quit();
});
updater.applyUpdate();
+ if (!finished) {
+ loop.exec();
+ }
+
if (!finalMessage.isEmpty()) {
if (success) {
appendProgress(finalMessage);
@@ -255,6 +267,7 @@ int main(int argc, char *argv[]) {
QQmlApplicationEngine engine;
LanguageManager languageManager(&app, &engine, &translator);
+ UiPreferencesManager uiPreferencesManager;
// Backend nesnelerini tüm QML dosyalarına global olarak aç
engine.rootContext()->setContextProperty("nvidiaDetector", &detector);
@@ -264,6 +277,8 @@ int main(int argc, char *argv[]) {
engine.rootContext()->setContextProperty("gpuMonitor", &gpuMonitor);
engine.rootContext()->setContextProperty("ramMonitor", &ramMonitor);
engine.rootContext()->setContextProperty("languageManager", &languageManager);
+ engine.rootContext()->setContextProperty("uiPreferences",
+ &uiPreferencesManager);
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
diff --git a/src/qml/Main.qml b/src/qml/Main.qml
index 86f3dad..fae0190 100644
--- a/src/qml/Main.qml
+++ b/src/qml/Main.qml
@@ -11,9 +11,12 @@ ApplicationWindow {
minimumHeight: 680
title: qsTr("ro-Control")
- readonly property bool darkMode: Qt.styleHints.colorScheme === Qt.Dark
- property bool compactMode: false
- property bool showAdvancedInfo: true
+ readonly property string themeMode: uiPreferences.themeMode
+ readonly property bool darkMode: themeMode === "dark"
+ || (themeMode === "system"
+ && Qt.styleHints.colorScheme === Qt.Dark)
+ readonly property bool compactMode: uiPreferences.compactMode
+ readonly property bool showAdvancedInfo: uiPreferences.showAdvancedInfo
readonly property var pageTitles: [
qsTr("Driver Control Center"),
@@ -162,7 +165,10 @@ ApplicationWindow {
spacing: 10
InfoBadge {
- text: root.darkMode ? qsTr("System Dark") : qsTr("System Light")
+ text: root.themeMode === "system"
+ ? qsTr("Follow System")
+ : root.darkMode ? qsTr("Dark Theme")
+ : qsTr("Light Theme")
backgroundColor: root.theme.infoBg
foregroundColor: root.theme.text
}
@@ -201,8 +207,6 @@ ApplicationWindow {
darkMode: root.darkMode
compactMode: root.compactMode
showAdvancedInfo: root.showAdvancedInfo
- onCompactModeChanged: root.compactMode = compactMode
- onShowAdvancedInfoChanged: root.showAdvancedInfo = showAdvancedInfo
}
}
}
diff --git a/src/qml/pages/DriverPage.qml b/src/qml/pages/DriverPage.qml
index 3ee12b6..1c5d1f1 100644
--- a/src/qml/pages/DriverPage.qml
+++ b/src/qml/pages/DriverPage.qml
@@ -19,6 +19,7 @@ Item {
property double operationStartedAt: 0
property double lastLogAt: 0
property int operationElapsedSeconds: 0
+ readonly property string nvidiaLicenseUrl: "https://www.nvidia.com/en-us/drivers/nvidia-license/"
readonly property bool backendBusy: nvidiaInstaller.busy || nvidiaUpdater.busy
readonly property bool operationRunning: page.operationActive || page.backendBusy
@@ -284,7 +285,7 @@ Item {
}
InfoBadge {
- text: nvidiaDetector.nouveauActive ? qsTr("Nouveau Active") : qsTr("Nouveau Inactive")
+ text: nvidiaDetector.nouveauActive ? qsTr("Fallback Open Driver Active") : qsTr("Fallback Open Driver Inactive")
backgroundColor: nvidiaDetector.nouveauActive ? page.theme.warningBg : page.theme.cardStrong
foregroundColor: page.theme.text
}
@@ -380,7 +381,17 @@ Item {
CheckBox {
id: eulaAccept
visible: nvidiaInstaller.proprietaryAgreementRequired
- text: qsTr("I accept the detected license / agreement terms")
+ text: qsTr("I reviewed the NVIDIA license terms")
+ }
+
+ Label {
+ Layout.fillWidth: true
+ visible: nvidiaInstaller.proprietaryAgreementRequired
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ color: page.theme.textSoft
+ text: qsTr("Official NVIDIA license: %1").arg(page.nvidiaLicenseUrl)
+ onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
GridLayout {
@@ -393,7 +404,7 @@ Item {
Layout.fillWidth: true
theme: page.theme
tone: "primary"
- text: qsTr("Install Proprietary")
+ text: qsTr("Install NVIDIA Driver")
enabled: !nvidiaInstaller.busy && (!nvidiaInstaller.proprietaryAgreementRequired || eulaAccept.checked)
onClicked: {
page.setOperationState(qsTr("Installer"), qsTr("Installing the proprietary NVIDIA driver (akmod-nvidia)..."), "info", true);
@@ -404,10 +415,10 @@ Item {
ActionButton {
Layout.fillWidth: true
theme: page.theme
- text: qsTr("Install Nouveau")
+ text: qsTr("Install Open Kernel Modules")
enabled: !nvidiaInstaller.busy
onClicked: {
- page.setOperationState(qsTr("Installer"), qsTr("Switching to the open-source driver..."), "info", true);
+ page.setOperationState(qsTr("Installer"), qsTr("Switching to NVIDIA open kernel modules..."), "info", true);
nvidiaInstaller.installOpenSource();
}
}
diff --git a/src/qml/pages/SettingsPage.qml b/src/qml/pages/SettingsPage.qml
index 7571b01..ce08931 100644
--- a/src/qml/pages/SettingsPage.qml
+++ b/src/qml/pages/SettingsPage.qml
@@ -9,6 +9,7 @@ Item {
property bool darkMode: false
property bool compactMode: false
property bool showAdvancedInfo: true
+ readonly property string themeMode: uiPreferences.themeMode
ScrollView {
id: pageScroll
@@ -29,8 +30,47 @@ Item {
SectionPanel {
Layout.fillWidth: true
theme: settingsPage.theme
- title: qsTr("Interface")
- subtitle: qsTr("Tune the shell density and how much operational detail the app exposes.")
+ title: qsTr("Appearance & Behavior")
+ subtitle: qsTr("Control theme, density and operator-focused interface behavior.")
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 12
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 4
+
+ Label {
+ text: qsTr("Theme mode")
+ font.bold: true
+ color: settingsPage.theme.text
+ }
+
+ Label {
+ text: qsTr("Choose whether the application follows the OS theme or uses an explicit light or dark shell.")
+ wrapMode: Text.Wrap
+ color: settingsPage.theme.textSoft
+ Layout.fillWidth: true
+ }
+ }
+
+ ComboBox {
+ id: themePicker
+ Layout.preferredWidth: 220
+ model: uiPreferences.availableThemeModes
+ textRole: "label"
+
+ Component.onCompleted: settingsPage.syncThemePicker()
+
+ onActivated: {
+ const selected = model[currentIndex];
+ if (selected && selected.code) {
+ uiPreferences.setThemeMode(selected.code);
+ }
+ }
+ }
+ }
RowLayout {
Layout.fillWidth: true
@@ -58,16 +98,9 @@ Item {
id: languagePicker
Layout.preferredWidth: 220
model: languageManager.availableLanguages
- textRole: "label"
+ textRole: "nativeLabel"
- Component.onCompleted: {
- for (let i = 0; i < model.length; ++i) {
- if (model[i].code === languageManager.currentLanguage) {
- currentIndex = i;
- break;
- }
- }
- }
+ Component.onCompleted: settingsPage.syncLanguagePicker()
onActivated: {
const selected = model[currentIndex];
@@ -100,8 +133,8 @@ Item {
}
Switch {
- checked: settingsPage.compactMode
- onToggled: settingsPage.compactMode = checked
+ checked: uiPreferences.compactMode
+ onToggled: uiPreferences.setCompactMode(checked)
}
}
@@ -127,12 +160,12 @@ Item {
}
Switch {
- checked: settingsPage.showAdvancedInfo
- onToggled: settingsPage.showAdvancedInfo = checked
+ checked: uiPreferences.showAdvancedInfo
+ onToggled: uiPreferences.setShowAdvancedInfo(checked)
}
}
- RowLayout {
+ Flow {
Layout.fillWidth: true
spacing: 8
@@ -143,16 +176,43 @@ Item {
}
InfoBadge {
- text: settingsPage.darkMode ? qsTr("System Dark Theme") : qsTr("System Light Theme")
+ text: settingsPage.themeMode === "system"
+ ? qsTr("Theme: Follow System")
+ : settingsPage.darkMode ? qsTr("Theme: Dark")
+ : qsTr("Theme: Light")
backgroundColor: settingsPage.theme.infoBg
foregroundColor: settingsPage.theme.text
}
InfoBadge {
- text: settingsPage.compactMode ? qsTr("Compact Active") : qsTr("Comfort Active")
+ text: uiPreferences.compactMode ? qsTr("Compact Active") : qsTr("Comfort Active")
backgroundColor: settingsPage.theme.cardStrong
foregroundColor: settingsPage.theme.text
}
+
+ InfoBadge {
+ text: uiPreferences.showAdvancedInfo ? qsTr("Advanced Visible") : qsTr("Advanced Hidden")
+ backgroundColor: settingsPage.theme.cardStrong
+ foregroundColor: settingsPage.theme.text
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 12
+
+ Label {
+ Layout.fillWidth: true
+ text: qsTr("Restore the recommended interface defaults if the shell starts to feel cluttered.")
+ wrapMode: Text.Wrap
+ color: settingsPage.theme.textSoft
+ }
+
+ ActionButton {
+ theme: settingsPage.theme
+ text: qsTr("Reset Interface Defaults")
+ onClicked: uiPreferences.resetToDefaults()
+ }
}
}
@@ -247,21 +307,31 @@ Item {
Layout.fillWidth: true
theme: settingsPage.theme
label: qsTr("Theme")
- value: settingsPage.darkMode ? qsTr("System Dark") : qsTr("System Light")
+ value: settingsPage.themeMode === "system"
+ ? qsTr("Follow System")
+ : settingsPage.darkMode ? qsTr("Dark")
+ : qsTr("Light")
+ }
+
+ DetailRow {
+ Layout.fillWidth: true
+ theme: settingsPage.theme
+ label: qsTr("Effective language")
+ value: languageManager.displayNameForLanguage(languageManager.effectiveLanguage)
}
DetailRow {
Layout.fillWidth: true
theme: settingsPage.theme
label: qsTr("Layout density")
- value: settingsPage.compactMode ? qsTr("Compact") : qsTr("Comfort")
+ value: uiPreferences.compactMode ? qsTr("Compact") : qsTr("Comfort")
}
DetailRow {
Layout.fillWidth: true
theme: settingsPage.theme
label: qsTr("Advanced diagnostics")
- value: settingsPage.showAdvancedInfo ? qsTr("Visible") : qsTr("Hidden")
+ value: uiPreferences.showAdvancedInfo ? qsTr("Visible") : qsTr("Hidden")
}
}
}
@@ -269,16 +339,37 @@ Item {
}
}
+ function syncLanguagePicker() {
+ for (let i = 0; i < languagePicker.model.length; ++i) {
+ if (languagePicker.model[i].code === languageManager.currentLanguage) {
+ languagePicker.currentIndex = i;
+ break;
+ }
+ }
+ }
+
+ function syncThemePicker() {
+ for (let i = 0; i < themePicker.model.length; ++i) {
+ if (themePicker.model[i].code === uiPreferences.themeMode) {
+ themePicker.currentIndex = i;
+ break;
+ }
+ }
+ }
+
Connections {
target: languageManager
function onCurrentLanguageChanged() {
- for (let i = 0; i < languagePicker.model.length; ++i) {
- if (languagePicker.model[i].code === languageManager.currentLanguage) {
- languagePicker.currentIndex = i;
- break;
- }
- }
+ settingsPage.syncLanguagePicker()
+ }
+ }
+
+ Connections {
+ target: uiPreferences
+
+ function onThemeModeChanged() {
+ settingsPage.syncThemePicker()
}
}
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 7de7d5c..ae3029b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -49,6 +49,21 @@ target_link_libraries(test_monitor PRIVATE
add_test(NAME test_monitor COMMAND test_monitor)
+# ─── Preferences / Localization Tests ───────────────────────────────────────
+qt_add_executable(test_preferences
+ test_preferences.cpp
+)
+
+ro_control_configure_test(test_preferences)
+
+target_link_libraries(test_preferences PRIVATE
+ Qt6::Qml
+ Qt6::Test
+ ro-control-backend
+)
+
+add_test(NAME test_preferences COMMAND test_preferences)
+
# ─── System Integration Tests ───────────────────────────────────────────────
qt_add_executable(test_system_integration
test_system_integration.cpp
diff --git a/tests/test_detector.cpp b/tests/test_detector.cpp
index b574a71..24c9587 100644
--- a/tests/test_detector.cpp
+++ b/tests/test_detector.cpp
@@ -19,6 +19,7 @@ private slots:
QVERIFY(info.driverVersion.isEmpty());
QCOMPARE(info.driverLoaded, false);
QCOMPARE(info.nouveauActive, false);
+ QCOMPARE(info.openKernelModulesInstalled, false);
QCOMPARE(info.secureBootEnabled, false);
}
diff --git a/tests/test_preferences.cpp b/tests/test_preferences.cpp
new file mode 100644
index 0000000..ebcf567
--- /dev/null
+++ b/tests/test_preferences.cpp
@@ -0,0 +1,94 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "backend/system/languagemanager.h"
+#include "backend/system/uipreferencesmanager.h"
+
+class TestPreferences : public QObject {
+ Q_OBJECT
+
+private slots:
+ void init();
+ void testUiPreferencesDefaults();
+ void testUiPreferencesPersistChanges();
+ void testUiPreferencesNormalizesInvalidThemeMode();
+ void testLanguageManagerExposesEffectiveLanguageMetadata();
+};
+
+void TestPreferences::init() {
+ QCoreApplication::setOrganizationName(
+ QStringLiteral("Project-Ro-ASD-TestSuite"));
+ QCoreApplication::setApplicationName(QStringLiteral("ro-control-preferences"));
+
+ QSettings settings;
+ settings.clear();
+ settings.sync();
+}
+
+void TestPreferences::testUiPreferencesDefaults() {
+ UiPreferencesManager preferences;
+
+ QCOMPARE(preferences.themeMode(), QStringLiteral("system"));
+ QCOMPARE(preferences.compactMode(), false);
+ QCOMPARE(preferences.showAdvancedInfo(), true);
+ QCOMPARE(preferences.availableThemeModes().size(), 3);
+}
+
+void TestPreferences::testUiPreferencesPersistChanges() {
+ UiPreferencesManager preferences;
+ QSignalSpy themeSpy(&preferences, &UiPreferencesManager::themeModeChanged);
+ QSignalSpy compactSpy(&preferences, &UiPreferencesManager::compactModeChanged);
+ QSignalSpy advancedSpy(&preferences,
+ &UiPreferencesManager::showAdvancedInfoChanged);
+
+ preferences.setThemeMode(QStringLiteral("dark"));
+ preferences.setCompactMode(true);
+ preferences.setShowAdvancedInfo(false);
+
+ QCOMPARE(themeSpy.count(), 1);
+ QCOMPARE(compactSpy.count(), 1);
+ QCOMPARE(advancedSpy.count(), 1);
+
+ UiPreferencesManager reloadedPreferences;
+ QCOMPARE(reloadedPreferences.themeMode(), QStringLiteral("dark"));
+ QCOMPARE(reloadedPreferences.compactMode(), true);
+ QCOMPARE(reloadedPreferences.showAdvancedInfo(), false);
+}
+
+void TestPreferences::testUiPreferencesNormalizesInvalidThemeMode() {
+ UiPreferencesManager preferences;
+
+ preferences.setThemeMode(QStringLiteral("midnight"));
+ QCOMPARE(preferences.themeMode(), QStringLiteral("system"));
+}
+
+void TestPreferences::testLanguageManagerExposesEffectiveLanguageMetadata() {
+ QQmlEngine engine;
+ QTranslator translator;
+ LanguageManager manager(QCoreApplication::instance(), &engine, &translator);
+
+ const QVariantList languages = manager.availableLanguages();
+ QVERIFY(!languages.isEmpty());
+ QCOMPARE(languages.first().toMap().value(QStringLiteral("code")).toString(),
+ QStringLiteral("system"));
+ QVERIFY(languages.first()
+ .toMap()
+ .contains(QStringLiteral("nativeLabel")));
+
+ manager.setCurrentLanguage(QStringLiteral("system"));
+ QVERIFY(
+ manager.currentLanguageLabel().startsWith(QStringLiteral("System Default")));
+
+ manager.setCurrentLanguage(QStringLiteral("tr"));
+ QCOMPARE(manager.currentLanguage(), QStringLiteral("tr"));
+ QCOMPARE(manager.effectiveLanguage(), QStringLiteral("tr"));
+ QCOMPARE(manager.currentLanguageLabel(), QStringLiteral("Turkce"));
+}
+
+QTEST_GUILESS_MAIN(TestPreferences)
+
+#include "test_preferences.moc"
diff --git a/tests/test_updater.cpp b/tests/test_updater.cpp
index 5a0bcae..d24bd1f 100644
--- a/tests/test_updater.cpp
+++ b/tests/test_updater.cpp
@@ -60,7 +60,8 @@ private slots:
updater.m_latestVersion = QStringLiteral("3:570.153.02-1.fc42");
const QStringList args =
- updater.buildTransactionArguments(QString(), QString(), QString());
+ updater.buildTransactionArguments(QString(), QString(), QString(),
+ QStringLiteral("akmod-nvidia"));
QCOMPARE(args.value(0), QStringLiteral("install"));
QVERIFY(args.contains(QStringLiteral("--refresh")));
@@ -76,7 +77,8 @@ private slots:
updater.m_latestVersion = QStringLiteral("3:570.153.02-1.fc42");
const QStringList args = updater.buildTransactionArguments(
- QString(), QStringLiteral("3:565.77-1.fc42"), QString());
+ QString(), QStringLiteral("3:565.77-1.fc42"), QString(),
+ QStringLiteral("akmod-nvidia"));
QCOMPARE(args.value(0), QStringLiteral("distro-sync"));
QVERIFY(args.contains(QStringLiteral("--allowerasing")));
@@ -109,6 +111,18 @@ private slots:
QVERIFY(updater.transactionChanged(result));
}
+
+ void testBuildTransactionArgumentsForOpenKernelModules() {
+ NvidiaUpdater updater;
+ updater.m_latestVersion = QStringLiteral("3:570.153.02-1.fc42");
+
+ const QStringList args = updater.buildTransactionArguments(
+ QString(), QStringLiteral("3:565.77-1.fc42"), QString(),
+ QStringLiteral("akmod-nvidia-open"));
+
+ QVERIFY(args.contains(
+ QStringLiteral("akmod-nvidia-open-3:570.153.02-1.fc42")));
+ }
};
QTEST_MAIN(TestUpdater)