updated ui to use multiple screens

This commit is contained in:
Sven laptop
2026-09-18 17:15:00 +02:00
parent c09b3f3f66
commit ebcc485f51
6 changed files with 224 additions and 26 deletions
+193 -24
View File
@@ -16,6 +16,13 @@
#define ID_DATE 107
#define ID_ROUTES 108
#define ID_STOPS 109
#define ID_BACK 110
#define ID_HALT 111
#define ID_PAGE_TITLE 112
#define ID_DATE_LABEL 113
#define ID_USER_LABEL 114
#define ID_PASSWORD_LABEL 115
#define ID_ROUTES_LABEL 116
#define MAX_BUF 65535
#define API_HOST "192.168.68.66"
@@ -29,6 +36,10 @@ typedef struct {
int routeCount;
int routeLineIds[32];
char routeLabels[32][256];
int stopCount;
int activeStop;
char stopNames[64][128];
char stopTimes[64][32];
} APPSTATE;
static void SetStatusText(HWND hwnd, const char *text)
@@ -41,6 +52,115 @@ static void SetOutputText(HWND hwnd, const char *text)
SetWindowTextA(GetDlgItem(hwnd, ID_OUTPUT), text);
}
static void ShowControl(HWND hwnd, int id, int visible)
{
ShowWindow(GetDlgItem(hwnd, id), visible ? SW_SHOW : SW_HIDE);
}
static void ShowRouteScreen(HWND hwnd)
{
SetWindowTextA(hwnd, "Transit Driver XP - Assigned Routes");
SetWindowTextA(GetDlgItem(hwnd, ID_PAGE_TITLE), "TODAY'S ASSIGNED ROUTES");
ShowControl(hwnd, ID_DATE, 1);
ShowControl(hwnd, ID_DATE_LABEL, 1);
ShowControl(hwnd, ID_ROUTES, 1);
ShowControl(hwnd, ID_ROUTES_LABEL, 1);
ShowControl(hwnd, ID_REFRESH, 1);
ShowControl(hwnd, ID_STOPS, 1);
ShowControl(hwnd, ID_OUTPUT, 1);
ShowControl(hwnd, ID_BACK, 0);
ShowControl(hwnd, ID_HALT, 0);
ShowControl(hwnd, ID_USERNAME, 0);
ShowControl(hwnd, ID_PASSWORD, 0);
ShowControl(hwnd, ID_USER_LABEL, 0);
ShowControl(hwnd, ID_PASSWORD_LABEL, 0);
ShowControl(hwnd, ID_LOGIN, 0);
}
static void ShowStartScreen(HWND hwnd, APPSTATE *state)
{
char title[256];
SetWindowTextA(hwnd, "Transit Driver XP - Route in Progress");
SetWindowTextA(GetDlgItem(hwnd, ID_PAGE_TITLE), "ROUTE IN PROGRESS");
ShowControl(hwnd, ID_DATE, 0);
ShowControl(hwnd, ID_DATE_LABEL, 0);
ShowControl(hwnd, ID_ROUTES, 0);
ShowControl(hwnd, ID_ROUTES_LABEL, 0);
ShowControl(hwnd, ID_REFRESH, 0);
ShowControl(hwnd, ID_STOPS, 0);
ShowControl(hwnd, ID_OUTPUT, 0);
ShowControl(hwnd, ID_BACK, 1);
ShowControl(hwnd, ID_HALT, 1);
ShowControl(hwnd, ID_USERNAME, 0);
ShowControl(hwnd, ID_PASSWORD, 0);
ShowControl(hwnd, ID_USER_LABEL, 0);
ShowControl(hwnd, ID_PASSWORD_LABEL, 0);
ShowControl(hwnd, ID_LOGIN, 0);
snprintf(title, sizeof(title), "Route started\r\n\r\nTap the halt button when you arrive at the displayed halt.");
SetWindowTextA(GetDlgItem(hwnd, ID_STATUS), "Route in progress");
SetWindowTextA(GetDlgItem(hwnd, ID_OUTPUT), title);
InvalidateRect(GetDlgItem(hwnd, ID_HALT), NULL, TRUE);
(void)state;
}
static void UpdateHaltButton(HWND hwnd, APPSTATE *state)
{
char label[256];
if (state->activeStop >= state->stopCount) {
SetWindowTextA(GetDlgItem(hwnd, ID_HALT), "ROUTE COMPLETE");
SetStatusText(hwnd, "Route complete");
return;
}
snprintf(label, sizeof(label), "%s\r\n+%s min", state->stopNames[state->activeStop], state->stopTimes[state->activeStop]);
SetWindowTextA(GetDlgItem(hwnd, ID_HALT), label);
SetStatusText(hwnd, "Route in progress");
}
static void ShowLoginScreen(HWND hwnd)
{
SetWindowTextA(hwnd, "Transit Driver XP - Driver Login");
SetWindowTextA(GetDlgItem(hwnd, ID_PAGE_TITLE), "DRIVER LOGIN");
ShowControl(hwnd, ID_DATE, 0);
ShowControl(hwnd, ID_DATE_LABEL, 0);
ShowControl(hwnd, ID_ROUTES, 0);
ShowControl(hwnd, ID_ROUTES_LABEL, 0);
ShowControl(hwnd, ID_REFRESH, 0);
ShowControl(hwnd, ID_STOPS, 0);
ShowControl(hwnd, ID_OUTPUT, 1);
ShowControl(hwnd, ID_BACK, 0);
ShowControl(hwnd, ID_HALT, 0);
ShowControl(hwnd, ID_USERNAME, 1);
ShowControl(hwnd, ID_PASSWORD, 1);
ShowControl(hwnd, ID_USER_LABEL, 1);
ShowControl(hwnd, ID_PASSWORD_LABEL, 1);
ShowControl(hwnd, ID_LOGIN, 1);
}
static void DrawHaltButton(const DRAWITEMSTRUCT *draw)
{
char label[256];
HBRUSH brush = CreateSolidBrush(RGB(210, 55, 45));
HPEN pen = CreatePen(PS_SOLID, 2, RGB(120, 25, 20));
HGDIOBJ oldBrush = SelectObject(draw->hDC, brush);
HGDIOBJ oldPen = SelectObject(draw->hDC, pen);
RECT rect = draw->rcItem;
Ellipse(draw->hDC, rect.left + 4, rect.top + 4, rect.right - 4, rect.bottom - 4);
SetBkMode(draw->hDC, TRANSPARENT);
SetTextColor(draw->hDC, RGB(255, 255, 255));
GetWindowTextA(draw->hwndItem, label, sizeof(label));
DrawTextA(draw->hDC, label, -1, &rect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
SelectObject(draw->hDC, oldBrush);
SelectObject(draw->hDC, oldPen);
DeleteObject(brush);
DeleteObject(pen);
}
static void PerformRefresh(HWND hwnd, APPSTATE *state);
static void TrimNewlines(char *text)
{
size_t len = strlen(text);
@@ -210,7 +330,7 @@ static void ExtractScheduleSummary(const char *json, APPSTATE *state, char *out,
ExtractJsonFieldString(routeText, "lineNumber", routeLine, sizeof(routeLine));
ExtractJsonFieldString(routeText, "startTime", routeStartTime, sizeof(routeStartTime));
ExtractJsonFieldString(routeText, "id", routeLineId, sizeof(routeLineId));
if (routeLine[0] == '\0') {
if (routeLine[0] == '\0' || routeLineId[0] == '\0') {
char busLine[1024];
const char *busKey = strstr(routeText, "\"busLine\"");
if (busKey) {
@@ -295,7 +415,7 @@ static void ExtractScheduleSummary(const char *json, APPSTATE *state, char *out,
snprintf(out, outSize, "%s", summary);
}
static void ExtractStopsSummary(const char *json, char *out, size_t outSize)
static void ExtractStopsSummary(const char *json, APPSTATE *state, char *out, size_t outSize)
{
const char *stopsKey = strstr(json, "\"stops\"");
const char *p;
@@ -338,6 +458,10 @@ static void ExtractStopsSummary(const char *json, char *out, size_t outSize)
snprintf(summary + strlen(summary), sizeof(summary) - strlen(summary),
"%s. %s (arrival +%s min)\r\n", order, name, eta);
if (count < 64) {
snprintf(state->stopNames[count], sizeof(state->stopNames[count]), "%s", name);
snprintf(state->stopTimes[count], sizeof(state->stopTimes[count]), "%s", eta);
}
count++;
p = end + 1;
while (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t' || *p == ',') {
@@ -350,6 +474,8 @@ static void ExtractStopsSummary(const char *json, char *out, size_t outSize)
return;
}
state->stopCount = count < 64 ? count : 64;
state->activeStop = 0;
snprintf(out, outSize, "Stop-by-stop timetable (%d stops)\r\n\r\n%s", count, summary);
}
@@ -390,9 +516,8 @@ static void ExtractTokenFromJson(const char *json, char *outToken, size_t outSiz
static void HttpErrorMessage(const char *context, DWORD error)
{
char text[256];
snprintf(text, sizeof(text), "%s (WinHTTP error %lu)", context, error);
MessageBoxA(NULL, text, "Driver app error", MB_ICONERROR | MB_OK);
(void)context;
(void)error;
}
static int HttpRequestText(const char *method,
@@ -623,23 +748,23 @@ static void PerformLogin(HWND hwnd, APPSTATE *state)
SetStatusText(hwnd, "Logging in...");
if (!HttpRequestText("POST", "http://192.168.68.66:5000/login", payload, NULL, response, sizeof(response))) {
SetOutputText(hwnd, "Login failed. Check that auth_api is running on 192.168.68.66:5000.");
SetOutputText(hwnd, "Driver service unreachable. Please try again.");
SetStatusText(hwnd, "Login failed");
return;
}
ExtractTokenFromJson(response, token, sizeof(token));
if (token[0] == '\0') {
SetOutputText(hwnd, response);
SetStatusText(hwnd, "No token returned");
SetOutputText(hwnd, "Driver service unreachable. Please try again.");
SetStatusText(hwnd, "Service unavailable");
return;
}
strncpy(state->token, token, sizeof(state->token) - 1);
state->token[sizeof(state->token) - 1] = '\0';
snprintf(state->lastText, sizeof(state->lastText), "Login succeeded. Driver session ready.\r\nUse Refresh schedule to load the route list.");
SetOutputText(hwnd, state->lastText);
SetStatusText(hwnd, "Connected");
ShowRouteScreen(hwnd);
SetStatusText(hwnd, "Loading today's assigned routes...");
PerformRefresh(hwnd, state);
}
static void PerformRefresh(HWND hwnd, APPSTATE *state)
@@ -663,7 +788,7 @@ static void PerformRefresh(HWND hwnd, APPSTATE *state)
SetStatusText(hwnd, "Loading schedule...");
if (!HttpRequestText("GET", scheduleUrl, NULL, authHeader, response, sizeof(response))) {
SetOutputText(hwnd, "Unable to load daily schedule. Check that transit_api is running on 192.168.68.66:5001.");
SetOutputText(hwnd, "Driver service unreachable. Please try again.");
SetStatusText(hwnd, "Schedule load failed");
return;
}
@@ -704,14 +829,14 @@ static void PerformLoadStops(HWND hwnd, APPSTATE *state)
SetStatusText(hwnd, "Loading stop details...");
if (!HttpRequestText("GET", stopsUrl, NULL, authHeader, response, sizeof(response))) {
SetOutputText(hwnd, "Unable to load stops for this route. Check the transit API connection.");
SetOutputText(hwnd, "Driver service unreachable. Please try again.");
SetStatusText(hwnd, "Stop details failed");
return;
}
ExtractStopsSummary(response, state->lastText, sizeof(state->lastText));
SetOutputText(hwnd, state->lastText);
SetStatusText(hwnd, "Stop details loaded");
ExtractStopsSummary(response, state, state->lastText, sizeof(state->lastText));
ShowStartScreen(hwnd, state);
UpdateHaltButton(hwnd, state);
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
@@ -723,22 +848,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_CREATE:
{
LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
SYSTEMTIME now;
char today[16];
state = (APPSTATE *)calloc(1, sizeof(APPSTATE));
state->hwnd = hwnd;
cs->lpCreateParams = state;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)state);
GetLocalTime(&now);
snprintf(today, sizeof(today), "%04d-%02d-%02d", now.wYear, now.wMonth, now.wDay);
CreateWindowExA(0, "STATIC", "TRANSIT DRIVER CONSOLE",
CreateWindowExA(0, "STATIC", "DRIVER LOGIN",
WS_CHILD | WS_VISIBLE,
20, 10, 330, 24,
hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hwnd, (HMENU)ID_PAGE_TITLE, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowExA(0, "STATIC", "Schedule date (YYYY-MM-DD)",
WS_CHILD | WS_VISIBLE,
20, 38, 150, 20,
hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
20, 38, 150, 20,
hwnd, (HMENU)ID_DATE_LABEL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowExA(0, "EDIT", "2026-09-17",
CreateWindowExA(0, "EDIT", today,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
175, 36, 130, 24,
hwnd, (HMENU)ID_DATE, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
@@ -746,7 +875,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
CreateWindowExA(0, "STATIC", "User code",
WS_CHILD | WS_VISIBLE,
20, 72, 90, 20,
hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hwnd, (HMENU)ID_USER_LABEL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hUser = CreateWindowExA(0, "EDIT", "",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
@@ -757,7 +886,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
CreateWindowExA(0, "STATIC", "Password",
WS_CHILD | WS_VISIBLE,
20, 104, 90, 20,
hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hwnd, (HMENU)ID_PASSWORD_LABEL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hPassword = CreateWindowExA(0, "EDIT", "",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_PASSWORD | ES_AUTOHSCROLL,
@@ -782,7 +911,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
CreateWindowExA(0, "STATIC", "TODAY'S ROUTES",
WS_CHILD | WS_VISIBLE,
20, 208, 200, 20,
hwnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
hwnd, (HMENU)ID_ROUTES_LABEL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | LBS_NOTIFY | WS_VSCROLL,
@@ -799,7 +928,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
20, 545, 190, 30,
hwnd, (HMENU)ID_STOPS, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowExA(0, "BUTTON", "HALT",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW,
145, 120, 250, 250,
hwnd, (HMENU)ID_HALT, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
CreateWindowExA(0, "BUTTON", "Back to today's routes",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
20, 545, 190, 30,
hwnd, (HMENU)ID_BACK, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
SetWindowTextA(hOutput, "Driver console ready.\r\nLog in, choose a date, and refresh the schedule.");
ShowLoginScreen(hwnd);
return 0;
}
@@ -821,6 +961,35 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
PerformLoadStops(hwnd, state);
return 0;
}
if (LOWORD(wParam) == ID_BACK && HIWORD(wParam) == BN_CLICKED) {
state = (APPSTATE *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
ShowRouteScreen(hwnd);
SetStatusText(hwnd, "Today's routes");
return 0;
}
if (LOWORD(wParam) == ID_HALT && HIWORD(wParam) == BN_CLICKED) {
state = (APPSTATE *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (state->activeStop < state->stopCount) {
state->activeStop++;
UpdateHaltButton(hwnd, state);
}
return 0;
}
if (LOWORD(wParam) == ID_ROUTES && HIWORD(wParam) == LBN_SELCHANGE) {
state = (APPSTATE *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
PerformLoadStops(hwnd, state);
return 0;
}
break;
case WM_DRAWITEM:
if (wParam == ID_HALT) {
DrawHaltButton((DRAWITEMSTRUCT *)lParam);
return TRUE;
}
break;
case WM_DESTROY: