Desktop widget engine
Argos
A native Win32 and Direct2D desktop widget engine, in the spirit of Rainmeter: transparent, always-on-top windows that draw live system data like a clock, CPU, RAM and disk usage, configured through a plain-text skin format. Components 1 through 3, the rendering engine, the skin format, and the four bundled v1 skins, are merged into main behind CI. Component 4, the manager application, is in progress on its own branch: a skin registry that scans a directory and merges rescans, and a manager window that lists skins and hit-tests clicks, reusing the same Direct2D rendering path the widgets themselves use. Two components remain as plan, and this page still says exactly that rather than rounding up.
Components 1-3 of 6 merged to main; component 4 (manager app) in progress on its own branch
- of 6 components with real code, 3 merged to main
- 4
- third-party dependencies beyond the Windows SDK
- 0
The problem
Widget engines in this space have a habit of reaching for process injection or shell hooks to do interesting things with the desktop, which is also exactly the kind of behaviour that gets a small unsigned tool flagged as malware and makes it fragile against the next Windows update. The design draws a hard line against that before any code exists: every widget is Argos's own transparent, layered window, drawn with documented Win32 and Direct2D APIs, and the engine never touches, injects into, or patches explorer.exe or any other process, for any feature, ever.
The other constraint is honesty about scope for a v1. No plugin system, no scripting language in the skin format, no property editor, no code signing. Four bundled sample skins prove the mechanism end to end rather than a wide library that would take attention away from getting the engine itself right.
How it works
Each widget is a layered, borderless, always-on-top window with per-pixel alpha transparency, drawn through Direct2D and DirectWrite rather than Win32 Common Controls, so the widgets and the manager application share exactly one rendering technology and one visual style.
Drag-to-reposition is built into the window engine itself rather than bolted on later, specifically so the manager application's own reposition feature can reuse it directly instead of implementing dragging a second time.
A widget's position is stored as a monitor plus an offset from that monitor's origin, not a raw virtual-desktop pixel coordinate, so a saved layout still makes sense after monitors get rearranged. Per-monitor DPI awareness is handled by rescaling the render target and repositioning into the rect Windows itself suggests on a DPI change.
The skin format is plain-text and INI-style, conceptually similar to Rainmeter's Measure and Meter split without being byte-compatible with Rainmeter's own syntax. A skin that fails to parse is logged and shown in the manager as failed with a reason, never a crash of the whole app.
Zero third-party dependencies beyond the Windows SDK. The skin format and the persisted application state both reuse the same hand-written INI parser rather than pulling in a second text format for one or the other.
Component 4 pulls the shared rendering path out into a D2DWindow base class, so the manager window that lists and manages skins draws through the exact same Direct2D setup the widgets themselves use, rather than reaching for a second UI toolkit for the one application window that isn't a widget.
Where it landed
Components 1 through 3 are merged into main behind CI: the rendering and window engine (layered, per-pixel-alpha windows with drag-to-reposition and per-monitor DPI rescaling), the skin format with its parser and first four measures and two meters, and four bundled v1 skins, Clock, CPU, RAM and disk usage, that exercise the format end to end rather than just the demo skin used during development.
Component 4, the manager application, is in progress on its own branch: a skin registry that scans a directory and merges rescans without losing state, and a manager window that renders the skin list and hit-tests clicks, built on the new shared D2DWindow base class. Two components remain, each still one GitHub issue, one branch, one PR, gated on the same Windows CI build as the rest.
What it taught me
A non-goals list is a design decision, not a placeholder
Writing down what v1 explicitly will not do, no plugin system, no shell modification, no code signing, did more to shape the actual engine architecture than the feature list did. Ruling out process injection up front is what made every later choice, like drawing widgets as ordinary layered windows, the obvious one instead of a corner cut later under pressure.
A helper hidden in an anonymous namespace is a helper nobody else can use
Measure.cpp's UTF-8-to-UTF-16 conversion was correct but scoped to its own translation unit, so the one other place that needed it, the skin-load error path, reinvented a version that mishandled non-ASCII text instead of calling the real one. Moving it into the shared namespace fixed that and surfaced a second bug for free: the original never guarded against the Windows API call returning zero, which would have underflowed a length calculation into a crash.
Built with
- C++17
- Win32
- Direct2D
- DirectWrite
- CMake
- Inno Setup