feat: Full UI internationalization, pool hashrate stats, and layout caching
- Replace all hardcoded English strings with TR() translation keys across every tab, dialog, and component (~20 UI files) - Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with complete translations (~37k lines added) - Improve i18n loader with exe-relative path fallback and English base fallback for missing keys - Add pool-side hashrate polling via pool stats API in xmrig_manager - Introduce Layout::beginFrame() per-frame caching and refresh balance layout config only on schema generation change - Offload daemon output parsing to worker thread - Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
This commit is contained in:
@@ -244,6 +244,18 @@ bool XmrigManager::start(const Config& cfg) {
|
||||
}
|
||||
stats_ = PoolStats{};
|
||||
|
||||
// Extract pool hostname for stats API queries
|
||||
{
|
||||
std::string url = cfg.pool_url;
|
||||
// Strip protocol prefix if present
|
||||
auto pos = url.find("://");
|
||||
if (pos != std::string::npos) url = url.substr(pos + 3);
|
||||
// Strip port suffix
|
||||
pos = url.find(':');
|
||||
if (pos != std::string::npos) url = url.substr(0, pos);
|
||||
pool_host_ = url;
|
||||
}
|
||||
|
||||
// Find binary
|
||||
std::string binary = findXmrigBinary();
|
||||
if (binary.empty()) {
|
||||
@@ -572,6 +584,7 @@ void XmrigManager::monitorProcess() {
|
||||
}
|
||||
|
||||
int poll_counter = 0;
|
||||
int pool_api_counter = 0;
|
||||
while (!should_stop_) {
|
||||
drainOutput();
|
||||
|
||||
@@ -605,6 +618,12 @@ void XmrigManager::monitorProcess() {
|
||||
fetchStatsHttp();
|
||||
}
|
||||
|
||||
// Poll pool-side stats every ~30 seconds (300 * 100ms)
|
||||
if (++pool_api_counter >= 300) {
|
||||
pool_api_counter = 0;
|
||||
fetchPoolApiStats();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
drainOutput(); // Final drain
|
||||
@@ -711,5 +730,53 @@ void XmrigManager::fetchStatsHttp() {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Pool-side stats (hashrate reported by the pool)
|
||||
// ============================================================================
|
||||
|
||||
void XmrigManager::fetchPoolApiStats() {
|
||||
if (state_ != State::Running || pool_host_.empty()) return;
|
||||
|
||||
// Query the pool's public stats API
|
||||
std::string url = "https://" + pool_host_ + "/api/stats";
|
||||
std::string responseData;
|
||||
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) return;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCb);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000L);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 3000L);
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if (res != CURLE_OK) return;
|
||||
|
||||
try {
|
||||
json resp = json::parse(responseData);
|
||||
|
||||
// Pool stats API format: { "pools": { "<name>": { "hashrate": ... } } }
|
||||
double poolHR = 0;
|
||||
if (resp.contains("pools") && resp["pools"].is_object()) {
|
||||
for (auto& [key, pool] : resp["pools"].items()) {
|
||||
if (pool.contains("hashrate") && pool["hashrate"].is_number()) {
|
||||
poolHR = pool["hashrate"].get<double>();
|
||||
break; // Use the first pool entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(stats_mutex_);
|
||||
stats_.pool_hashrate = poolHR;
|
||||
} catch (...) {
|
||||
// Malformed response — ignore
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace daemon
|
||||
} // namespace dragonx
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
int64_t memory_total = 0; // bytes
|
||||
int64_t memory_used = 0; // bytes (resident set size)
|
||||
int threads_active = 0; // actual mining threads
|
||||
// Pool-side hashrate (from pool stats API, not xmrig)
|
||||
double pool_hashrate = 0;
|
||||
};
|
||||
|
||||
/// User-facing config (maps 1:1 to UI fields / Settings)
|
||||
@@ -138,6 +140,7 @@ private:
|
||||
void drainOutput();
|
||||
void appendOutput(const char* data, size_t len);
|
||||
void fetchStatsHttp(); // Blocking HTTP call — runs on monitor thread only
|
||||
void fetchPoolApiStats(); // Fetch pool-side stats (hashrate) from pool HTTP API
|
||||
|
||||
std::atomic<State> state_{State::Stopped};
|
||||
std::string last_error_;
|
||||
@@ -149,6 +152,7 @@ private:
|
||||
int api_port_ = 0;
|
||||
std::string api_token_;
|
||||
int threads_ = 0; // Thread count for mining
|
||||
std::string pool_host_; // Pool hostname for stats API
|
||||
PoolStats stats_;
|
||||
mutable std::mutex stats_mutex_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user