# ObsidianDragon — Architecture Guide A native C++17 desktop wallet for DragonX (DRGX), built with ImGui + SDL3. ## Directory Layout ``` src/ ├── main.cpp Entry point, SDL3/backend init, render loop ├── app.h / app.cpp App class: init, shutdown, frame dispatch ├── app_network.cpp RPC connection, data refresh, mining ops ├── app_security.cpp Encryption, lock screen, PIN management ├── app_wizard.cpp First-run setup wizard ├── config/ │ └── settings.h/.cpp JSON settings persistence (~/.config/ObsidianDragon/) ├── daemon/ │ ├── embedded_daemon.h/.cpp dragonxd process lifecycle │ └── xmrig_manager.h/.cpp xmrig-hac pool mining management ├── rpc/ │ ├── rpc_client.h/.cpp JSON-RPC over HTTPS (libcurl) │ ├── rpc_worker.h/.cpp Background work queue + main-thread drain │ ├── connection.h/.cpp DRAGONX.conf auto-detection │ └── types.h Transaction, address data types ├── ui/ │ ├── sidebar.h NavPage enum, sidebar rendering │ ├── notifications.h/.cpp Toast notification system │ ├── schema/ │ │ ├── ui_schema.h/.cpp UISchema singleton (TOML → typed lookups) │ │ ├── skin_manager.h/.cpp Skin enumeration, switching, import │ │ └── color_var_resolver.h/.cpp CSS-style color resolution │ └── windows/ Per-tab render functions (console, settings, etc.) ├── util/ │ ├── i18n.h/.cpp Internationalization (TR() macro, I18n singleton) │ ├── platform.h/.cpp Cross-platform utilities (paths, URLs) │ └── logger.h DEBUG_LOGF / VERBOSE_LOGF macros ├── platform/ Windows-specific (DX11 context, backdrop effects) ├── embedded/ Build-generated headers (embedded resources) └── resources/ Runtime resource loading (fonts, images) ``` ## Threading Model ``` ┌─────────────────────────────────────────────────────────────┐ │ Main Thread (UI) │ │ • ImGui render loop (App::update()) │ │ • drainResults() each frame → executes completed callbacks │ │ • NEVER does blocking I/O │ └────────────┬────────────────────────┬───────────────────────┘ │ post() │ post() ▼ ▼ ┌────────────────────┐ ┌────────────────────────────────┐ │ worker_ (RPCWorker)│ │ fast_worker_ (RPCWorker) │ │ General RPC polling│ │ Console commands, hashrate │ │ refreshData() 5s │ │ Avoids head-of-line blocking │ └────────────────────┘ └────────────────────────────────┘ ┌────────────────────────────────────────────┐ │ Monitor Threads (per child process) │ │ • EmbeddedDaemon: reads dragonxd stdout │ │ • XmrigManager: reads xmrig-hac stdout │ └────────────────────────────────────────────┘ ``` ### Work Submission Pattern ```cpp worker_->post([this]() -> rpc::RPCWorker::MainCb { json result = rpc_->call("getinfo"); // Runs on worker thread return [this, result]() { state_.connected = true; // Runs on main thread }; }); ``` ## RPC Architecture **Dual-client design** avoids head-of-line blocking: | Client | Worker | Purpose | |--------|--------|---------| | `rpc_` | `worker_` | Main polling: balance, transactions, sync status | | `fast_rpc_` | `fast_worker_` | Console commands, mining stats | Both are `RPCClient` instances wrapping libcurl for JSON-RPC over HTTPS to `dragonxd`. **Error flow:** `RPCClient::call()` throws `std::runtime_error` → caught in worker lambda → error string captured → `MainCb` calls `Notifications::instance().error(msg)` on main thread. ## Connection Lifecycle ``` [Disconnected] → tryConnect() every 5s → auto-detect DRAGONX.conf (host, port, rpcuser, rpcpassword) → if no config: start embedded daemon → retry → post async rpc_->connect() to worker_ → success: onConnected() → state_.connected = true → auth 401: try .cookie auth → retry → failure: onDisconnected(reason) → may restart daemon ``` ## UI System ### Page Routing `NavPage` enum defines all pages. `App::update()` dispatches via switch on `current_page_` to render functions (e.g., `RenderBalanceTab(this)`). ### Theme System - Layout defined in `res/themes/ui.toml`, accessed via `schema::UI()` - Skins are TOML files in `res/themes/` defining color palettes - `scripts/expand_themes.py` merges layout into skins at build time - Colors support CSS-style variables resolved by `ColorVarResolver` - Hot-reload: `UISchema::pollForChanges()` watches file mtime ### Internationalization - All UI strings use `TR("key")` macro → `I18n` singleton lookup - Language files: `res/lang/{locale}.json` with compiled-in fallbacks - Supported: en, es, zh, ru, de, fr, pt, ja, ko ### Notifications ```cpp Notifications::instance().success("Transaction sent"); Notifications::instance().error("Insufficient funds"); ``` Toast-style popups, bottom-right corner, 5-second default duration. ## Build System CMake 3.20+, C++17. FetchContent for SDL3, nlohmann/json, toml++. Bundled: ImGui, GLAD, miniz, libsodium, qrcode. ```bash ./build.sh # Dev build ./build.sh --mac-release # macOS universal .app + DMG ./build.sh --linux-release # Linux AppImage + zip ./build.sh --win-release # Windows cross-compile (MinGW) ``` ### Platform Backends | Platform | Renderer | Notes | |----------|----------|-------| | Windows | DirectX 11 | `imgui_impl_dx11`, backdrop effects | | macOS | OpenGL 3 | Universal binary (arm64+x86_64), target 11.0+ | | Linux | OpenGL 3 | GLAD loader | ## Key Conventions 1. **Never block the main thread** — all I/O goes through `worker_->post()` 2. **Never hardcode pixel sizes** — use `schema::UI()` lookups from `ui.toml` 3. **Always use `TR()` for strings** — no hardcoded English in UI code 4. **Use `ICON_MD_*` for icons** — Material Design icon font 5. **`std::unique_ptr` for ownership** — no raw `new`/`delete` 6. **Check the right connection state** — `state_.connected` vs `rpc_->isConnected()`