- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
122 lines
3.9 KiB
C++
122 lines
3.9 KiB
C++
// statuspage.cpp — Status page (NTP + connections) in settings
|
|
#include "statuspage.h"
|
|
#include "../transport/utun_node.h"
|
|
|
|
extern "C" {
|
|
#include "../transport/gui_bridge.h"
|
|
#include "chat/chat_core.h"
|
|
}
|
|
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QFont>
|
|
#include <QTimer>
|
|
#include <QDateTime>
|
|
|
|
static StatusPage* g_activeStatusPage = nullptr;
|
|
|
|
static void statusRefreshCallback(const char* text, int len) {
|
|
if (g_activeStatusPage)
|
|
g_activeStatusPage->setText(QString::fromUtf8(text, len));
|
|
}
|
|
|
|
StatusPage::StatusPage(UtunNode* node, QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_node(node)
|
|
{
|
|
auto* layout = new QVBoxLayout(this);
|
|
layout->setSpacing(2);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
/* ── clock block ── */
|
|
m_clockFrame = new QFrame(this);
|
|
m_clockFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
|
|
m_clockFrame->setStyleSheet(
|
|
"QFrame { border: 1px solid palette(mid); border-radius: 2px;"
|
|
" padding: 1px; background: palette(alternate-base); }");
|
|
|
|
auto* clockLayout = new QVBoxLayout(m_clockFrame);
|
|
clockLayout->setContentsMargins(4, 0, 4, 0);
|
|
|
|
m_clockLabel = new QLabel("--:--:--", m_clockFrame);
|
|
QFont clockFont("monospace", 20);
|
|
clockFont.setBold(true);
|
|
m_clockLabel->setFont(clockFont);
|
|
m_clockLabel->setAlignment(Qt::AlignCenter);
|
|
m_clockLabel->setStyleSheet("border: none; color: palette(text);");
|
|
clockLayout->addWidget(m_clockLabel);
|
|
|
|
layout->addWidget(m_clockFrame);
|
|
|
|
/* ── System Status section ── */
|
|
auto* title = new QLabel("System Status", this);
|
|
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
|
layout->addWidget(title);
|
|
|
|
m_textEdit = new QPlainTextEdit(this);
|
|
m_textEdit->setReadOnly(true);
|
|
m_textEdit->setFont(QFont("monospace", 10));
|
|
m_textEdit->setStyleSheet(
|
|
"QPlainTextEdit { border: 1px solid palette(mid); border-radius: 4px;"
|
|
" padding: 8px; background: palette(base); }");
|
|
m_textEdit->setPlaceholderText("Click Refresh to load status");
|
|
layout->addWidget(m_textEdit, 1);
|
|
|
|
auto* btnLayout = new QHBoxLayout();
|
|
btnLayout->addStretch();
|
|
m_refreshBtn = new QPushButton("Refresh", this);
|
|
m_refreshBtn->setFixedWidth(90);
|
|
m_refreshBtn->setStyleSheet(
|
|
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;"
|
|
" padding: 6px 16px; font-size: 13px; background: palette(button); }"
|
|
"QPushButton:hover { background: palette(light); }");
|
|
connect(m_refreshBtn, &QPushButton::clicked, this, &StatusPage::onRefreshClicked);
|
|
btnLayout->addWidget(m_refreshBtn);
|
|
layout->addLayout(btnLayout);
|
|
|
|
m_clockTimer = new QTimer(this);
|
|
m_clockTimer->setSingleShot(true);
|
|
m_clockTimer->setTimerType(Qt::PreciseTimer);
|
|
connect(m_clockTimer, &QTimer::timeout, this, &StatusPage::updateClock);
|
|
|
|
g_activeStatusPage = this;
|
|
gui_bridge_set_status_refresh_cb(statusRefreshCallback);
|
|
}
|
|
|
|
StatusPage::~StatusPage() {
|
|
m_clockTimer->stop();
|
|
if (g_activeStatusPage == this) g_activeStatusPage = nullptr;
|
|
}
|
|
|
|
void StatusPage::updateClock() {
|
|
if (!m_node) return;
|
|
|
|
qint64 nowMs = QDateTime::currentMSecsSinceEpoch();
|
|
int64_t offsetUs = m_node->ntpOffsetUs();
|
|
bool synced = m_node->ntpSynced();
|
|
|
|
qint64 correctedMs = nowMs - (offsetUs / 1000);
|
|
QDateTime corrected = QDateTime::fromMSecsSinceEpoch(correctedMs).toLocalTime();
|
|
|
|
QString t = corrected.toString("HH:mm:ss");
|
|
|
|
if (synced)
|
|
m_clockLabel->setText(QString("%1 [NTP ✓]").arg(t));
|
|
else
|
|
m_clockLabel->setText(QString("%1 [LOCAL]").arg(t));
|
|
|
|
int msToNext = 1000 - (corrected.time().msec());
|
|
m_clockTimer->start(msToNext);
|
|
}
|
|
|
|
void StatusPage::refreshStatus() {
|
|
updateClock();
|
|
m_textEdit->setPlainText("Loading...");
|
|
gui_bridge_post_uasync_fn(chat_core_collect_status_trampoline, NULL);
|
|
}
|
|
|
|
void StatusPage::onRefreshClicked() {
|
|
refreshStatus();
|
|
}
|