Compare commits
3 commits
8fdd9692e6
...
c96b023fbe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c96b023fbe | ||
|
|
937ae5af2e | ||
|
|
c8d71bd871 |
8 changed files with 71 additions and 39 deletions
|
|
@ -54,6 +54,7 @@ PanelWindow {
|
||||||
text: ":"
|
text: ":"
|
||||||
color: colon._colors[colon._colorIdx % colon._colors.length]
|
color: colon._colors[colon._colorIdx % colon._colors.length]
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
enabled: !M.Theme.reducedMotion
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 800
|
duration: 800
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +111,7 @@ PanelWindow {
|
||||||
readonly property var _colors: [M.Theme.base08, M.Theme.base09, M.Theme.base0A, M.Theme.base0B, M.Theme.base0C, M.Theme.base0D, M.Theme.base0E, M.Theme.base05]
|
readonly property var _colors: [M.Theme.base08, M.Theme.base09, M.Theme.base0A, M.Theme.base0B, M.Theme.base0C, M.Theme.base0D, M.Theme.base0E, M.Theme.base05]
|
||||||
color: _colors[_colorIdx % _colors.length]
|
color: _colors[_colorIdx % _colors.length]
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
enabled: !M.Theme.reducedMotion
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 800
|
duration: 800
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +119,7 @@ PanelWindow {
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
running: true
|
running: !M.Theme.reducedMotion
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: colon
|
target: colon
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
|
|
@ -187,6 +189,7 @@ PanelWindow {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
color: colon._colors[colon._colorIdx % colon._colors.length]
|
color: colon._colors[colon._colorIdx % colon._colors.length]
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
enabled: !M.Theme.reducedMotion
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 800
|
duration: 800
|
||||||
}
|
}
|
||||||
|
|
@ -195,6 +198,7 @@ PanelWindow {
|
||||||
opacity: 0.6
|
opacity: 0.6
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
|
enabled: !M.Theme.reducedMotion
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 300
|
duration: 300
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ M.BarSection {
|
||||||
|
|
||||||
property int usage: M.SystemStats.cpuUsage
|
property int usage: M.SystemStats.cpuUsage
|
||||||
Behavior on usage {
|
Behavior on usage {
|
||||||
|
enabled: root._showPanel
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 400
|
duration: 400
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
|
|
@ -16,15 +17,14 @@ M.BarSection {
|
||||||
|
|
||||||
property real freqGhz: M.SystemStats.cpuFreqGhz
|
property real freqGhz: M.SystemStats.cpuFreqGhz
|
||||||
Behavior on freqGhz {
|
Behavior on freqGhz {
|
||||||
|
enabled: root._showPanel
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 400
|
duration: 400
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var _coreUsage: M.SystemStats.cpuCoreUsage
|
readonly property var _cores: M.SystemStats.cpuCores
|
||||||
readonly property var _coreFreq: M.SystemStats.cpuCoreFreq
|
|
||||||
readonly property var _coreHistory: M.SystemStats.cpuCoreHistory
|
|
||||||
readonly property var _coreMaxFreq: M.SystemStats.cpuCoreMaxFreq
|
readonly property var _coreMaxFreq: M.SystemStats.cpuCoreMaxFreq
|
||||||
readonly property var _coreTypes: M.SystemStats.cpuCoreTypes
|
readonly property var _coreTypes: M.SystemStats.cpuCoreTypes
|
||||||
|
|
||||||
|
|
@ -114,14 +114,14 @@ M.BarSection {
|
||||||
|
|
||||||
// Per-core rows
|
// Per-core rows
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root._coreUsage.length
|
model: root._cores.length
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
required property int index
|
required property int index
|
||||||
width: hoverPanel.contentWidth
|
width: hoverPanel.contentWidth
|
||||||
|
|
||||||
readonly property int _u: root._coreUsage[index] ?? 0
|
readonly property int _u: root._cores[index]?.usage ?? 0
|
||||||
readonly property real _f: root._coreFreq[index] ?? 0
|
readonly property real _f: root._cores[index]?.freq_ghz ?? 0
|
||||||
readonly property color _barColor: root._loadColor(_u)
|
readonly property color _barColor: root._loadColor(_u)
|
||||||
readonly property bool _throttled: {
|
readonly property bool _throttled: {
|
||||||
const maxF = root._coreMaxFreq[index] ?? 0;
|
const maxF = root._coreMaxFreq[index] ?? 0;
|
||||||
|
|
@ -204,7 +204,7 @@ M.BarSection {
|
||||||
width: 32
|
width: 32
|
||||||
height: 10
|
height: 10
|
||||||
|
|
||||||
property var _hist: root._coreHistory[parent.parent.index] || []
|
property var _hist: root._cores[parent.parent.index]?.history ?? []
|
||||||
property color _col: parent.parent._barColor
|
property color _col: parent.parent._barColor
|
||||||
|
|
||||||
on_HistChanged: if (root._showPanel)
|
on_HistChanged: if (root._showPanel)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ QtObject {
|
||||||
property var overviewBackdrop: ({
|
property var overviewBackdrop: ({
|
||||||
enable: true
|
enable: true
|
||||||
})
|
})
|
||||||
|
property var statsDaemon: ({
|
||||||
|
interval: -1
|
||||||
|
})
|
||||||
|
|
||||||
property FileView _file: FileView {
|
property FileView _file: FileView {
|
||||||
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/modules.json"
|
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/modules.json"
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ PanelWindow {
|
||||||
// Wave animation: 6s sweep + 8s pause, only while overview is open
|
// Wave animation: 6s sweep + 8s pause, only while overview is open
|
||||||
SequentialAnimation on uWavePhase {
|
SequentialAnimation on uWavePhase {
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
running: M.NiriIpc.overviewOpen
|
running: M.NiriIpc.overviewOpen && !M.Theme.reducedMotion
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
from: -200
|
from: -200
|
||||||
to: fx.width + 200
|
to: fx.width + 200
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@ QtObject {
|
||||||
// ── CPU ──────────────────────────────────────────────────────────────
|
// ── CPU ──────────────────────────────────────────────────────────────
|
||||||
property int cpuUsage: 0
|
property int cpuUsage: 0
|
||||||
property real cpuFreqGhz: 0
|
property real cpuFreqGhz: 0
|
||||||
property var cpuCoreUsage: []
|
property var cpuCores: [] // [{usage, freq_ghz, history:[]}]
|
||||||
property var cpuCoreFreq: []
|
|
||||||
property var cpuCoreHistory: []
|
|
||||||
property var cpuCoreMaxFreq: []
|
property var cpuCoreMaxFreq: []
|
||||||
property var cpuCoreTypes: []
|
property var cpuCoreTypes: []
|
||||||
|
|
||||||
|
|
@ -31,7 +29,10 @@ QtObject {
|
||||||
// nova-stats stream (cpu + mem)
|
// nova-stats stream (cpu + mem)
|
||||||
property var _statsProc: Process {
|
property var _statsProc: Process {
|
||||||
running: true
|
running: true
|
||||||
command: ["nova-stats"]
|
command: {
|
||||||
|
const ms = M.Modules.statsDaemon.interval;
|
||||||
|
return ms > 0 ? ["nova-stats", "--interval", ms.toString()] : ["nova-stats"];
|
||||||
|
}
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
splitMarker: "\n"
|
splitMarker: "\n"
|
||||||
onRead: line => {
|
onRead: line => {
|
||||||
|
|
@ -40,17 +41,17 @@ QtObject {
|
||||||
if (ev.type === "cpu") {
|
if (ev.type === "cpu") {
|
||||||
root.cpuUsage = ev.usage;
|
root.cpuUsage = ev.usage;
|
||||||
root.cpuFreqGhz = ev.freq_ghz;
|
root.cpuFreqGhz = ev.freq_ghz;
|
||||||
root.cpuCoreUsage = ev.core_usage;
|
|
||||||
root.cpuCoreFreq = ev.core_freq_ghz;
|
|
||||||
const histLen = 16;
|
const histLen = 16;
|
||||||
const oldH = root.cpuCoreHistory;
|
const prev = root.cpuCores;
|
||||||
const newH = [];
|
root.cpuCores = ev.cores.map((c, i) => {
|
||||||
for (let i = 0; i < ev.core_usage.length; i++) {
|
const oldHist = prev[i]?.history ?? [];
|
||||||
const prev = i < oldH.length ? oldH[i] : [];
|
const hist = oldHist.concat([c.usage]);
|
||||||
const next = prev.concat([ev.core_usage[i]]);
|
return {
|
||||||
newH.push(next.length > histLen ? next.slice(next.length - histLen) : next);
|
usage: c.usage,
|
||||||
}
|
freq_ghz: c.freq_ghz,
|
||||||
root.cpuCoreHistory = newH;
|
history: hist.length > histLen ? hist.slice(hist.length - histLen) : hist
|
||||||
|
};
|
||||||
|
});
|
||||||
} else if (ev.type === "mem") {
|
} else if (ev.type === "mem") {
|
||||||
root.memPercent = ev.percent;
|
root.memPercent = ev.percent;
|
||||||
root.memUsedGb = ev.used_gb;
|
root.memUsedGb = ev.used_gb;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ QtObject {
|
||||||
property int groupSpacing: 6
|
property int groupSpacing: 6
|
||||||
property int radius: 4
|
property int radius: 4
|
||||||
property int screenRadius: 15
|
property int screenRadius: 15
|
||||||
|
property bool reducedMotion: false
|
||||||
|
|
||||||
property FileView _themeFile: FileView {
|
property FileView _themeFile: FileView {
|
||||||
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/theme.json"
|
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/theme.json"
|
||||||
|
|
@ -75,5 +76,7 @@ QtObject {
|
||||||
root.radius = data.radius;
|
root.radius = data.radius;
|
||||||
if (data.screenRadius !== undefined)
|
if (data.screenRadius !== undefined)
|
||||||
root.screenRadius = data.screenRadius;
|
root.screenRadius = data.screenRadius;
|
||||||
|
if (data.reducedMotion !== undefined)
|
||||||
|
root.reducedMotion = data.reducedMotion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,17 @@ in
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
statsDaemon = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
description = "Configuration for the nova-stats daemon.";
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options.interval = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = -1;
|
||||||
|
description = "nova-stats polling interval in milliseconds (-1 = use binary default of 1s).";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
theme = lib.mkOption {
|
theme = lib.mkOption {
|
||||||
|
|
|
||||||
|
|
@ -132,22 +132,18 @@ fn emit_cpu(out: &mut impl Write, prev: &[Sample], curr: &[Sample], freqs: &[f64
|
||||||
freqs.iter().sum::<f64>() / freqs.len() as f64
|
freqs.iter().sum::<f64>() / freqs.len() as f64
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let n = core_usage.len().max(freqs.len());
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
out,
|
out,
|
||||||
"{{\"type\":\"cpu\",\"usage\":{usage},\"freq_ghz\":{avg_freq:.3},\"core_usage\":["
|
"{{\"type\":\"cpu\",\"usage\":{usage},\"freq_ghz\":{avg_freq:.3},\"cores\":["
|
||||||
);
|
);
|
||||||
for (i, u) in core_usage.iter().enumerate() {
|
for i in 0..n {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
let _ = write!(out, ",");
|
let _ = write!(out, ",");
|
||||||
}
|
}
|
||||||
let _ = write!(out, "{u}");
|
let u = core_usage.get(i).copied().unwrap_or(0);
|
||||||
}
|
let f = freqs.get(i).copied().unwrap_or(0.0);
|
||||||
let _ = write!(out, "],\"core_freq_ghz\":[");
|
let _ = write!(out, "{{\"usage\":{u},\"freq_ghz\":{f:.3}}}");
|
||||||
for (i, f) in freqs.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
let _ = write!(out, ",");
|
|
||||||
}
|
|
||||||
let _ = write!(out, "{f:.3}");
|
|
||||||
}
|
}
|
||||||
let _ = writeln!(out, "]}}");
|
let _ = writeln!(out, "]}}");
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +159,22 @@ fn emit_mem(out: &mut impl Write) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_interval_ms() -> u64 {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let mut i = 1;
|
||||||
|
while i < args.len() {
|
||||||
|
if args[i] == "--interval" {
|
||||||
|
if let Some(ms) = args.get(i + 1).and_then(|s| s.parse().ok()) {
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
1000
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let interval = Duration::from_millis(parse_interval_ms());
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
let mut out = io::BufWriter::new(stdout.lock());
|
let mut out = io::BufWriter::new(stdout.lock());
|
||||||
let mut prev: Vec<Sample> = vec![];
|
let mut prev: Vec<Sample> = vec![];
|
||||||
|
|
@ -185,8 +196,8 @@ fn main() {
|
||||||
tick += 1;
|
tick += 1;
|
||||||
|
|
||||||
let elapsed = t0.elapsed();
|
let elapsed = t0.elapsed();
|
||||||
if elapsed < Duration::from_secs(1) {
|
if elapsed < interval {
|
||||||
thread::sleep(Duration::from_secs(1) - elapsed);
|
thread::sleep(interval - elapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -360,8 +371,7 @@ SwapFree: 8192000 kB";
|
||||||
assert!(s.contains("\"type\":\"cpu\""));
|
assert!(s.contains("\"type\":\"cpu\""));
|
||||||
assert!(s.contains("\"usage\":"));
|
assert!(s.contains("\"usage\":"));
|
||||||
assert!(s.contains("\"freq_ghz\":"));
|
assert!(s.contains("\"freq_ghz\":"));
|
||||||
assert!(s.contains("\"core_usage\":"));
|
assert!(s.contains("\"cores\":"));
|
||||||
assert!(s.contains("\"core_freq_ghz\":"));
|
|
||||||
assert!(s.trim().ends_with('}'));
|
assert!(s.trim().ends_with('}'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,8 +409,8 @@ SwapFree: 8192000 kB";
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
emit_cpu(&mut buf, &curr, &curr, &freqs);
|
emit_cpu(&mut buf, &curr, &curr, &freqs);
|
||||||
let s = String::from_utf8(buf).unwrap();
|
let s = String::from_utf8(buf).unwrap();
|
||||||
assert!(s.contains("3.200"), "got: {s}");
|
assert!(s.contains("\"freq_ghz\":3.200"), "got: {s}");
|
||||||
assert!(s.contains("2.900"), "got: {s}");
|
assert!(s.contains("\"freq_ghz\":2.900"), "got: {s}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── emit_mem (via parse_meminfo) ─────────────────────────────────────
|
// ── emit_mem (via parse_meminfo) ─────────────────────────────────────
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue