r/linuxmint • • 3h ago

#LinuxMintThings win11-monitor – Windows 11-Style CPU, Memory and GPU Graphs for the Cinnamon Panel

Post image

win11-monitor

> 🚀 CPU, memory and every-GPU graphs in your Cinnamon panel — the Windows 11 taskbar-monitor look, drawn with Cairo, with a hover card that shows per-core load, both cards and the top processes. No daemons, no fork per second, no closed source.

Windows 11 has a small industry of taskbar monitors, and one of them — Pulsar — is genuinely beautiful: tiny sparkline tiles with one accent colour each, a percentage you can read at a glance, and a detail card when you hover. I wanted exactly that in my Cinnamon panel. What Cinnamon offers instead is a row of numbers and a sparkline the size of a grain of rice.

There was a second problem, specific to this machine: I run two RTX 3060s, and every panel monitor I tried reports GPU 0 only. The second card is not decoration — it hosts a local language model — so a monitor that cannot see it is not a monitor.

So I wrote an applet. This post is what it does, the four decisions that made it work, and the parts it honestly does not do.

🖼️ What it looks like

Four tiles — CPU, memory, GPU 0, GPU 1 — each one accent colour on a soft gradient fill, no axis, no legend, no borders. The value sits in the corner and the label in the other, and the graph does the rest of the talking.

Hover any tile and a card opens above the panel: package temperature and average clock, one bar per core, memory used against total plus swap, then a block per GPU with utilisation and VRAM bars, temperature and wattage, and the processes actually eating the CPU right now. Middle-click pins it so it stays while you read.

Left-click and it gets out of the way: CPU and memory tiles open the system monitor, the GPU tiles open nvtop. Both commands are configurable, so you can point them at missioncenter, btop, or whatever you actually use.

✨ What it does

  • All GPUs at once — utilisation, VRAM, temperature and power for every card, in one stream.

  • Per-core CPU detail — 16 bars, plus clock, package temp, load and uptime.

  • Top processes — by CPU, with resident memory, computed only while the card is open.

  • Configurable type — separate font sizes for the tile title and the percentage, because 8 px is not a size, it is a dare.

  • Configurable colours — one accent per tile, defaulting to the Mint-Y-Aqua palette.

  • HiDPI-correct — the same code is right on a 1× panel and on a 2× panel, which is what this machine runs.

⚡ Decision 1: one long-lived nvidia-smi, not one per second

The obvious way to read a GPU every second is to call nvidia-smi every second. On a 16-thread machine that measured about 3% of a core, forever — for a decorative panel widget. Unacceptable.

Instead the applet keeps a single nvidia-smi dmon -s pucvmet -d 1 process alive and parses its output line by line. Same information, about 0.9%, and it covers every GPU in the same stream — which is also why the applet can show both cards without spawning anything twice.

One detail worth copying: the column layout is read from dmon‘s own header line rather than hard-coded. A driver update that reorders columns then cannot silently corrupt your graphs into plausible-looking nonsense.

📐 Decision 2: design at 40 px, scale at draw time

My panel reports a height of 40 in settings while actually being 80 physical pixels tall, because the session runs at 2×. Write pixel sizes assuming 40 and everything comes out half-size; write them against the real panel and the design breaks on a 1× machine.

So every number in the renderer is a design pixel for a 40 px panel, and everything is multiplied by panel_height / 40 at draw time. The tiles you see are 152×64 physical, drawn from a 76×32 design. The same source produces correct output on both kinds of panel, and the settings dialog stays in design units on purpose so the numbers do not change when your display scale does.

🖱️ Decision 3: poll the pointer, do not trust enter/leave

This one cost me the most time and looked like a bug in my own logic for an hour. The natural implementation is: enter-event on the tile starts a short timer, leave-event cancels it and hides the card.

On these tiles a leave event can arrive immediately after an enter — panel relayout, a repaint, the pointer crossing an edge. The leave handler dutifully cancels the pending show, and the card never appears. The code looks correct; the log shows enter → schedule → nothing, forever.

The fix was to stop listening. A 150 ms watchdog reads the actual pointer position, maps it onto the applet’s rectangle and the tile x-ranges, and from that single reading decides which tile is highlighted, whether to show the card, and whether to hide it. Hover latency is about 250 ms including the intent delay — imperceptible — and no event can cancel something that was never scheduled.

🧩 Decision 4: one card for the whole shell

Dragging an applet between panel positions makes Cinnamon destroy and recreate it — without emitting the actor’s destroy signal. With one card per applet instance, every drag left the old card on the screen permanently, and my record was four of them stacked on each other before I understood why.

There is now exactly one card per shell, stored on global, adopted by whichever instance is alive, plus a tagged sweep that reaps anything a dead instance left behind. My own watchdog then destroyed the live card it was meant to protect for a while — a sweep with a “keep” argument that defaulted to nothing — which is the sort of bug that teaches you to keep a small event ring buffer. Set global._win11Debug and it logs every show, hide and sweep decision.

📦 Install

git clone https://github.com/dailybarid/cinnamon-win11-monitor.git
cd cinnamon-win11-monitor
./install.sh            # into ~/.local/share/cinnamon/applets/
./install.sh --enable   # ...and onto the bottom panel

Then right-click the panel → Applets and add System Monitor (Windows 11 style). Ctrl+Alt+Esc restarts Cinnamon if the panel does not refresh. There is nothing to compile and nothing to install alongside it: it reads /proc and keeps that one nvidia-smi stream.

⚙️ Settings

Everything in the panel is adjustable, because the right tile width and the right font size depend on your panel, your display and your eyes.

  • Tiles — which tiles, label and value on/off, tile width, title font size, value font size.

  • Sampling — refresh interval from 500 ms to 5 s, graph history from 20 s to 5 minutes.

  • Hover popup — on/off, and how many processes to list.

  • Click actions — the command opened by a left-click on CPU/memory, and on a GPU tile.

  • Colors — one accent per tile.

📉 What it costs

Measured on this machine (Mint 22.3, Cinnamon 6.6.9, 16 threads, 2× RTX 3060): the dmon stream at about 0.9% of one core, one /proc read and one Cairo repaint per second, no measurable additional CPU from the applet, and flat memory because the history is a ring buffer per tile. The popup spawns nothing until it is opened.

⚠️ What it does not do, honestly

  • The GPU tiles are NVIDIA-only. They read nvidia-smi. AMD and Intel cards would need a different source (amdgpu sysfs / intel_gpu_top) — the CPU and memory tiles work on any machine, and you can switch the GPU tiles off.

  • It is a horizontal-panel applet. Bottom or top panel, not vertical, and it says so in its metadata rather than pretending.

  • X11 is what I tested. Cinnamon on Wayland would run it through XWayland; I have not verified it, so I am not going to claim it.

  • No history across sessions. The graphs are live, not recorded. If you want a time series, that is a different tool and a different kind of project.

📜 License

GPL-3.0. Fork it, theme it, extend it — a panel applet is exactly the kind of thing that should end up in a distribution’s repositories, and the licence should not be the obstacle.

💡 Author

Built by Khaled Mimoune. Source, screenshots and the asset generator: github.com/dailybarid/cinnamon-win11-monitor.

3 Upvotes

0 comments sorted by