_popen/_popen-style shell-outs flash a cmd.exe console window on Windows. Add
Platform::runHiddenCapture() — CreateProcess + CREATE_NO_WINDOW capturing stdout on
Windows, popen on POSIX — and route the remaining shell-outs through it:
- GPU-aware idle detection (getGpuUtilization: "where nvidia-smi" / "nvidia-smi --query-gpu")
- xmrig discovery + version (findXmrigBinary "where xmrig.exe"; "<bin> --version", stderr merged)
- wallet-rebuild helper (app_network) — keeps its exit-code check via the new exitCode out-param
None of these are on the launch path (that was the daemon spawn, fixed in a2f84be); each
would flash a console only when it ran (idle-GPU mining, mining tab, wallet recovery).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
201 lines
7.1 KiB
C++
201 lines
7.1 KiB
C++
// DragonX Wallet - ImGui Edition
|
||
// Copyright 2024-2026 The Hush Developers
|
||
// Released under the GPLv3
|
||
|
||
#pragma once
|
||
|
||
#include <string>
|
||
#include <cstdint>
|
||
|
||
namespace dragonx {
|
||
namespace util {
|
||
|
||
/**
|
||
* @brief Platform-specific utilities
|
||
*/
|
||
class Platform {
|
||
public:
|
||
/**
|
||
* @brief Open a URL in the default browser
|
||
* @param url The URL to open
|
||
* @return true if successful
|
||
*/
|
||
static bool openUrl(const std::string& url);
|
||
|
||
/**
|
||
* @brief Open a folder in the system file manager
|
||
* @param path Path to the folder
|
||
* @param createIfMissing Create the folder if it doesn't exist
|
||
* @return true if successful
|
||
*/
|
||
static bool openFolder(const std::string& path, bool createIfMissing = true);
|
||
|
||
/**
|
||
* @brief Get the size of a file in bytes
|
||
* @param path Path to the file
|
||
* @return File size in bytes, or 0 if file doesn't exist
|
||
*/
|
||
static uint64_t getFileSize(const std::string& path);
|
||
|
||
/**
|
||
* @brief Format a file size as human-readable string
|
||
* @param bytes Size in bytes
|
||
* @return Formatted string (e.g., "5.23 MB")
|
||
*/
|
||
static std::string formatFileSize(uint64_t bytes);
|
||
|
||
/**
|
||
* @brief Get the user's home directory
|
||
* @return Home directory path
|
||
*/
|
||
static std::string getHomeDir();
|
||
|
||
/**
|
||
* @brief Get the DragonX data directory
|
||
* @return Path like ~/.hush/DRAGONX/ or %APPDATA%\Hush\DRAGONX\
|
||
*/
|
||
static std::string getDragonXDataDir();
|
||
|
||
/**
|
||
* @brief Get the wallet data directory (alias for getDragonXDataDir)
|
||
* @return Path like ~/.hush/DRAGONX/ or %APPDATA%\Hush\DRAGONX\
|
||
*/
|
||
static std::string getDataDir();
|
||
|
||
/**
|
||
* @brief Get the LITE wallet data directory (where the SilentDragonXLite backend stores
|
||
* silentdragonxlite-wallet.dat). Mirrors the backend's get_zcash_data_path() for the
|
||
* "main" chain: dirs::data_dir()/silentdragonxlite on Windows/macOS, ~/.silentdragonxlite
|
||
* on Linux. Distinct from getDragonXDataDir() (the full-node blocks/wallet dir).
|
||
* @return Path like ~/.silentdragonxlite/ or %APPDATA%\silentdragonxlite\
|
||
*/
|
||
static std::string getLiteWalletDataDir();
|
||
|
||
/**
|
||
* @brief Get the config directory for storing wallet exports/backups
|
||
* @return Path like ~/.config/ObsidianDragon/ or %APPDATA%\ObsidianDragon\
|
||
*/
|
||
static std::string getConfigDir();
|
||
|
||
/**
|
||
* @brief Delete a file
|
||
* @param path Path to the file
|
||
* @return true if successful or file didn't exist
|
||
*/
|
||
static bool deleteFile(const std::string& path);
|
||
|
||
/**
|
||
* @brief Write a file atomically and durably.
|
||
*
|
||
* Writes @p content to `<path>.tmp`, flushes it to stable storage
|
||
* (fsync / FlushFileBuffers), then atomically renames it over @p path
|
||
* (POSIX rename / Win32 MoveFileEx with REPLACE_EXISTING). A crash or power
|
||
* loss at any point leaves either the old file intact or the fully-written
|
||
* new one — never a truncated/corrupt file. The parent directory is created
|
||
* if missing, and on POSIX it is fsync'd so the rename itself is durable.
|
||
*
|
||
* @param path Destination file path.
|
||
* @param content Bytes to write.
|
||
* @param restrictPermissions When true, create the file owner-only (0600 on
|
||
* POSIX) before the rename so it is never briefly world-readable.
|
||
* @return true on success; on failure any pre-existing file is left untouched.
|
||
*/
|
||
static bool writeFileAtomically(const std::string& path,
|
||
const std::string& content,
|
||
bool restrictPermissions = false);
|
||
|
||
/**
|
||
* @brief Get the directory containing the executable
|
||
* @return Path to executable's directory
|
||
*/
|
||
static std::string getExecutableDirectory();
|
||
|
||
/**
|
||
* @brief Get the ObsidianDragon config directory
|
||
* @return Path like ~/.config/ObsidianDragon/ or %APPDATA%\ObsidianDragon\
|
||
*/
|
||
static std::string getObsidianDragonDir();
|
||
|
||
/**
|
||
* @brief Create ObsidianDragon folder structure and template files on first run
|
||
*
|
||
* Creates:
|
||
* ObsidianDragon/
|
||
* ObsidianDragon/themes/
|
||
* ObsidianDragon/themes/my_theme.toml (template)
|
||
*
|
||
* Only writes template files if they don't already exist.
|
||
*/
|
||
static void ensureObsidianDragonSetup();
|
||
|
||
/**
|
||
* @brief Create a directory (and parents) if missing, with a clear error on failure.
|
||
*
|
||
* Uses the non-throwing std::error_code overload internally. On failure sets *outError
|
||
* (when non-null) to one consistent, user-facing message:
|
||
* "Cannot create <dir>: <reason>. Check permissions / free space."
|
||
*
|
||
* @return true if the directory exists (already did, or was just created).
|
||
*/
|
||
static bool ensureDirectory(const std::string& dir, std::string* outError = nullptr);
|
||
|
||
/**
|
||
* @brief Get total system RAM in megabytes
|
||
* @return Total physical RAM in MB, or 0 on failure
|
||
*/
|
||
static double getTotalSystemRAM_MB();
|
||
|
||
/**
|
||
* @brief Get currently used system RAM in megabytes
|
||
* @return Used physical RAM in MB (total - available), or 0 on failure
|
||
*/
|
||
static double getUsedSystemRAM_MB();
|
||
|
||
/**
|
||
* @brief Get this process's own RSS (resident set size) in megabytes
|
||
* @return Self process RSS in MB, or 0 on failure
|
||
*/
|
||
static double getSelfMemoryUsageMB();
|
||
|
||
/**
|
||
* @brief Get total RSS of all dragonxd daemon processes in megabytes
|
||
* Scans for any running dragonxd process by name, regardless of how it was launched.
|
||
* @return Combined daemon RSS in MB, or 0 if no daemon found
|
||
*/
|
||
static double getDaemonMemoryUsageMB();
|
||
|
||
/**
|
||
* @brief Get system-wide idle time in seconds
|
||
* Uses platform-specific APIs: GetLastInputInfo (Windows),
|
||
* XScreenSaverQueryInfo via dlopen (Linux), IOKit (macOS).
|
||
* @return Seconds since last user input, or 0 on failure
|
||
*/
|
||
static int getSystemIdleSeconds();
|
||
|
||
/**
|
||
* @brief Get GPU utilization percentage (0–100).
|
||
* Linux: reads sysfs for AMD, /proc for NVIDIA.
|
||
* Windows: queries PDH GPU engine counters.
|
||
* @return GPU busy percent, or -1 if unavailable.
|
||
*/
|
||
static int getGpuUtilization();
|
||
|
||
// Run a command line and capture its stdout WITHOUT ever popping a console window: Windows uses
|
||
// CreateProcess + CREATE_NO_WINDOW (a plain popen()/_popen() flashes a cmd.exe console), POSIX uses
|
||
// popen(). Use this instead of _popen for anything run while the GUI is up. `mergeStderr` folds the
|
||
// child's stderr into the result (like "2>&1"); otherwise stderr is discarded. `exitCode`, if given,
|
||
// receives the child's exit status (raw pclose() status on POSIX, GetExitCodeProcess on Windows; -1
|
||
// if the process could not be launched).
|
||
static std::string runHiddenCapture(const std::string& cmdLine, bool mergeStderr = false,
|
||
int* exitCode = nullptr);
|
||
};
|
||
|
||
/**
|
||
* @brief Get the directory containing the executable (free function)
|
||
* @return Path to executable's directory
|
||
*/
|
||
std::string getExecutableDirectory();
|
||
|
||
} // namespace util
|
||
} // namespace dragonx
|