Browse Source

gr restore v2

nodeinfo-routing-update
jeka 1 month ago
parent
commit
1947810cb0
  1. 4
      src/control_server.c
  2. 51
      tools/etcpmon/Makefile
  3. 41
      tools/etcpmon/etcpmon_graph.c
  4. 55
      tools/etcpmon/etcpmon_gui.c

4
src/control_server.c

@ -419,6 +419,10 @@ static void client_read_callback(socket_t fd, void* arg) {
if (received == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) {
if (err == 10054) {
client->connected = 0;
return;
}
if (server && server->log_file) {
fprintf(server->log_file, "%llu: [ERROR] Client recv error: %d\n",
(unsigned long long)get_timestamp_ms(), err);

51
tools/etcpmon/Makefile

@ -1,40 +1,35 @@
# ETCP Monitor Makefile for MinGW
# Usage: make [CC=gcc] [CFLAGS=...] [LDFLAGS=...]
# ETCP Monitor Makefile
# Usage: make [target]
#
# Targets:
# all - Build the application (default)
# clean - Remove object files and executable
# rebuild - Clean and rebuild
CC = gcc
CFLAGS = -Wall -O2 -DWIN32_LEAN_AND_MEAN
CFLAGS += -I. -I../../src
LDFLAGS = -mwindows
LIBS = -lws2_32 -lcomctl32 -luser32 -lgdi32
# Compiler
CC ?= gcc
# Target
TARGET = etcpmon.exe
# Source files
SRCS = etcpmon_main.c \
etcpmon_gui.c \
etcpmon_client.c \
etcpmon_graph.c
# Object files
SRCS = etcpmon_main.c etcpmon_gui.c etcpmon_client.c etcpmon_graph.c
OBJS = $(SRCS:.c=.o)
# Windows-specific libraries
LIBS = -lws2_32 -lgdi32 -lcomdlg32 -lkernel32 -luser32 -lcomctl32
# Compiler flags
CFLAGS += -Wall -Wextra
CFLAGS += -O2
CFLAGS += -D_WIN32_WINNT=0x0601
CFLAGS += -DUNICODE -D_UNICODE
CFLAGS += -I.
# Build rule
.PHONY: all clean
.PHONY: all clean rebuild
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
@echo Linking $@
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
@echo Compiling $<
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) $(TARGET) *.o
@echo Cleaning...
rm -f $(OBJS) $(TARGET)
rebuild: clean $(TARGET)

41
tools/etcpmon/etcpmon_graph.c

@ -6,49 +6,40 @@
* - Строка с значениями под курсором ("RTT-L: 45.2 ...") полностью убрана
* - Значения в блоках под графиком всегда живые (обновляются в on_metrics)
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "etcpmon_graph.h"
#include "etcpmon_client.h"
#include <stdlib.h>
#include <stdio.h>
#include "etcpmon_gui.h"
const COLORREF graph_colors[GRAPH_METRICS_COUNT] = {
RGB(255,0,0), RGB(0,255,0), RGB(0,0,255), RGB(255,255,0),
RGB(255,0,255), RGB(0,255,255), RGB(255,128,0), RGB(128,0,255)
};
const char* graph_metric_names[GRAPH_METRICS_COUNT] = {
"RTT Last","RTT Avg10","RTT Avg100","Jitter",
"Retrns","ACKs","Inflght","Bytes/s"
};
static void RecalculateMinMax(struct metrics_history* hist, int vis, int oldest)
{
for (int m = 0; m < GRAPH_METRICS_COUNT; m++) {
float mn = 1e9f;
float mx = -1e9f;
for (int i = 0; i < vis; i++) {
float v = hist->values[m][(oldest + i) % GRAPH_HISTORY_SIZE];
if (v < mn) mn = v;
if (v > mx) mx = v;
}
if (mx - mn < 0.001f) {
if (mn > 0.0f) { mn *= 0.9f; mx *= 1.1f; }
else { mx = 1.0f; }
}
hist->min_val[m] = mn;
hist->max_val[m] = mx;
}
}
void GetSelectedChannels(struct etcpmon_app* app, int* selected, int* count) {
*count = 0;
if (!app) return;
@ -62,26 +53,20 @@ void GetSelectedChannels(struct etcpmon_app* app, int* selected, int* count) {
for (int i = 0; i < 4 && i < GRAPH_METRICS_COUNT; i++)
selected[(*count)++] = i;
}
void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
{
if (!app || !rc) return;
int w = rc->right - rc->left;
int h = rc->bottom - rc->top;
/* Off-screen буфер */
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP bmp = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, bmp);
SetBkMode(memDC, TRANSPARENT);
/* Фон */
HBRUSH bg = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
FillRect(memDC, rc, bg);
DeleteObject(bg);
/* Нет данных */
if (!app->client || !etcpmon_client_get_history(app->client) ||
etcpmon_client_get_history(app->client)->count < 2)
@ -90,21 +75,17 @@ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
DrawTextA(memDC, "Waiting for data...", -1, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
goto end;
}
struct metrics_history* hist = etcpmon_client_get_history(app->client);
/* Выбранные каналы */
int sel[GRAPH_METRICS_COUNT];
int sc = 0;
GetSelectedChannels(app, sel, &sc);
int pad = 5;
int gl = pad + 45; /* левая граница графика */
int gr = w - pad; /* правая граница */
int gt = pad; /* верх */
int gb = h - 25; /* низ */
int gh = gb - gt;
/* === Сетка === */
HPEN grid = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNSHADOW));
HPEN old = (HPEN)SelectObject(memDC, grid);
@ -115,7 +96,6 @@ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
}
SelectObject(memDC, old);
DeleteObject(grid);
/* === Оси === */
HPEN axis = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWTEXT));
old = (HPEN)SelectObject(memDC, axis);
@ -123,30 +103,24 @@ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
MoveToEx(memDC, gl, gb, NULL); LineTo(memDC, gr, gb);
SelectObject(memDC, old);
DeleteObject(axis);
/* Сколько точек видно */
int vis = (hist->count < gr - gl) ? hist->count : (gr - gl);
int oldest = (hist->head - vis + GRAPH_HISTORY_SIZE) % GRAPH_HISTORY_SIZE;
int startx = gr - vis;
/* Пересчёт min/max 5 раз в секунду */
DWORD now = GetTickCount();
if (now - hist->last_minmax_update >= 200) {
RecalculateMinMax(hist, vis, oldest);
hist->last_minmax_update = now;
}
/* Буфер точек */
POINT* points = (POINT*)malloc((vis + 1) * sizeof(POINT));
/* Рисуем графики */
for (int ch = 0; ch < sc; ch++) {
int m = sel[ch];
float mn = hist->min_val[m];
float mx = hist->max_val[m];
SelectObject(memDC, app->hGraphPens[m]);
for (int i = 0; i < vis; i++) {
float v = hist->values[m][(oldest + i) % GRAPH_HISTORY_SIZE];
int x = startx + i;
@ -158,9 +132,7 @@ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
}
Polyline(memDC, points, vis);
}
free(points);
/* === ВЕРТИКАЛЬНАЯ ЛИНИЯ КУРСОРА (только линия, без текста) === */
if (app->graph_cursor_active) {
HPEN cp = CreatePen(PS_SOLID, 1, RGB(80, 80, 80));
@ -170,20 +142,16 @@ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app)
SelectObject(memDC, old);
DeleteObject(cp);
}
end:
/* Копируем на экран */
BitBlt(hdc, 0, 0, w, h, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, oldBmp);
DeleteObject(bmp);
DeleteDC(memDC);
}
void UpdateChannelValues(struct etcpmon_app* app, int history_idx)
{
if (!app || !app->client) return;
struct metrics_history* h = etcpmon_client_get_history(app->client);
if (!h || history_idx < 0 || history_idx >= GRAPH_HISTORY_SIZE) {
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
@ -192,15 +160,11 @@ void UpdateChannelValues(struct etcpmon_app* app, int history_idx)
}
return;
}
static char last_text[GRAPH_METRICS_COUNT][32] = {0};
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
if (!app->hChannelValue[i]) continue;
float val = h->values[i][history_idx];
char buf[32] = {0};
if (i == GRAPH_METRIC_BYTES_SENT) {
if (val >= 1024*1024) snprintf(buf, sizeof(buf), "%.1f MB/s", val/(1024.*1024.));
else if (val >= 1024) snprintf(buf, sizeof(buf), "%.1f KB/s", val/1024.);
@ -213,22 +177,17 @@ void UpdateChannelValues(struct etcpmon_app* app, int history_idx)
} else {
snprintf(buf, sizeof(buf), "%.1f ms", val);
}
if (strcmp(buf, last_text[i]) == 0)
continue;
strcpy(last_text[i], buf);
SendMessageA(app->hChannelValue[i], WM_SETREDRAW, FALSE, 0);
SetWindowTextA(app->hChannelValue[i], buf);
SendMessageA(app->hChannelValue[i], WM_SETREDRAW, TRUE, 0);
InvalidateRect(app->hChannelValue[i], NULL, FALSE);
}
}
LRESULT CALLBACK GraphWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
struct etcpmon_app* app = (struct etcpmon_app*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
switch (message) {
case WM_CREATE: {
CREATESTRUCTA* cs = (CREATESTRUCTA*)lParam;

55
tools/etcpmon/etcpmon_gui.c

@ -1,35 +1,28 @@
/*
* 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 10 /* 50ms → 20 samples per second */
/* 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);
@ -38,7 +31,6 @@ 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);
@ -47,7 +39,6 @@ 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];
@ -57,7 +48,6 @@ static void SetDlgItemTextFmt(HWND hDlg, int nIDDlgItem, const char* fmt, ...) {
va_end(args);
SetDlgItemTextA(hDlg, nIDDlgItem, buf);
}
int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
if (!app) return -1;
@ -111,7 +101,6 @@ int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
return 0;
}
HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
if (!app) return NULL;
@ -141,7 +130,6 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
/* Create all controls */
CreateControls(app);
SetTimer(app->hWndMain, IDC_TIMER_UPDATE, UPDATE_INTERVAL, NULL);
/* Set default values */
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ADDR, "127.0.0.1");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_PORT, "9090");
@ -159,12 +147,10 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
app->hGraphPens[i] = CreatePen(PS_SOLID, 2, graph_colors[i]);
}
UpdateUIState(app);
return app->hWndMain;
}
static void CreateControls(struct etcpmon_app* app) {
HWND hWnd = app->hWndMain;
HINSTANCE hInst = app->hInstance;
@ -215,7 +201,6 @@ static void CreateControls(struct etcpmon_app* app) {
/* Graph area */
y += 140;
app->hGraphWnd = CreateWindowExA(
WS_EX_CLIENTEDGE,
"ETCPMonGraph", "",
@ -393,7 +378,6 @@ static void CreateControls(struct etcpmon_app* app) {
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;
@ -429,7 +413,6 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
SendMessage(app->hWndStatus, WM_SIZE, 0, 0);
}
break;
case WM_TIMER:
if (wParam == IDC_TIMER_UPDATE && app) {
OnTimer(app);
@ -447,7 +430,6 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
return 0;
}
static void OnConnect(struct etcpmon_app* app) {
if (!app || !app->client) return;
@ -470,7 +452,6 @@ static void OnConnect(struct etcpmon_app* app) {
etcpmon_gui_set_status(app, "Connection failed");
}
}
static void OnDisconnect(struct etcpmon_app* app) {
if (!app || !app->client) return;
@ -480,7 +461,6 @@ static void OnDisconnect(struct etcpmon_app* app) {
etcpmon_gui_clear_metrics(app);
etcpmon_gui_set_status(app, "Disconnected");
}
static void OnConnectionSelect(struct etcpmon_app* app) {
if (!app || !app->client) return;
@ -493,32 +473,26 @@ static void OnConnectionSelect(struct etcpmon_app* app) {
/* 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;
/* 1. Сеть */
etcpmon_client_process(app->client);
/* 2. Первый запрос списка после подключения */
static int first_connect = 1;
if (first_connect && app->isConnected) {
etcpmon_client_request_list(app->client);
first_connect = 0;
}
/* 3. Запрос метрик */
if (app->client->selected_peer_id != 0) {
etcpmon_client_request_metrics(app->client);
}
/* 4. График — 20 раз в секунду */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); /* форсируем немедленную перерисовку */
}
}
static void UpdateUIState(struct etcpmon_app* app) {
if (!app) return;
@ -527,7 +501,6 @@ static void UpdateUIState(struct etcpmon_app* app) {
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;
@ -540,7 +513,6 @@ static void on_connected(void* user_data) {
}
etcpmon_client_request_list(app->client);
}
void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics) {
if (!app || !metrics) return;
@ -556,26 +528,22 @@ void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metric
}
}
}
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);
etcpmon_gui_set_status(app, "Connection list updated");
/* Automatically select the first connection if available */
if (count > 0 && app->hListConnections) {
SendMessage(app->hListConnections, LB_SETCURSEL, 0, 0);
OnConnectionSelect(app);
}
}
static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data) {
@ -583,7 +551,6 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
if (app) {
etcpmon_gui_update_metrics(app, metrics, links, links_count);
etcpmon_client_add_to_history(app->client, metrics);
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;
@ -591,18 +558,14 @@ static void on_metrics(struct etcpmon_rsp_metrics* 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);
@ -610,29 +573,24 @@ int etcpmon_gui_run(struct etcpmon_app* app) {
}
return (int)msg.wParam;
}
void etcpmon_gui_cleanup(struct etcpmon_app* app) {
if (!app) return;
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
if (app->hGraphPens[i]) DeleteObject(app->hGraphPens[i]);
}
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) {
@ -653,7 +611,6 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
}
}
}
/* Обновляет EDIT только если текст действительно изменился → убирает моргание */
static BOOL UpdateEditIfChanged(HWND hDlg, int nIDDlgItem, const char* fmt, ...)
{
@ -662,27 +619,21 @@ static BOOL UpdateEditIfChanged(HWND hDlg, int nIDDlgItem, const char* fmt, ...)
va_start(args, fmt);
vsnprintf(new_text, sizeof(new_text), fmt, args);
va_end(args);
char old_text[128] = {0};
GetDlgItemTextA(hDlg, nIDDlgItem, old_text, sizeof(old_text));
if (strcmp(old_text, new_text) == 0)
return FALSE;
SetDlgItemTextA(hDlg, nIDDlgItem, new_text);
InvalidateRect(GetDlgItem(hDlg, nIDDlgItem), NULL, FALSE);
return TRUE;
}
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;
HWND hMain = app->hWndMain;
/* ETCP Metrics — обновляем ТОЛЬКО при изменении */
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RTT_LAST, "%u us", metrics->etcp.rtt_last * 100);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RTT_AVG10, "%u us", metrics->etcp.rtt_avg_10 * 100);
@ -693,7 +644,6 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_ACKS, "%u", metrics->etcp.ack_count);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_INFLIGHT, "%u bytes", metrics->etcp.unacked_bytes);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_LINKS, "%u", metrics->etcp.links_count);
/* TUN Metrics */
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_READ_BYTES, "%llu", (unsigned long long)metrics->tun.bytes_read);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_BYTES, "%llu", (unsigned long long)metrics->tun.bytes_written);
@ -701,7 +651,6 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_PKTS, "%u", metrics->tun.packets_written);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_READ_ERRS, "%u", metrics->tun.read_errors);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_ERRS, "%u", metrics->tun.write_errors);
/* Links list */
if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
@ -721,21 +670,17 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
(unsigned long long)links[i].total_decrypted,
links[i].bandwidth,
links[i].nat_changes_count);
SendMessageA(app->hListLinks, LB_ADDSTRING, 0, (LPARAM)display);
}
InvalidateRect(app->hListLinks, NULL, FALSE);
}
/* График */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
}
}
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, "");

Loading…
Cancel
Save