From 36029f1d2abb863aa9a14006c1e736abd53e2a8a Mon Sep 17 00:00:00 2001 From: Ryan Collier Date: Mon, 29 Jun 2026 09:33:14 -0400 Subject: [PATCH] Receive data on all sessions simultaneously via multi-socket select interactive_loop now watches every connected session's socket in one select() call. Background session data is stored in that session's scroll buffers without touching the screen (suppress_draw flag). When the user switches to that session, refresh_all() replays everything from the buffer. Co-Authored-By: Claude Sonnet 4.6 --- termtcp.c | 54 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/termtcp.c b/termtcp.c index febe042..b9c0800 100644 --- a/termtcp.c +++ b/termtcp.c @@ -149,6 +149,9 @@ static void on_sigint(int signum) sigint_pending = 1; } +/* set while receiving data for a background session — suppresses live screen writes */ +static int suppress_draw = 0; + /* ── BPQ / monitor state (shared across all sessions) ─────── */ int mon_visible = 1; int mon_tx = 1; @@ -954,11 +957,12 @@ void append_mon(const char *text, int len, int attr) const char *p = text; const char *end = text + len; + int live = (mon_win != NULL && !suppress_draw); S->mon_part_cur = attr; if (S->mon_part_len == 0) S->mon_part_base = attr; - if (mon_win && S->mon_scroll == 0) + if (live && S->mon_scroll == 0) wattron(mon_win, S->mon_part_cur); while (p < end) { @@ -969,7 +973,7 @@ void append_mon(const char *text, int len, int attr) S->mon_part[S->mon_part_len++] = (char)0x1B; S->mon_part[S->mon_part_len++] = p[1]; } - if (mon_win && S->mon_scroll == 0) { + if (live && S->mon_scroll == 0) { wattroff(mon_win, S->mon_part_cur); wattron(mon_win, na); } @@ -984,19 +988,19 @@ void append_mon(const char *text, int len, int attr) if (S->mon_sbuf_count < SCRL_MAX) S->mon_sbuf_count++; S->mon_part_len = 0; S->mon_part_base = S->mon_part_cur; - if (mon_win && S->mon_scroll == 0) + if (live && S->mon_scroll == 0) waddch(mon_win, '\n'); p++; } else if (c >= 32 && c < 127) { if (S->mon_part_len < SCRL_W - 1) S->mon_part[S->mon_part_len++] = (char)c; - if (mon_win && S->mon_scroll == 0) + if (live && S->mon_scroll == 0) waddch(mon_win, c); p++; } else { p++; } } - if (mon_win && S->mon_scroll == 0) { + if (live && S->mon_scroll == 0) { wattroff(mon_win, S->mon_part_cur); wrefresh(mon_win); if (input_win) wrefresh(input_win); @@ -1009,11 +1013,12 @@ void append_out(const char *text, int len, int attr) const char *p = text; const char *end = text + len; + int live = (out_win != NULL && !suppress_draw); S->out_part_cur = attr; if (S->out_part_len == 0) S->out_part_base = attr; - if (out_win && S->out_scroll == 0) + if (live && S->out_scroll == 0) wattron(out_win, S->out_part_cur); while (p < end) { @@ -1024,7 +1029,7 @@ void append_out(const char *text, int len, int attr) S->out_part[S->out_part_len++] = (char)0x1B; S->out_part[S->out_part_len++] = p[1]; } - if (out_win && S->out_scroll == 0) { + if (live && S->out_scroll == 0) { wattroff(out_win, S->out_part_cur); wattron(out_win, na); } @@ -1039,19 +1044,19 @@ void append_out(const char *text, int len, int attr) if (S->out_sbuf_count < SCRL_MAX) S->out_sbuf_count++; S->out_part_len = 0; S->out_part_base = S->out_part_cur; - if (out_win && S->out_scroll == 0) + if (live && S->out_scroll == 0) waddch(out_win, '\n'); p++; } else if (c >= 32 && c < 127) { if (S->out_part_len < SCRL_W - 1) S->out_part[S->out_part_len++] = (char)c; - if (out_win && S->out_scroll == 0) + if (live && S->out_scroll == 0) waddch(out_win, c); p++; } else { p++; } } - if (out_win && S->out_scroll == 0) { + if (live && S->out_scroll == 0) { wattroff(out_win, S->out_part_cur); wrefresh(out_win); if (input_win) wrefresh(input_win); @@ -3165,27 +3170,46 @@ void interactive_loop(void) draw_status(); + /* Watch stdin + every connected session's socket */ FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); - if (S->connected && S->sock >= 0) FD_SET(S->sock, &readfds); + int maxfd = STDIN_FILENO; + for (int i = 0; i < MAX_SESSIONS; i++) { + if (sessions[i].active && sessions[i].connected && sessions[i].sock >= 0) { + FD_SET(sessions[i].sock, &readfds); + if (sessions[i].sock > maxfd) maxfd = sessions[i].sock; + } + } tv.tv_sec = 1; tv.tv_usec = 0; - int maxfd = (S->connected && S->sock > STDIN_FILENO) ? S->sock : STDIN_FILENO; int ready = select(maxfd + 1, &readfds, NULL, NULL, &tv); if (ready < 0) continue; - /* Network data */ - if (S->connected && S->sock >= 0 && FD_ISSET(S->sock, &readfds)) { + /* Network data — process all active sessions */ + Session *active = S; + for (int i = 0; i < MAX_SESSIONS; i++) { + if (!sessions[i].active || !sessions[i].connected || sessions[i].sock < 0) + continue; + if (!FD_ISSET(sessions[i].sock, &readfds)) + continue; + + int is_bg = (&sessions[i] != active); + S = &sessions[i]; + if (is_bg) suppress_draw = 1; + int n = recv(S->sock, recv_buf, sizeof(recv_buf)-1, 0); if (n > 0) { bpq_process(recv_buf, n); - draw_input(); + if (!is_bg) draw_input(); } else if (n == 0 || (n < 0 && errno != EAGAIN)) { append_out("\n*** Server closed connection ***\n", 33, COLOR_PAIR(CP_RED)); tcp_disconnect(); } + + suppress_draw = 0; + S = active; } /* Keyboard */