You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

754 lines
28 KiB

/*
* etcpmon_gui.c - ETCP Monitor GUI Implementation
*/
/* Must define this before any windows headers to exclude winsock.h */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
/* Must include winsock2.h before windows.h */
#include <winsock2.h>
#include <windows.h>
#include <commctrl.h>
#include "etcpmon_gui.h"
#include "etcpmon_client.h"
#include "etcpmon_protocol.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#pragma comment(lib, "comctl32.lib")
//#pragma comment(lib, "user32.lib")
//#pragma comment(lib, "gdi32.lib")
#define WINDOW_WIDTH 900
#define WINDOW_HEIGHT 900
#define UPDATE_INTERVAL 100 /* 100ms */
/* Global app pointer for callbacks */
static struct etcpmon_app* g_app = NULL;
/* Forward declarations */
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static void CreateControls(struct etcpmon_app* app);
static void OnConnect(struct etcpmon_app* app);
static void OnDisconnect(struct etcpmon_app* app);
static void OnConnectionSelect(struct etcpmon_app* app);
static void OnTimer(struct etcpmon_app* app);
static void UpdateUIState(struct etcpmon_app* app);
/* Callback functions for client events */
static void on_connected(void* user_data);
static void on_disconnected(void* user_data);
static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data);
static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data);
static void on_error(const char* msg, void* user_data);
/* Helper functions */
static void SetDlgItemTextFmt(HWND hDlg, int nIDDlgItem, const char* fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
SetDlgItemTextA(hDlg, nIDDlgItem, buf);
}
int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
if (!app) return -1;
memset(app, 0, sizeof(*app));
app->hInstance = hInstance;
g_app = app;
/* Initialize common controls */
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(iccex);
iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
InitCommonControlsEx(&iccex);
/* Register window class */
WNDCLASSEXA wcex;
memset(&wcex, 0, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcex.lpszClassName = ETCPMON_WINDOW_CLASS;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassExA(&wcex)) {
return -1;
}
/* Register Graph window class */
WNDCLASSEXA wcexGraph;
memset(&wcexGraph, 0, sizeof(wcexGraph));
wcexGraph.cbSize = sizeof(wcexGraph);
wcexGraph.style = CS_HREDRAW | CS_VREDRAW;
wcexGraph.lpfnWndProc = GraphWndProc;
wcexGraph.hInstance = hInstance;
wcexGraph.hIcon = NULL;
wcexGraph.hCursor = LoadCursor(NULL, IDC_CROSS);
wcexGraph.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcexGraph.lpszClassName = "ETCPMonGraph";
wcexGraph.hIconSm = NULL;
RegisterClassExA(&wcexGraph);
/* Initialize Winsock */
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return -1;
}
return 0;
}
HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
if (!app) return NULL;
/* Create main window */
app->hWndMain = CreateWindowExA(
0,
ETCPMON_WINDOW_CLASS,
"ETCP Monitor",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT,
NULL, NULL, app->hInstance, app
);
if (!app->hWndMain) {
return NULL;
}
/* Create status bar */
app->hWndStatus = CreateWindowExA(
0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
0, 0, 0, 0,
app->hWndMain, (HMENU)IDC_STATIC, app->hInstance, NULL
);
/* Create all controls */
CreateControls(app);
/* Set default values */
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ADDR, "127.0.0.1");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_PORT, "9090");
/* Initialize client */
app->client = (struct etcpmon_client*)malloc(sizeof(struct etcpmon_client));
if (app->client) {
etcpmon_client_init(app->client);
etcpmon_client_set_callbacks(app->client,
on_connected, on_disconnected,
on_conn_list, on_metrics, on_error,
app);
}
UpdateUIState(app);
return app->hWndMain;
}
static void CreateControls(struct etcpmon_app* app) {
HWND hWnd = app->hWndMain;
HINSTANCE hInst = app->hInstance;
int x = 10, y = 10;
/* Connection group */
CreateWindowExA(0, "BUTTON", "Connection",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
x, y, 880, 60, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
/* Address label and edit */
CreateWindowExA(0, "STATIC", "Server:",
WS_CHILD | WS_VISIBLE,
x + 10, y + 20, 40, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditAddr = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,
x + 55, y + 18, 120, 22, hWnd, (HMENU)IDC_EDIT_ADDR, hInst, NULL);
/* Port label and edit */
CreateWindowExA(0, "STATIC", "Port:",
WS_CHILD | WS_VISIBLE,
x + 185, y + 20, 30, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditPort = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_NUMBER,
x + 220, y + 18, 60, 22, hWnd, (HMENU)IDC_EDIT_PORT, hInst, NULL);
/* Connect/Disconnect buttons */
app->hBtnConnect = CreateWindowExA(0, "BUTTON", "Connect",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
x + 290, y + 18, 80, 24, hWnd, (HMENU)IDC_BTN_CONNECT, hInst, NULL);
app->hBtnDisconnect = CreateWindowExA(0, "BUTTON", "Disconnect",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
x + 380, y + 18, 80, 24, hWnd, (HMENU)IDC_BTN_DISCONNECT, hInst, NULL);
y += 70;
/* Connections list */
CreateWindowExA(0, "STATIC", "Connections:",
WS_CHILD | WS_VISIBLE,
x, y, 100, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hListConnections = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "",
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT,
x, y + 20, 300, 120, hWnd, (HMENU)IDC_LIST_CONNECTIONS, hInst, NULL);
/* Graph area - between Connections and ETCP Metrics */
y += 140;
// app->hGraphWnd = CreateWindowA("STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
// 10, y, GRAPH_WIDTH, GRAPH_HEIGHT, hWnd, (HMENU)IDC_GRAPH, hInst, app);
app->hGraphWnd = CreateWindowExA(
WS_EX_CLIENTEDGE,
"ETCPMonGraph", "",
WS_CHILD | WS_VISIBLE | WS_BORDER,
10, y, GRAPH_WIDTH, GRAPH_HEIGHT,
hWnd, (HMENU)IDC_GRAPH, hInst, app);
/* Channel list - grid blocks below graph */
y += GRAPH_HEIGHT + 5;
const char* channel_short_names[] = {
"RTT-L", "RTT10", "RTT100", "Jitter",
"Retrns", "ACKs", "Inflght", "Bytes/s"
};
int block_w = 200;
int block_h = 50;
int cols = 4;
int spacing = 5;
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
int col = i % cols;
int row = i / cols;
int bx = 10 + col * (block_w + spacing);
int by = y + row * (block_h + spacing);
app->hChannelName[i] = CreateWindowExA(0, "STATIC", channel_short_names[i],
WS_CHILD | WS_VISIBLE,
bx, by, block_w, 16, hWnd, (HMENU)(UINT_PTR)(IDC_CH_NAME_0 + i), hInst, NULL);
app->hChannelValue[i] = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
bx, by + 16, block_w, 18, hWnd, (HMENU)(UINT_PTR)(IDC_CH_VALUE_0 + i), hInst, NULL);
app->hChannelCheck[i] = CreateWindowExA(0, "BUTTON", "",
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BST_CHECKED,
bx, by + 34, 16, 14, hWnd, (HMENU)(UINT_PTR)(IDC_CH_CHECK_0 + i), hInst, NULL);
}
int channel_grid_h = ((GRAPH_METRICS_COUNT + cols - 1) / cols) * (block_h + spacing) - spacing;
y += channel_grid_h;
/* ETCP Metrics group */
y = 485;
CreateWindowExA(0, "BUTTON", "ETCP Metrics",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
x, y, 430, 220, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
int mx = x + 10, my = y + 20;
CreateWindowExA(0, "STATIC", "Name:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpName = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL,
mx + 85, my, 330, 20, hWnd, (HMENU)IDC_EDIT_ETCP_NAME, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "RTT Last:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpRttLast = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_RTT_LAST, hInst, NULL);
CreateWindowExA(0, "STATIC", "RTT Avg 10:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpRttAvg10 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_RTT_AVG10, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "RTT Avg 100:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpRttAvg100 = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_RTT_AVG100, hInst, NULL);
CreateWindowExA(0, "STATIC", "Jitter:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpJitter = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_JITTER, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "Bytes Sent:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpBytesSent = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_BYTES_SENT, hInst, NULL);
CreateWindowExA(0, "STATIC", "Retrans:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpRetrans = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_RETRANS, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "ACKs:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpAcks = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_ACKS, hInst, NULL);
CreateWindowExA(0, "STATIC", "Inflight:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpInflight = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_INFLIGHT, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "Links:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditEtcpLinks = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_LINKS, hInst, NULL);
/* TUN Metrics group - right of ETCP Metrics */
x = 460; y = 485;
CreateWindowExA(0, "BUTTON", "TUN Metrics",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
x, y, 430, 140, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
mx = x + 10; my = y + 20;
CreateWindowExA(0, "STATIC", "Read Bytes:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunReadBytes = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_READ_BYTES, hInst, NULL);
CreateWindowExA(0, "STATIC", "Write Bytes:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunWriteBytes = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_WRITE_BYTES, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "Read Pkts:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunReadPkts = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_READ_PKTS, hInst, NULL);
CreateWindowExA(0, "STATIC", "Write Pkts:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunWritePkts = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_WRITE_PKTS, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "Read Errs:",
WS_CHILD | WS_VISIBLE, mx, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunReadErrs = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_READ_ERRS, hInst, NULL);
CreateWindowExA(0, "STATIC", "Write Errs:",
WS_CHILD | WS_VISIBLE, mx + 200, my, 80, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditTunWriteErrs = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_WRITE_ERRS, hInst, NULL);
/* Links list */
y = 715;
CreateWindowExA(0, "STATIC", "Links:",
WS_CHILD | WS_VISIBLE,
10, y, 100, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hListLinks = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "",
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT,
10, y + 20, 880, 100, hWnd, (HMENU)IDC_LIST_LINKS, hInst, NULL);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
struct etcpmon_app* app = NULL;
if (message == WM_CREATE) {
CREATESTRUCTA* cs = (CREATESTRUCTA*)lParam;
app = (struct etcpmon_app*)cs->lpCreateParams;
SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)app);
} else {
app = (struct etcpmon_app*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
}
switch (message) {
case WM_COMMAND:
if (app) {
switch (LOWORD(wParam)) {
case IDC_BTN_CONNECT:
OnConnect(app);
break;
case IDC_BTN_DISCONNECT:
OnDisconnect(app);
break;
case IDC_LIST_CONNECTIONS:
if (HIWORD(wParam) == LBN_SELCHANGE) {
OnConnectionSelect(app);
}
break;
}
}
break;
case WM_TIMER:
if (app && wParam == IDC_TIMER_UPDATE) {
OnTimer(app);
}
break;
case WM_SIZE:
if (app && app->hWndStatus) {
SendMessage(app->hWndStatus, WM_SIZE, 0, 0);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcA(hWnd, message, wParam, lParam);
}
return 0;
}
static void OnConnect(struct etcpmon_app* app) {
if (!app || !app->client) return;
char addr[64];
char port_str[16];
GetDlgItemTextA(app->hWndMain, IDC_EDIT_ADDR, addr, sizeof(addr));
GetDlgItemTextA(app->hWndMain, IDC_EDIT_PORT, port_str, sizeof(port_str));
uint16_t port = (uint16_t)atoi(port_str);
if (port == 0) port = 9090;
etcpmon_gui_set_status(app, "Connecting...");
if (etcpmon_client_connect(app->client, addr, port) == 0) {
/* Start update timer */
app->updateTimer = SetTimer(app->hWndMain, IDC_TIMER_UPDATE, UPDATE_INTERVAL, NULL);
app->isConnected = 1;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Connected");
} else {
etcpmon_gui_set_status(app, "Connection failed");
}
}
static void OnDisconnect(struct etcpmon_app* app) {
if (!app || !app->client) return;
if (app->updateTimer) {
KillTimer(app->hWndMain, app->updateTimer);
app->updateTimer = 0;
}
etcpmon_client_disconnect(app->client);
app->isConnected = 0;
UpdateUIState(app);
etcpmon_gui_clear_metrics(app);
etcpmon_gui_set_status(app, "Disconnected");
}
static void OnConnectionSelect(struct etcpmon_app* app) {
if (!app || !app->client) return;
int sel = (int)SendMessage(app->hListConnections, LB_GETCURSEL, 0, 0);
if (sel == LB_ERR) return;
/* Get peer_id from item data */
uint64_t peer_id = (uint64_t)SendMessage(app->hListConnections, LB_GETITEMDATA, sel, 0);
/* Request metrics for this connection */
etcpmon_client_select_connection(app->client, peer_id);
}
static void OnTimer(struct etcpmon_app* app) {
if (!app || !app->client) return;
/* Process any incoming data */
// int result =
etcpmon_client_process(app->client);
/* If newly connected, request connection list */
static int first_connect = 1;
if (first_connect && app->isConnected) {
etcpmon_client_request_list(app->client);
first_connect = 0;
}
/* Request metrics periodically if a connection is selected */
if (app->client->selected_peer_id != 0) {
etcpmon_client_request_metrics(app->client);
}
}
static void UpdateUIState(struct etcpmon_app* app) {
if (!app) return;
EnableWindow(app->hEditAddr, !app->isConnected);
EnableWindow(app->hEditPort, !app->isConnected);
EnableWindow(app->hBtnConnect, !app->isConnected);
EnableWindow(app->hBtnDisconnect, app->isConnected);
}
/* Client callbacks */
//static void on_connected(void* user_data) {
// struct etcpmon_app* app = (struct etcpmon_app*)user_data;
// etcpmon_gui_set_status(app, "Connected");
//}
static void on_connected(void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
app->isConnected = 1;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Connected");
// Добавьте для теста
if (app->client->log_file) {
fprintf(app->client->log_file, "[DEBUG] on_connected called\n");
fflush(app->client->log_file);
}
etcpmon_client_request_list(app->client);
}
void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics) {
if (!app || !metrics) return;
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
}
if (app->client) {
struct metrics_history* h = etcpmon_client_get_history(app->client);
if (h && h->count > 0) {
int last_idx = (h->head - 1 + GRAPH_HISTORY_SIZE) % GRAPH_HISTORY_SIZE;
UpdateChannelValues(app, last_idx);
}
}
}
static void on_disconnected(void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
app->isConnected = 0;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Disconnected");
}
//static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data) {
// struct etcpmon_app* app = (struct etcpmon_app*)user_data;
// etcpmon_gui_update_conn_list(app, list, count);
//}
static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
etcpmon_gui_update_conn_list(app, list, count);
etcpmon_gui_set_status(app, "Connection list updated");
// Automatically select the first connection if available
if (count > 0 && app->hListConnections) {
// Select the first item (index 0)
SendMessage(app->hListConnections, LB_SETCURSEL, 0, 0);
// Trigger the selection handler to request metrics
OnConnectionSelect(app);
}
}
static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
if (app) {
etcpmon_gui_update_metrics(app, metrics, links, links_count);
etcpmon_client_add_to_history(app->client, metrics);
}
}
static void on_error(const char* msg, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
etcpmon_gui_set_status(app, msg);
}
int etcpmon_gui_run(struct etcpmon_app* app) {
if (!app || !app->hWndMain) return -1;
ShowWindow(app->hWndMain, SW_SHOW);
UpdateWindow(app->hWndMain);
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return (int)msg.wParam;
}
void etcpmon_gui_cleanup(struct etcpmon_app* app) {
if (!app) return;
if (app->updateTimer) {
KillTimer(app->hWndMain, app->updateTimer);
}
if (app->client) {
etcpmon_client_cleanup(app->client);
free(app->client);
app->client = NULL;
}
WSACleanup();
g_app = NULL;
}
void etcpmon_gui_set_status(struct etcpmon_app* app, const char* text) {
if (!app || !app->hWndStatus) return;
SendMessageA(app->hWndStatus, SB_SETTEXTA, 0, (LPARAM)text);
}
void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
struct etcpmon_conn_info* list,
uint8_t count) {
if (!app || !app->hListConnections) return;
/* Clear existing items */
SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0);
/* Add new items */
for (uint8_t i = 0; i < count; i++) {
char display[64];
snprintf(display, sizeof(display), "%s (%016llX)",
list[i].name, (unsigned long long)list[i].peer_node_id);
int idx = (int)SendMessageA(app->hListConnections, LB_ADDSTRING, 0, (LPARAM)display);
if (idx != LB_ERR) {
SendMessage(app->hListConnections, LB_SETITEMDATA, idx, (LPARAM)list[i].peer_node_id);
}
}
}
void etcpmon_gui_update_metrics(struct etcpmon_app* app,
struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count) {
if (!app || !metrics) return;
/* Update ETCP metrics */
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_LAST, "%u us",
metrics->etcp.rtt_last * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG10, "%u us",
metrics->etcp.rtt_avg_10 * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG100, "%u us",
metrics->etcp.rtt_avg_100 * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_JITTER, "%u us",
metrics->etcp.jitter * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_BYTES_SENT, "%llu",
(unsigned long long)metrics->etcp.bytes_sent_total);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RETRANS, "%u",
metrics->etcp.retrans_count);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_ACKS, "%u",
metrics->etcp.ack_count);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_INFLIGHT, "%u bytes",
metrics->etcp.unacked_bytes);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_LINKS, "%u",
metrics->etcp.links_count);
/* Update TUN metrics */
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_BYTES, "%llu",
(unsigned long long)metrics->tun.bytes_read);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_BYTES, "%llu",
(unsigned long long)metrics->tun.bytes_written);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_PKTS, "%u",
metrics->tun.packets_read);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_PKTS, "%u",
metrics->tun.packets_written);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_ERRS, "%u",
metrics->tun.read_errors);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_ERRS, "%u",
metrics->tun.write_errors);
/* Update links list */
if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
for (uint8_t i = 0; i < links_count; i++) {
char display[256];
snprintf(display, sizeof(display),
"Link %u: Status=%s, EncErrors=%u, DecErrors=%u, "
"SendErr=%u, RecvErr=%u, BytesEnc=%llu, BytesDec=%llu, "
"BW=%u Kbps, NATChg=%u",
links[i].local_link_id,
links[i].status ? "UP" : "DOWN",
links[i].encrypt_errors,
links[i].decrypt_errors,
links[i].send_errors,
links[i].recv_errors,
(unsigned long long)links[i].total_encrypted,
(unsigned long long)links[i].total_decrypted,
links[i].bandwidth,
links[i].nat_changes_count);
SendMessageA(app->hListLinks, LB_ADDSTRING, 0, (LPARAM)display);
}
}
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); // Force immediate paint
}
}
void etcpmon_gui_clear_metrics(struct etcpmon_app* app) {
if (!app) return;
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_NAME, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_RTT_LAST, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG10, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG100, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_JITTER, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_BYTES_SENT, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_RETRANS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_ACKS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_INFLIGHT, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_LINKS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_READ_BYTES, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_WRITE_BYTES, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_READ_PKTS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_WRITE_PKTS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_READ_ERRS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_WRITE_ERRS, "");
if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
}
SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0);
}