762 lines
25 KiB
Rust
762 lines
25 KiB
Rust
//! Settings service: parses `$XDG_CONFIG_HOME/nova-shell/modules.json` once at
|
|
//! plugin load and exposes per-leaf primitive Q_PROPERTYs to QML. Defaults
|
|
//! mirror the previous QML implementation; a missing file yields all defaults
|
|
//! silently. Read errors / malformed JSON crash so misconfigurations are loud.
|
|
//! Unknown fields warn via `tracing` and are otherwise ignored.
|
|
|
|
use core::pin::Pin;
|
|
use cxx_qt::CxxQtType;
|
|
use cxx_qt_lib::{QList, QString};
|
|
use serde::Deserialize;
|
|
use std::path::PathBuf;
|
|
|
|
#[cxx_qt::bridge]
|
|
pub mod qobject {
|
|
unsafe extern "C++" {
|
|
include!("cxx-qt-lib/qstring.h");
|
|
type QString = cxx_qt_lib::QString;
|
|
|
|
include!("cxx-qt-lib/core/qlist/qlist_QString.h");
|
|
type QList_QString = cxx_qt_lib::QList<cxx_qt_lib::QString>;
|
|
}
|
|
|
|
extern "RustQt" {
|
|
#[qobject]
|
|
#[qml_element]
|
|
#[qml_singleton]
|
|
// Stats polling interval (-1 = use default).
|
|
#[qproperty(i32, stats_daemon_interval, cxx_name = "statsDaemonInterval")]
|
|
// Per-module enables (mirrors `<group>.enable`).
|
|
#[qproperty(bool, workspaces_enable, cxx_name = "workspacesEnable")]
|
|
#[qproperty(bool, tray_enable, cxx_name = "trayEnable")]
|
|
#[qproperty(bool, window_title_enable, cxx_name = "windowTitleEnable")]
|
|
#[qproperty(bool, clock_enable, cxx_name = "clockEnable")]
|
|
#[qproperty(bool, mpris_enable, cxx_name = "mprisEnable")]
|
|
#[qproperty(bool, volume_enable, cxx_name = "volumeEnable")]
|
|
#[qproperty(bool, bluetooth_enable, cxx_name = "bluetoothEnable")]
|
|
#[qproperty(bool, network_enable, cxx_name = "networkEnable")]
|
|
#[qproperty(bool, power_profile_enable, cxx_name = "powerProfileEnable")]
|
|
#[qproperty(bool, idle_inhibitor_enable, cxx_name = "idleInhibitorEnable")]
|
|
#[qproperty(bool, cpu_enable, cxx_name = "cpuEnable")]
|
|
#[qproperty(bool, memory_enable, cxx_name = "memoryEnable")]
|
|
#[qproperty(bool, privacy_enable, cxx_name = "privacyEnable")]
|
|
#[qproperty(bool, screen_corners_enable, cxx_name = "screenCornersEnable")]
|
|
#[qproperty(bool, power_enable, cxx_name = "powerEnable")]
|
|
#[qproperty(bool, background_overlay_enable, cxx_name = "backgroundOverlayEnable")]
|
|
#[qproperty(bool, overview_backdrop_enable, cxx_name = "overviewBackdropEnable")]
|
|
// Notifications.
|
|
#[qproperty(bool, notifications_enable, cxx_name = "notificationsEnable")]
|
|
#[qproperty(i32, notifications_timeout, cxx_name = "notificationsTimeout")]
|
|
#[qproperty(i32, notifications_max_popups, cxx_name = "notificationsMaxPopups")]
|
|
#[qproperty(i32, notifications_max_visible, cxx_name = "notificationsMaxVisible")]
|
|
#[qproperty(i32, notifications_max_history, cxx_name = "notificationsMaxHistory")]
|
|
// Backlight.
|
|
#[qproperty(bool, backlight_enable, cxx_name = "backlightEnable")]
|
|
#[qproperty(i32, backlight_step, cxx_name = "backlightStep")]
|
|
// Weather.
|
|
#[qproperty(bool, weather_enable, cxx_name = "weatherEnable")]
|
|
#[qproperty(QList_QString, weather_args, cxx_name = "weatherArgs")]
|
|
#[qproperty(i32, weather_interval, cxx_name = "weatherInterval")]
|
|
// Temperature.
|
|
#[qproperty(bool, temperature_enable, cxx_name = "temperatureEnable")]
|
|
#[qproperty(i32, temperature_warm, cxx_name = "temperatureWarm")]
|
|
#[qproperty(i32, temperature_hot, cxx_name = "temperatureHot")]
|
|
#[qproperty(QString, temperature_device, cxx_name = "temperatureDevice")]
|
|
// GPU.
|
|
#[qproperty(bool, gpu_enable, cxx_name = "gpuEnable")]
|
|
#[qproperty(i32, gpu_warm, cxx_name = "gpuWarm")]
|
|
#[qproperty(i32, gpu_hot, cxx_name = "gpuHot")]
|
|
// Disk.
|
|
#[qproperty(bool, disk_enable, cxx_name = "diskEnable")]
|
|
#[qproperty(i32, disk_interval, cxx_name = "diskInterval")]
|
|
#[qproperty(i32, disk_warn_threshold, cxx_name = "diskWarnThreshold")]
|
|
// Battery.
|
|
#[qproperty(bool, battery_enable, cxx_name = "batteryEnable")]
|
|
#[qproperty(bool, battery_poweralertd, cxx_name = "batteryPoweralertd")]
|
|
#[qproperty(i32, battery_warning, cxx_name = "batteryWarning")]
|
|
#[qproperty(i32, battery_critical, cxx_name = "batteryCritical")]
|
|
// Lock screen.
|
|
#[qproperty(bool, lock_enable, cxx_name = "lockEnable")]
|
|
#[qproperty(bool, lock_mpris, cxx_name = "lockMpris")]
|
|
#[qproperty(bool, lock_notifications, cxx_name = "lockNotifications")]
|
|
#[qproperty(bool, lock_screenshot, cxx_name = "lockScreenshot")]
|
|
#[qproperty(bool, lock_threat_effect, cxx_name = "lockThreatEffect")]
|
|
#[qproperty(bool, lock_volume, cxx_name = "lockVolume")]
|
|
#[qproperty(bool, lock_weather, cxx_name = "lockWeather")]
|
|
// Dock.
|
|
#[qproperty(bool, dock_enable, cxx_name = "dockEnable")]
|
|
#[qproperty(i32, dock_width, cxx_name = "dockWidth")]
|
|
#[qproperty(bool, dock_applet_clock, cxx_name = "dockAppletClock")]
|
|
#[qproperty(bool, dock_applet_cpu, cxx_name = "dockAppletCpu")]
|
|
#[qproperty(bool, dock_applet_gpu, cxx_name = "dockAppletGpu")]
|
|
#[qproperty(bool, dock_applet_memory, cxx_name = "dockAppletMemory")]
|
|
#[qproperty(bool, dock_applet_temperature, cxx_name = "dockAppletTemperature")]
|
|
#[qproperty(bool, dock_applet_disk, cxx_name = "dockAppletDisk")]
|
|
#[qproperty(bool, dock_applet_battery, cxx_name = "dockAppletBattery")]
|
|
#[qproperty(bool, dock_applet_network, cxx_name = "dockAppletNetwork")]
|
|
#[qproperty(bool, dock_applet_bluetooth, cxx_name = "dockAppletBluetooth")]
|
|
#[qproperty(bool, dock_applet_volume, cxx_name = "dockAppletVolume")]
|
|
#[qproperty(bool, dock_applet_backlight, cxx_name = "dockAppletBacklight")]
|
|
#[qproperty(bool, dock_applet_weather, cxx_name = "dockAppletWeather")]
|
|
#[qproperty(bool, dock_applet_mpris, cxx_name = "dockAppletMpris")]
|
|
#[qproperty(bool, dock_applet_notifications, cxx_name = "dockAppletNotifications")]
|
|
#[qproperty(bool, dock_applet_power, cxx_name = "dockAppletPower")]
|
|
// Unified systemd bar module (covers local + nspawn containers, and later remotes).
|
|
#[qproperty(bool, systemd_enable, cxx_name = "systemdEnable")]
|
|
#[qproperty(i32, systemd_interval, cxx_name = "systemdInterval")]
|
|
type ModulesService = super::ModulesServiceRust;
|
|
}
|
|
|
|
impl cxx_qt::Initialize for ModulesService {}
|
|
}
|
|
|
|
// ── Pure Rust data layer ─────────────────────────────────────────────────────
|
|
// One struct per group, plus a top-level `ModulesData`. Defaults mirror the
|
|
// previous QML implementation, NOT serde's type defaults. `#[serde(default)]`
|
|
// on every leaf so partial JSON objects get filled in from `Default`.
|
|
|
|
mod data {
|
|
use super::Deserialize;
|
|
|
|
fn t() -> bool {
|
|
true
|
|
}
|
|
fn weather_args_default() -> Vec<String> {
|
|
vec!["--nerd".to_owned()]
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Plain {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
}
|
|
impl Default for Plain {
|
|
fn default() -> Self {
|
|
Self { enable: true }
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Notifications {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Notifications::d_timeout")]
|
|
pub timeout: i32,
|
|
#[serde(default = "Notifications::d_max_popups")]
|
|
pub max_popups: i32,
|
|
#[serde(default = "Notifications::d_max_visible")]
|
|
pub max_visible: i32,
|
|
#[serde(default = "Notifications::d_max_history")]
|
|
pub max_history: i32,
|
|
}
|
|
impl Notifications {
|
|
fn d_timeout() -> i32 {
|
|
3000
|
|
}
|
|
fn d_max_popups() -> i32 {
|
|
4
|
|
}
|
|
fn d_max_visible() -> i32 {
|
|
10
|
|
}
|
|
fn d_max_history() -> i32 {
|
|
-1
|
|
}
|
|
}
|
|
impl Default for Notifications {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
timeout: Self::d_timeout(),
|
|
max_popups: Self::d_max_popups(),
|
|
max_visible: Self::d_max_visible(),
|
|
max_history: Self::d_max_history(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Backlight {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Backlight::d_step")]
|
|
pub step: i32,
|
|
}
|
|
impl Backlight {
|
|
fn d_step() -> i32 {
|
|
5
|
|
}
|
|
}
|
|
impl Default for Backlight {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
step: Self::d_step(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Weather {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "weather_args_default")]
|
|
pub args: Vec<String>,
|
|
#[serde(default = "Weather::d_interval")]
|
|
pub interval: i32,
|
|
}
|
|
impl Weather {
|
|
fn d_interval() -> i32 {
|
|
3_600_000
|
|
}
|
|
}
|
|
impl Default for Weather {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
args: weather_args_default(),
|
|
interval: Self::d_interval(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Temperature {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Temperature::d_warm")]
|
|
pub warm: i32,
|
|
#[serde(default = "Temperature::d_hot")]
|
|
pub hot: i32,
|
|
#[serde(default)]
|
|
pub device: String,
|
|
}
|
|
impl Temperature {
|
|
fn d_warm() -> i32 {
|
|
80
|
|
}
|
|
fn d_hot() -> i32 {
|
|
90
|
|
}
|
|
}
|
|
impl Default for Temperature {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
warm: Self::d_warm(),
|
|
hot: Self::d_hot(),
|
|
device: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Gpu {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Gpu::d_warm")]
|
|
pub warm: i32,
|
|
#[serde(default = "Gpu::d_hot")]
|
|
pub hot: i32,
|
|
}
|
|
impl Gpu {
|
|
fn d_warm() -> i32 {
|
|
70
|
|
}
|
|
fn d_hot() -> i32 {
|
|
85
|
|
}
|
|
}
|
|
impl Default for Gpu {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
warm: Self::d_warm(),
|
|
hot: Self::d_hot(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Disk {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Disk::d_interval")]
|
|
pub interval: i32,
|
|
#[serde(default = "Disk::d_warn_threshold")]
|
|
pub warn_threshold: i32,
|
|
}
|
|
impl Disk {
|
|
fn d_interval() -> i32 {
|
|
30_000
|
|
}
|
|
fn d_warn_threshold() -> i32 {
|
|
85
|
|
}
|
|
}
|
|
impl Default for Disk {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
interval: Self::d_interval(),
|
|
warn_threshold: Self::d_warn_threshold(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Battery {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "t")]
|
|
pub poweralertd: bool,
|
|
#[serde(default = "Battery::d_warning")]
|
|
pub warning: i32,
|
|
#[serde(default = "Battery::d_critical")]
|
|
pub critical: i32,
|
|
}
|
|
impl Battery {
|
|
fn d_warning() -> i32 {
|
|
25
|
|
}
|
|
fn d_critical() -> i32 {
|
|
15
|
|
}
|
|
}
|
|
impl Default for Battery {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
poweralertd: true,
|
|
warning: Self::d_warning(),
|
|
critical: Self::d_critical(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Default)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Lock {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "t")]
|
|
pub mpris: bool,
|
|
#[serde(default = "t")]
|
|
pub notifications: bool,
|
|
#[serde(default = "t")]
|
|
pub screenshot: bool,
|
|
#[serde(default = "t")]
|
|
pub threat_effect: bool,
|
|
#[serde(default = "t")]
|
|
pub volume: bool,
|
|
#[serde(default = "t")]
|
|
pub weather: bool,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Default)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DockApplets {
|
|
#[serde(default = "t")]
|
|
pub clock: bool,
|
|
#[serde(default = "t")]
|
|
pub cpu: bool,
|
|
#[serde(default = "t")]
|
|
pub gpu: bool,
|
|
#[serde(default = "t")]
|
|
pub memory: bool,
|
|
#[serde(default = "t")]
|
|
pub temperature: bool,
|
|
#[serde(default = "t")]
|
|
pub disk: bool,
|
|
#[serde(default = "t")]
|
|
pub battery: bool,
|
|
#[serde(default = "t")]
|
|
pub network: bool,
|
|
#[serde(default = "t")]
|
|
pub bluetooth: bool,
|
|
#[serde(default = "t")]
|
|
pub volume: bool,
|
|
#[serde(default = "t")]
|
|
pub backlight: bool,
|
|
#[serde(default = "t")]
|
|
pub weather: bool,
|
|
#[serde(default = "t")]
|
|
pub mpris: bool,
|
|
#[serde(default = "t")]
|
|
pub notifications: bool,
|
|
#[serde(default = "t")]
|
|
pub power: bool,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Dock {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "Dock::d_width")]
|
|
pub width: i32,
|
|
#[serde(default)]
|
|
pub applets: DockApplets,
|
|
}
|
|
impl Dock {
|
|
fn d_width() -> i32 {
|
|
300
|
|
}
|
|
}
|
|
impl Default for Dock {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
width: Self::d_width(),
|
|
applets: DockApplets::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WithInterval {
|
|
#[serde(default = "t")]
|
|
pub enable: bool,
|
|
#[serde(default = "WithInterval::d_interval")]
|
|
pub interval: i32,
|
|
}
|
|
impl WithInterval {
|
|
fn d_interval() -> i32 {
|
|
15_000
|
|
}
|
|
}
|
|
impl Default for WithInterval {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable: true,
|
|
interval: Self::d_interval(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct StatsDaemon {
|
|
#[serde(default = "StatsDaemon::d_interval")]
|
|
pub interval: i32,
|
|
}
|
|
impl StatsDaemon {
|
|
fn d_interval() -> i32 {
|
|
-1
|
|
}
|
|
}
|
|
impl Default for StatsDaemon {
|
|
fn default() -> Self {
|
|
Self {
|
|
interval: Self::d_interval(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Default)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ModulesData {
|
|
#[serde(default)]
|
|
pub workspaces: Plain,
|
|
#[serde(default)]
|
|
pub tray: Plain,
|
|
#[serde(default)]
|
|
pub window_title: Plain,
|
|
#[serde(default)]
|
|
pub clock: Plain,
|
|
#[serde(default)]
|
|
pub mpris: Plain,
|
|
#[serde(default)]
|
|
pub volume: Plain,
|
|
#[serde(default)]
|
|
pub bluetooth: Plain,
|
|
#[serde(default)]
|
|
pub network: Plain,
|
|
#[serde(default)]
|
|
pub power_profile: Plain,
|
|
#[serde(default)]
|
|
pub idle_inhibitor: Plain,
|
|
#[serde(default)]
|
|
pub cpu: Plain,
|
|
#[serde(default)]
|
|
pub memory: Plain,
|
|
#[serde(default)]
|
|
pub privacy: Plain,
|
|
#[serde(default)]
|
|
pub screen_corners: Plain,
|
|
#[serde(default)]
|
|
pub power: Plain,
|
|
#[serde(default)]
|
|
pub background_overlay: Plain,
|
|
#[serde(default)]
|
|
pub overview_backdrop: Plain,
|
|
#[serde(default)]
|
|
pub notifications: Notifications,
|
|
#[serde(default)]
|
|
pub backlight: Backlight,
|
|
#[serde(default)]
|
|
pub weather: Weather,
|
|
#[serde(default)]
|
|
pub temperature: Temperature,
|
|
#[serde(default)]
|
|
pub gpu: Gpu,
|
|
#[serde(default)]
|
|
pub disk: Disk,
|
|
#[serde(default)]
|
|
pub battery: Battery,
|
|
#[serde(default)]
|
|
pub lock: Lock,
|
|
#[serde(default)]
|
|
pub dock: Dock,
|
|
#[serde(default)]
|
|
pub systemd: WithInterval,
|
|
#[serde(default)]
|
|
pub stats_daemon: StatsDaemon,
|
|
}
|
|
}
|
|
|
|
use data::ModulesData;
|
|
|
|
pub struct ModulesServiceRust {
|
|
stats_daemon_interval: i32,
|
|
workspaces_enable: bool,
|
|
tray_enable: bool,
|
|
window_title_enable: bool,
|
|
clock_enable: bool,
|
|
mpris_enable: bool,
|
|
volume_enable: bool,
|
|
bluetooth_enable: bool,
|
|
network_enable: bool,
|
|
power_profile_enable: bool,
|
|
idle_inhibitor_enable: bool,
|
|
cpu_enable: bool,
|
|
memory_enable: bool,
|
|
privacy_enable: bool,
|
|
screen_corners_enable: bool,
|
|
power_enable: bool,
|
|
background_overlay_enable: bool,
|
|
overview_backdrop_enable: bool,
|
|
notifications_enable: bool,
|
|
notifications_timeout: i32,
|
|
notifications_max_popups: i32,
|
|
notifications_max_visible: i32,
|
|
notifications_max_history: i32,
|
|
backlight_enable: bool,
|
|
backlight_step: i32,
|
|
weather_enable: bool,
|
|
weather_args: QList<QString>,
|
|
weather_interval: i32,
|
|
temperature_enable: bool,
|
|
temperature_warm: i32,
|
|
temperature_hot: i32,
|
|
temperature_device: QString,
|
|
gpu_enable: bool,
|
|
gpu_warm: i32,
|
|
gpu_hot: i32,
|
|
disk_enable: bool,
|
|
disk_interval: i32,
|
|
disk_warn_threshold: i32,
|
|
battery_enable: bool,
|
|
battery_poweralertd: bool,
|
|
battery_warning: i32,
|
|
battery_critical: i32,
|
|
lock_enable: bool,
|
|
lock_mpris: bool,
|
|
lock_notifications: bool,
|
|
lock_screenshot: bool,
|
|
lock_threat_effect: bool,
|
|
lock_volume: bool,
|
|
lock_weather: bool,
|
|
dock_enable: bool,
|
|
dock_width: i32,
|
|
dock_applet_clock: bool,
|
|
dock_applet_cpu: bool,
|
|
dock_applet_gpu: bool,
|
|
dock_applet_memory: bool,
|
|
dock_applet_temperature: bool,
|
|
dock_applet_disk: bool,
|
|
dock_applet_battery: bool,
|
|
dock_applet_network: bool,
|
|
dock_applet_bluetooth: bool,
|
|
dock_applet_volume: bool,
|
|
dock_applet_backlight: bool,
|
|
dock_applet_weather: bool,
|
|
dock_applet_mpris: bool,
|
|
dock_applet_notifications: bool,
|
|
dock_applet_power: bool,
|
|
systemd_enable: bool,
|
|
systemd_interval: i32,
|
|
}
|
|
|
|
impl Default for ModulesServiceRust {
|
|
fn default() -> Self {
|
|
Self::from_data(load_modules_data())
|
|
}
|
|
}
|
|
|
|
impl ModulesServiceRust {
|
|
fn from_data(d: ModulesData) -> Self {
|
|
let mut weather_args = QList::<QString>::default();
|
|
for s in &d.weather.args {
|
|
weather_args.append(QString::from(s.as_str()));
|
|
}
|
|
Self {
|
|
stats_daemon_interval: d.stats_daemon.interval,
|
|
workspaces_enable: d.workspaces.enable,
|
|
tray_enable: d.tray.enable,
|
|
window_title_enable: d.window_title.enable,
|
|
clock_enable: d.clock.enable,
|
|
mpris_enable: d.mpris.enable,
|
|
volume_enable: d.volume.enable,
|
|
bluetooth_enable: d.bluetooth.enable,
|
|
network_enable: d.network.enable,
|
|
power_profile_enable: d.power_profile.enable,
|
|
idle_inhibitor_enable: d.idle_inhibitor.enable,
|
|
cpu_enable: d.cpu.enable,
|
|
memory_enable: d.memory.enable,
|
|
privacy_enable: d.privacy.enable,
|
|
screen_corners_enable: d.screen_corners.enable,
|
|
power_enable: d.power.enable,
|
|
background_overlay_enable: d.background_overlay.enable,
|
|
overview_backdrop_enable: d.overview_backdrop.enable,
|
|
notifications_enable: d.notifications.enable,
|
|
notifications_timeout: d.notifications.timeout,
|
|
notifications_max_popups: d.notifications.max_popups,
|
|
notifications_max_visible: d.notifications.max_visible,
|
|
notifications_max_history: d.notifications.max_history,
|
|
backlight_enable: d.backlight.enable,
|
|
backlight_step: d.backlight.step,
|
|
weather_enable: d.weather.enable,
|
|
weather_args,
|
|
weather_interval: d.weather.interval,
|
|
temperature_enable: d.temperature.enable,
|
|
temperature_warm: d.temperature.warm,
|
|
temperature_hot: d.temperature.hot,
|
|
temperature_device: QString::from(d.temperature.device.as_str()),
|
|
gpu_enable: d.gpu.enable,
|
|
gpu_warm: d.gpu.warm,
|
|
gpu_hot: d.gpu.hot,
|
|
disk_enable: d.disk.enable,
|
|
disk_interval: d.disk.interval,
|
|
disk_warn_threshold: d.disk.warn_threshold,
|
|
battery_enable: d.battery.enable,
|
|
battery_poweralertd: d.battery.poweralertd,
|
|
battery_warning: d.battery.warning,
|
|
battery_critical: d.battery.critical,
|
|
lock_enable: d.lock.enable,
|
|
lock_mpris: d.lock.mpris,
|
|
lock_notifications: d.lock.notifications,
|
|
lock_screenshot: d.lock.screenshot,
|
|
lock_threat_effect: d.lock.threat_effect,
|
|
lock_volume: d.lock.volume,
|
|
lock_weather: d.lock.weather,
|
|
dock_enable: d.dock.enable,
|
|
dock_width: d.dock.width,
|
|
dock_applet_clock: d.dock.applets.clock,
|
|
dock_applet_cpu: d.dock.applets.cpu,
|
|
dock_applet_gpu: d.dock.applets.gpu,
|
|
dock_applet_memory: d.dock.applets.memory,
|
|
dock_applet_temperature: d.dock.applets.temperature,
|
|
dock_applet_disk: d.dock.applets.disk,
|
|
dock_applet_battery: d.dock.applets.battery,
|
|
dock_applet_network: d.dock.applets.network,
|
|
dock_applet_bluetooth: d.dock.applets.bluetooth,
|
|
dock_applet_volume: d.dock.applets.volume,
|
|
dock_applet_backlight: d.dock.applets.backlight,
|
|
dock_applet_weather: d.dock.applets.weather,
|
|
dock_applet_mpris: d.dock.applets.mpris,
|
|
dock_applet_notifications: d.dock.applets.notifications,
|
|
dock_applet_power: d.dock.applets.power,
|
|
systemd_enable: d.systemd.enable,
|
|
systemd_interval: d.systemd.interval,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn config_path(file: &str) -> PathBuf {
|
|
let base = dirs::config_dir().unwrap_or_else(|| {
|
|
// Treat a missing HOME/XDG as a user error, not a silent default; matches
|
|
// the rule that perm/format issues should surface immediately.
|
|
panic!("[nova-plugin] could not resolve $XDG_CONFIG_HOME or $HOME for config lookup");
|
|
});
|
|
base.join("nova-shell").join(file)
|
|
}
|
|
|
|
fn load_modules_data() -> ModulesData {
|
|
let path = config_path("modules.json");
|
|
let raw = match std::fs::read_to_string(&path) {
|
|
Ok(s) => s,
|
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
|
tracing::info!(
|
|
target: "nova_plugin::config",
|
|
"modules.json not found at {}, using defaults", path.display()
|
|
);
|
|
return ModulesData::default();
|
|
}
|
|
Err(e) => panic!("[nova-plugin] cannot read {}: {e}", path.display()),
|
|
};
|
|
parse_modules(&raw, &path)
|
|
}
|
|
|
|
fn parse_modules(raw: &str, path: &std::path::Path) -> ModulesData {
|
|
// First pass: catch unknown keys via a generic Value, warn but keep going.
|
|
if let Ok(serde_json::Value::Object(map)) = serde_json::from_str::<serde_json::Value>(raw) {
|
|
let known: &[&str] = &[
|
|
"workspaces",
|
|
"tray",
|
|
"windowTitle",
|
|
"clock",
|
|
"mpris",
|
|
"volume",
|
|
"bluetooth",
|
|
"network",
|
|
"powerProfile",
|
|
"idleInhibitor",
|
|
"cpu",
|
|
"memory",
|
|
"privacy",
|
|
"screenCorners",
|
|
"power",
|
|
"backgroundOverlay",
|
|
"overviewBackdrop",
|
|
"notifications",
|
|
"backlight",
|
|
"weather",
|
|
"temperature",
|
|
"gpu",
|
|
"disk",
|
|
"battery",
|
|
"lock",
|
|
"dock",
|
|
"systemd",
|
|
"statsDaemon",
|
|
];
|
|
for key in map.keys() {
|
|
if !known.contains(&key.as_str()) {
|
|
tracing::warn!(
|
|
target: "nova_plugin::config",
|
|
"{}: unknown top-level key `{key}` ignored", path.display()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
serde_json::from_str(raw)
|
|
.unwrap_or_else(|e| panic!("[nova-plugin] {}: malformed JSON: {e}", path.display()))
|
|
}
|
|
|
|
impl cxx_qt::Initialize for qobject::ModulesService {
|
|
fn initialize(self: Pin<&mut Self>) {
|
|
// Default-construct populates from disk; nothing else to do.
|
|
let _ = self;
|
|
}
|
|
}
|