diff --git a/dvx/dvxApp.c b/dvx/dvxApp.c index 90545f9..44a0fc0 100644 --- a/dvx/dvxApp.c +++ b/dvx/dvxApp.c @@ -24,6 +24,8 @@ static void drawCursorAt(AppContextT *ctx, int32_t x, int32_t y); static void handleMouseButton(AppContextT *ctx, int32_t mx, int32_t my, int32_t buttons); static void initColorScheme(AppContextT *ctx); static void initMouse(AppContextT *ctx); +static void pollAnsiTermWidgets(AppContextT *ctx); +static void pollAnsiTermWidgetsWalk(AppContextT *ctx, WidgetT *w, WindowT *win); static void pollKeyboard(AppContextT *ctx); static void pollMouse(AppContextT *ctx); static void refreshMinimizedIcons(AppContextT *ctx); @@ -536,6 +538,7 @@ bool dvxUpdate(AppContextT *ctx) { pollMouse(ctx); pollKeyboard(ctx); dispatchEvents(ctx); + pollAnsiTermWidgets(ctx); // Periodically refresh one minimized window thumbnail (staggered) ctx->frameCount++; @@ -819,6 +822,41 @@ static void initMouse(AppContextT *ctx) { } +// ============================================================ +// pollAnsiTermWidgets — poll and repaint all ANSI term widgets +// ============================================================ + +static void pollAnsiTermWidgets(AppContextT *ctx) { + for (int32_t i = 0; i < ctx->stack.count; i++) { + WindowT *win = ctx->stack.windows[i]; + + if (win->widgetRoot) { + pollAnsiTermWidgetsWalk(ctx, win->widgetRoot, win); + } + } +} + + +// ============================================================ +// pollAnsiTermWidgetsWalk — recursive helper +// ============================================================ + +static void pollAnsiTermWidgetsWalk(AppContextT *ctx, WidgetT *w, WindowT *win) { + if (w->type == WidgetAnsiTermE) { + wgtAnsiTermPoll(w); + + if (wgtAnsiTermRepaint(w) > 0) { + win->contentDirty = true; + dvxInvalidateWindow(ctx, win); + } + } + + for (WidgetT *child = w->firstChild; child; child = child->nextSibling) { + pollAnsiTermWidgetsWalk(ctx, child, win); + } +} + + // ============================================================ // pollKeyboard // ============================================================ diff --git a/dvx/dvxWidget.h b/dvx/dvxWidget.h index 62eca5a..0a7310a 100644 --- a/dvx/dvxWidget.h +++ b/dvx/dvxWidget.h @@ -296,6 +296,7 @@ typedef struct WidgetT { bool cursorVisible; bool wrapMode; // auto-wrap at right margin bool bold; // SGR bold flag (brightens foreground) + bool originMode; // cursor positioning relative to scroll region bool csiPrivate; // '?' prefix in CSI sequence uint8_t curAttr; // current text attribute (fg | bg<<4) uint8_t parseState; // 0=normal, 1=ESC, 2=CSI @@ -303,6 +304,9 @@ typedef struct WidgetT { int32_t paramCount; // number of CSI params collected int32_t savedRow; // saved cursor position (SCP) int32_t savedCol; + // Scrolling region (0-based, inclusive) + int32_t scrollTop; // top row of scroll region + int32_t scrollBot; // bottom row of scroll region // Scrollback uint8_t *scrollback; // circular buffer of scrollback lines int32_t scrollbackMax; // max lines in scrollback buffer diff --git a/dvx/widgets/widgetAnsiTerm.c b/dvx/widgets/widgetAnsiTerm.c index def02ae..5bfc913 100644 --- a/dvx/widgets/widgetAnsiTerm.c +++ b/dvx/widgets/widgetAnsiTerm.c @@ -61,7 +61,6 @@ static const int32_t sAnsiToCga[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; static void ansiTermAddToScrollback(WidgetT *w, int32_t screenRow); static void ansiTermDeleteLines(WidgetT *w, int32_t count); -static void ansiTermDirtyAll(WidgetT *w); static void ansiTermDirtyRange(WidgetT *w, int32_t startCell, int32_t count); static void ansiTermDirtyRow(WidgetT *w, int32_t row); static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd); @@ -105,15 +104,6 @@ static void ansiTermAddToScrollback(WidgetT *w, int32_t screenRow) { } -// ============================================================ -// ansiTermDirtyAll -// ============================================================ - -static void ansiTermDirtyAll(WidgetT *w) { - w->as.ansiTerm.dirtyRows = 0xFFFFFFFF; -} - - // ============================================================ // ansiTermDirtyRange // ============================================================ @@ -148,32 +138,34 @@ static void ansiTermDirtyRow(WidgetT *w, int32_t row) { static void ansiTermDeleteLines(WidgetT *w, int32_t count) { int32_t cols = w->as.ansiTerm.cols; - int32_t rows = w->as.ansiTerm.rows; + int32_t bot = w->as.ansiTerm.scrollBot; int32_t row = w->as.ansiTerm.cursorRow; - if (count > rows - row) { - count = rows - row; + if (count > bot - row + 1) { + count = bot - row + 1; } if (count <= 0) { return; } - uint8_t *cells = w->as.ansiTerm.cells; + uint8_t *cells = w->as.ansiTerm.cells; + int32_t bytesPerRow = cols * 2; - // Shift lines up - int32_t bytesPerRow = cols * 2; - memmove(cells + row * bytesPerRow, - cells + (row + count) * bytesPerRow, - (rows - row - count) * bytesPerRow); + // Shift lines up within region + if (row + count <= bot) { + memmove(cells + row * bytesPerRow, + cells + (row + count) * bytesPerRow, + (bot - row - count + 1) * bytesPerRow); + } - // Clear the bottom lines - for (int32_t r = rows - count; r < rows; r++) { + // Clear the bottom lines of the region + for (int32_t r = bot - count + 1; r <= bot; r++) { ansiTermFillCells(w, r * cols, cols); } - // All rows from cursorRow down are affected - for (int32_t r = row; r < rows; r++) { + // Dirty affected rows + for (int32_t r = row; r <= bot; r++) { ansiTermDirtyRow(w, r); } } @@ -192,6 +184,12 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { int32_t mode = (n >= 1) ? p[0] : 0; if (cmd == 'h') { + if (mode == 6) { + w->as.ansiTerm.originMode = true; + w->as.ansiTerm.cursorRow = w->as.ansiTerm.scrollTop; + w->as.ansiTerm.cursorCol = 0; + } + if (mode == 7) { w->as.ansiTerm.wrapMode = true; } @@ -200,6 +198,12 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { w->as.ansiTerm.cursorVisible = true; } } else if (cmd == 'l') { + if (mode == 6) { + w->as.ansiTerm.originMode = false; + w->as.ansiTerm.cursorRow = 0; + w->as.ansiTerm.cursorCol = 0; + } + if (mode == 7) { w->as.ansiTerm.wrapMode = false; } @@ -213,13 +217,40 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { } switch (cmd) { - case 'A': // CUU - cursor up + case '@': // ICH - insert character { int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + int32_t cols = w->as.ansiTerm.cols; + int32_t row = w->as.ansiTerm.cursorRow; + int32_t col = w->as.ansiTerm.cursorCol; + + if (count > cols - col) { + count = cols - col; + } + + if (count > 0) { + uint8_t *base = w->as.ansiTerm.cells + row * cols * 2; + memmove(base + (col + count) * 2, base + col * 2, (cols - col - count) * 2); + + for (int32_t i = col; i < col + count; i++) { + base[i * 2] = ' '; + base[i * 2 + 1] = w->as.ansiTerm.curAttr; + } + + ansiTermDirtyRow(w, row); + } + + break; + } + + case 'A': // CUU - cursor up + { + int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + int32_t minRow = w->as.ansiTerm.originMode ? w->as.ansiTerm.scrollTop : 0; w->as.ansiTerm.cursorRow -= count; - if (w->as.ansiTerm.cursorRow < 0) { - w->as.ansiTerm.cursorRow = 0; + if (w->as.ansiTerm.cursorRow < minRow) { + w->as.ansiTerm.cursorRow = minRow; } break; @@ -227,11 +258,12 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { case 'B': // CUD - cursor down { - int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + int32_t maxRow = w->as.ansiTerm.originMode ? w->as.ansiTerm.scrollBot : w->as.ansiTerm.rows - 1; w->as.ansiTerm.cursorRow += count; - if (w->as.ansiTerm.cursorRow >= w->as.ansiTerm.rows) { - w->as.ansiTerm.cursorRow = w->as.ansiTerm.rows - 1; + if (w->as.ansiTerm.cursorRow > maxRow) { + w->as.ansiTerm.cursorRow = maxRow; } break; @@ -261,12 +293,44 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { break; } + case 'c': // DA - device attributes + { + if (w->as.ansiTerm.commWrite) { + // Respond as VT100 with advanced video option + const uint8_t reply[] = "\033[?1;2c"; + w->as.ansiTerm.commWrite(w->as.ansiTerm.commCtx, reply, 7); + } + + break; + } + + case 'E': // CNL - cursor next line + { + int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + + for (int32_t i = 0; i < count; i++) { + ansiTermNewline(w); + } + + w->as.ansiTerm.cursorCol = 0; + break; + } + case 'H': // CUP - cursor position case 'f': // HVP - same { int32_t row = (n >= 1 && p[0]) ? p[0] - 1 : 0; int32_t col = (n >= 2 && p[1]) ? p[1] - 1 : 0; + // Origin mode: row is relative to scroll region + if (w->as.ansiTerm.originMode) { + row += w->as.ansiTerm.scrollTop; + + if (row > w->as.ansiTerm.scrollBot) { + row = w->as.ansiTerm.scrollBot; + } + } + if (row < 0) { row = 0; } @@ -316,6 +380,32 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { break; } + case 'P': // DCH - delete character + { + int32_t count = (n >= 1 && p[0]) ? p[0] : 1; + int32_t cols = w->as.ansiTerm.cols; + int32_t row = w->as.ansiTerm.cursorRow; + int32_t col = w->as.ansiTerm.cursorCol; + + if (count > cols - col) { + count = cols - col; + } + + if (count > 0) { + uint8_t *base = w->as.ansiTerm.cells + row * cols * 2; + memmove(base + col * 2, base + (col + count) * 2, (cols - col - count) * 2); + + for (int32_t i = cols - count; i < cols; i++) { + base[i * 2] = ' '; + base[i * 2 + 1] = w->as.ansiTerm.curAttr; + } + + ansiTermDirtyRow(w, row); + } + + break; + } + case 'S': // SU - scroll up { int32_t count = (n >= 1 && p[0]) ? p[0] : 1; @@ -338,10 +428,72 @@ static void ansiTermDispatchCsi(WidgetT *w, uint8_t cmd) { break; } + case 'Z': // CBT - back tab + { + int32_t col = w->as.ansiTerm.cursorCol; + + if (col > 0) { + col = ((col - 1) / 8) * 8; + } + + w->as.ansiTerm.cursorCol = col; + break; + } + case 'm': // SGR - select graphic rendition ansiTermProcessSgr(w); break; + case 'n': // DSR - device status report + { + int32_t mode = (n >= 1) ? p[0] : 0; + + if (w->as.ansiTerm.commWrite) { + if (mode == 6) { + // CPR — cursor position report: ESC[row;colR (1-based) + char reply[16]; + int32_t len = snprintf(reply, sizeof(reply), "\033[%ld;%ldR", (long)(w->as.ansiTerm.cursorRow + 1), (long)(w->as.ansiTerm.cursorCol + 1)); + w->as.ansiTerm.commWrite(w->as.ansiTerm.commCtx, (const uint8_t *)reply, len); + } else if (mode == 255) { + // Screen size report + char reply[16]; + int32_t len = snprintf(reply, sizeof(reply), "\033[%ld;%ldR", (long)w->as.ansiTerm.rows, (long)w->as.ansiTerm.cols); + w->as.ansiTerm.commWrite(w->as.ansiTerm.commCtx, (const uint8_t *)reply, len); + } + } + + break; + } + + case 'r': // DECSTBM - set scrolling region + { + int32_t rows = w->as.ansiTerm.rows; + int32_t top = (n >= 1 && p[0]) ? p[0] - 1 : 0; + int32_t bot = (n >= 2 && p[1]) ? p[1] - 1 : rows - 1; + + if (top < 0) { + top = 0; + } + + if (bot >= rows) { + bot = rows - 1; + } + + if (top < bot) { + w->as.ansiTerm.scrollTop = top; + w->as.ansiTerm.scrollBot = bot; + } else { + // Invalid or reset — restore full screen + w->as.ansiTerm.scrollTop = 0; + w->as.ansiTerm.scrollBot = rows - 1; + } + + // Home cursor (relative to region if origin mode) + w->as.ansiTerm.cursorRow = w->as.ansiTerm.originMode ? w->as.ansiTerm.scrollTop : 0; + w->as.ansiTerm.cursorCol = 0; + break; + } + case 's': // SCP - save cursor position w->as.ansiTerm.savedRow = w->as.ansiTerm.cursorRow; w->as.ansiTerm.savedCol = w->as.ansiTerm.cursorCol; @@ -475,32 +627,34 @@ static const uint8_t *ansiTermGetLine(WidgetT *w, int32_t lineIndex) { static void ansiTermInsertLines(WidgetT *w, int32_t count) { int32_t cols = w->as.ansiTerm.cols; - int32_t rows = w->as.ansiTerm.rows; + int32_t bot = w->as.ansiTerm.scrollBot; int32_t row = w->as.ansiTerm.cursorRow; - if (count > rows - row) { - count = rows - row; + if (count > bot - row + 1) { + count = bot - row + 1; } if (count <= 0) { return; } - uint8_t *cells = w->as.ansiTerm.cells; + uint8_t *cells = w->as.ansiTerm.cells; + int32_t bytesPerRow = cols * 2; - // Shift lines down - int32_t bytesPerRow = cols * 2; - memmove(cells + (row + count) * bytesPerRow, - cells + row * bytesPerRow, - (rows - row - count) * bytesPerRow); + // Shift lines down within region + if (row + count <= bot) { + memmove(cells + (row + count) * bytesPerRow, + cells + row * bytesPerRow, + (bot - row - count + 1) * bytesPerRow); + } // Clear the inserted lines for (int32_t r = row; r < row + count; r++) { ansiTermFillCells(w, r * cols, cols); } - // All rows from cursorRow down are affected - for (int32_t r = row; r < rows; r++) { + // Dirty affected rows + for (int32_t r = row; r <= bot; r++) { ansiTermDirtyRow(w, r); } } @@ -513,11 +667,12 @@ static void ansiTermInsertLines(WidgetT *w, int32_t count) { // Move cursor to next line, scrolling if at the bottom. static void ansiTermNewline(WidgetT *w) { - w->as.ansiTerm.cursorRow++; + int32_t bot = w->as.ansiTerm.scrollBot; - if (w->as.ansiTerm.cursorRow >= w->as.ansiTerm.rows) { - w->as.ansiTerm.cursorRow = w->as.ansiTerm.rows - 1; + if (w->as.ansiTerm.cursorRow == bot) { ansiTermScrollUp(w); + } else if (w->as.ansiTerm.cursorRow < w->as.ansiTerm.rows - 1) { + w->as.ansiTerm.cursorRow++; } } @@ -547,6 +702,11 @@ static void ansiTermProcessByte(WidgetT *w, uint8_t ch) { if (w->as.ansiTerm.cursorCol >= w->as.ansiTerm.cols) { w->as.ansiTerm.cursorCol = w->as.ansiTerm.cols - 1; } + } else if (ch == '\f') { + // Form feed — clear screen and home cursor + ansiTermEraseDisplay(w, 2); + w->as.ansiTerm.cursorRow = 0; + w->as.ansiTerm.cursorCol = 0; } else if (ch == '\a') { // Bell — ignored } else { @@ -562,6 +722,27 @@ static void ansiTermProcessByte(WidgetT *w, uint8_t ch) { w->as.ansiTerm.paramCount = 0; w->as.ansiTerm.csiPrivate = false; memset(w->as.ansiTerm.params, 0, sizeof(w->as.ansiTerm.params)); + } else if (ch == 'D') { + // IND — scroll up one line + ansiTermScrollUp(w); + w->as.ansiTerm.parseState = PARSE_NORMAL; + } else if (ch == 'M') { + // RI — scroll down one line + ansiTermScrollDown(w); + w->as.ansiTerm.parseState = PARSE_NORMAL; + } else if (ch == 'c') { + // RIS — terminal reset + w->as.ansiTerm.cursorRow = 0; + w->as.ansiTerm.cursorCol = 0; + w->as.ansiTerm.curAttr = ANSI_DEFAULT_ATTR; + w->as.ansiTerm.bold = false; + w->as.ansiTerm.wrapMode = true; + w->as.ansiTerm.originMode = false; + w->as.ansiTerm.cursorVisible = true; + w->as.ansiTerm.scrollTop = 0; + w->as.ansiTerm.scrollBot = w->as.ansiTerm.rows - 1; + ansiTermEraseDisplay(w, 2); + w->as.ansiTerm.parseState = PARSE_NORMAL; } else { // Unknown escape — return to normal w->as.ansiTerm.parseState = PARSE_NORMAL; @@ -635,6 +816,9 @@ static void ansiTermProcessSgr(WidgetT *w) { uint8_t tmp = fg; fg = bg; bg = tmp; + } else if (code == 8) { + // Invisible — foreground same as background + fg = bg & 0x07; } else if (code == 22) { // Normal intensity w->as.ansiTerm.bold = false; @@ -697,18 +881,26 @@ static void ansiTermPutChar(WidgetT *w, uint8_t ch) { // ============================================================ static void ansiTermScrollDown(WidgetT *w) { - int32_t cols = w->as.ansiTerm.cols; - int32_t rows = w->as.ansiTerm.rows; - uint8_t *cells = w->as.ansiTerm.cells; + int32_t cols = w->as.ansiTerm.cols; + int32_t top = w->as.ansiTerm.scrollTop; + int32_t bot = w->as.ansiTerm.scrollBot; + uint8_t *cells = w->as.ansiTerm.cells; int32_t bytesPerRow = cols * 2; - // Shift all lines down by one - memmove(cells + bytesPerRow, cells, (rows - 1) * bytesPerRow); + // Shift lines within region down by one + if (bot > top) { + memmove(cells + (top + 1) * bytesPerRow, + cells + top * bytesPerRow, + (bot - top) * bytesPerRow); + } - // Clear the top line - ansiTermFillCells(w, 0, cols); + // Clear the top line of the region + ansiTermFillCells(w, top * cols, cols); - ansiTermDirtyAll(w); + // Dirty affected rows + for (int32_t r = top; r <= bot && r < 32; r++) { + w->as.ansiTerm.dirtyRows |= (1U << r); + } } @@ -720,29 +912,36 @@ static void ansiTermScrollDown(WidgetT *w) { // the scrollback buffer before being discarded. static void ansiTermScrollUp(WidgetT *w) { - int32_t cols = w->as.ansiTerm.cols; - int32_t rows = w->as.ansiTerm.rows; - uint8_t *cells = w->as.ansiTerm.cells; + int32_t cols = w->as.ansiTerm.cols; + int32_t top = w->as.ansiTerm.scrollTop; + int32_t bot = w->as.ansiTerm.scrollBot; + uint8_t *cells = w->as.ansiTerm.cells; int32_t bytesPerRow = cols * 2; - // Track whether the view was following live output - bool wasAtBottom = (w->as.ansiTerm.scrollPos == w->as.ansiTerm.scrollbackCount); + // Only push to scrollback when scrolling the full screen + if (top == 0 && bot == w->as.ansiTerm.rows - 1) { + bool wasAtBottom = (w->as.ansiTerm.scrollPos == w->as.ansiTerm.scrollbackCount); + ansiTermAddToScrollback(w, 0); - // Push top line to scrollback - ansiTermAddToScrollback(w, 0); - - // Shift all lines up by one - memmove(cells, cells + bytesPerRow, (rows - 1) * bytesPerRow); - - // Clear the bottom line - ansiTermFillCells(w, (rows - 1) * cols, cols); - - // Keep view at bottom if it was following live output - if (wasAtBottom) { - w->as.ansiTerm.scrollPos = w->as.ansiTerm.scrollbackCount; + if (wasAtBottom) { + w->as.ansiTerm.scrollPos = w->as.ansiTerm.scrollbackCount; + } } - ansiTermDirtyAll(w); + // Shift lines within region up by one + if (bot > top) { + memmove(cells + top * bytesPerRow, + cells + (top + 1) * bytesPerRow, + (bot - top) * bytesPerRow); + } + + // Clear the bottom line of the region + ansiTermFillCells(w, bot * cols, cols); + + // Dirty affected rows + for (int32_t r = top; r <= bot && r < 32; r++) { + w->as.ansiTerm.dirtyRows |= (1U << r); + } } @@ -794,12 +993,15 @@ WidgetT *wgtAnsiTerm(WidgetT *parent, int32_t cols, int32_t rows) { w->as.ansiTerm.cursorVisible = true; w->as.ansiTerm.wrapMode = true; w->as.ansiTerm.bold = false; + w->as.ansiTerm.originMode = false; w->as.ansiTerm.csiPrivate = false; w->as.ansiTerm.curAttr = ANSI_DEFAULT_ATTR; w->as.ansiTerm.parseState = PARSE_NORMAL; w->as.ansiTerm.paramCount = 0; w->as.ansiTerm.savedRow = 0; w->as.ansiTerm.savedCol = 0; + w->as.ansiTerm.scrollTop = 0; + w->as.ansiTerm.scrollBot = rows - 1; w->as.ansiTerm.scrollbackMax = sbMax; w->as.ansiTerm.scrollbackCount = 0; w->as.ansiTerm.scrollbackHead = 0; diff --git a/proxy/proxy.c b/proxy/proxy.c index c7ffaa1..c553ace 100644 --- a/proxy/proxy.c +++ b/proxy/proxy.c @@ -76,7 +76,6 @@ static int sClientFd = -1; static int connectToBbs(const char *host, int port); static int createListenSocket(int port); -static void hexDump(const char *label, const uint8_t *data, int len); static void onRecvFromDos(void *ctx, const uint8_t *data, int len, uint8_t channel); static void seedRng(void); static void sigHandler(int sig); @@ -152,28 +151,11 @@ static int createListenSocket(int port) { } -static void hexDump(const char *label, const uint8_t *data, int len) { - printf("%s (%d bytes):", label, len); - for (int i = 0; i < len && i < 64; i++) { - if (i % 16 == 0) { - printf("\n "); - } - printf("%02X ", data[i]); - } - if (len > 64) { - printf("\n ..."); - } - printf("\n"); -} - - static void onRecvFromDos(void *ctx, const uint8_t *data, int len, uint8_t channel) { int bbsFd = *(int *)ctx; (void)channel; - hexDump("DOS->proxy (decrypted)", data, len); - // Check for ENTER before BBS is connected if (!sGotEnter) { for (int i = 0; i < len; i++) { @@ -186,8 +168,6 @@ static void onRecvFromDos(void *ctx, const uint8_t *data, int len, uint8_t chann return; } - hexDump("DOS->BBS", data, len); - int sent = 0; while (sent < len) { ssize_t n = write(bbsFd, data + sent, len - sent); @@ -487,8 +467,6 @@ int main(int argc, char *argv[]) { int cleanLen = telnetFilter(bbsFd, raw, (int)n, clean); if (cleanLen > 0) { - hexDump("BBS->DOS", clean, cleanLen); - // Retry with ACK processing if the send window is full int rc = secLinkSend(link, clean, cleanLen, CHANNEL_TERMINAL, true, false); while (rc != SECLINK_SUCCESS && sRunning) { diff --git a/termdemo/termdemo.c b/termdemo/termdemo.c index 5f0fbde..c037f33 100644 --- a/termdemo/termdemo.c +++ b/termdemo/termdemo.c @@ -11,8 +11,6 @@ #include "dvxWidget.h" #include "secLink.h" #include "security.h" -#include "../rs232/rs232.h" - #include #include #include @@ -190,15 +188,6 @@ int main(int argc, char *argv[]) { TermContextT tc; memset(&tc, 0, sizeof(tc)); - // Test rs232 directly first for diagnostics - printf("Testing rs232Open on COM%d...\n", comPort + 1); - int rsRc = rs232Open(comPort, baudRate, 8, 'N', 1, 0); - printf("rs232Open returned %d\n", rsRc); - if (rsRc == RS232_SUCCESS) { - printf("UART type: %d\n", rs232GetUartType(comPort)); - rs232Close(comPort); - } - // Open secLink on the specified COM port printf("Opening SecLink on COM%d...\n", comPort + 1); tc.link = secLinkOpen(comPort, baudRate, 8, 'N', 1, 0, onRecv, &tc); @@ -282,15 +271,9 @@ int main(int argc, char *argv[]) { ctx.idleCallback = idlePoll; ctx.idleCtx = &tc; - // Main loop — poll serial, render immediately, composite + // Main loop — poll serial, dvxUpdate handles terminal widget automatically while (ctx.running) { secLinkPoll(tc.link); - wgtAnsiTermPoll(term); - - if (wgtAnsiTermRepaint(term) > 0) { - win->contentDirty = true; - dvxInvalidateWindow(&ctx, win); - } if (!dvxUpdate(&ctx)) { break;