@ -54,9 +54,10 @@
# define CP_YELLOW 4
# define CP_CYAN 5
# define CP_MAGENTA 6
# define CP_STATUS 7 /* black on cyan */
# define CP_INPUT 8 /* white on blue */
# define CP_SEP 9 /* cyan on black */
# define CP_STATUS 7 /* white on blue - status bar */
# define CP_INPUT 8 /* black on white - input area */
# define CP_SEP 9 /* cyan on black - separators */
# define CP_FKEY 10 /* blue on white - fkey chip label */
/* ── Host config ─────────────────────────────────────────── */
typedef struct {
@ -141,10 +142,11 @@ static char hist_save[INPUT_MAX]; /* saved input while browsing */
/* ── ncurses windows ─────────────────────────────────────── */
WINDOW * status_win = NULL ;
WINDOW * out_win = NULL ;
WINDOW * sep_win = NULL ; /* "----- Monitor -----" between output and monitor */
WINDOW * sep_win = NULL ; /* separator between monitor and output panes */
WINDOW * mon_win = NULL ;
WINDOW * sep2_win = NULL ; /* "----- Input -----" above input area */
WINDOW * sep2_win = NULL ; /* separator above input area */
WINDOW * input_win = NULL ;
WINDOW * fkey_win = NULL ; /* function key hint bar at bottom */
/* ── Input buffer ────────────────────────────────────────── */
char input_buf [ INPUT_MAX ] ;
@ -684,9 +686,10 @@ void init_colors(void)
init_pair ( CP_YELLOW , COLOR_YELLOW , COLOR_BLACK ) ;
init_pair ( CP_CYAN , COLOR_CYAN , COLOR_BLACK ) ;
init_pair ( CP_MAGENTA , COLOR_MAGENTA , COLOR_BLACK ) ;
init_pair ( CP_STATUS , COLOR_ BLACK, COLOR_CYAN ) ;
init_pair ( CP_STATUS , COLOR_ WHITE, COLOR_BLUE ) ;
init_pair ( CP_INPUT , COLOR_BLACK , COLOR_WHITE ) ;
init_pair ( CP_SEP , COLOR_CYAN , COLOR_BLACK ) ;
init_pair ( CP_FKEY , COLOR_BLUE , COLOR_WHITE ) ;
}
@ -734,10 +737,11 @@ void create_windows(void)
if ( mon_win ) { delwin ( mon_win ) ; mon_win = NULL ; }
if ( sep2_win ) { delwin ( sep2_win ) ; sep2_win = NULL ; }
if ( input_win ) { delwin ( input_win ) ; input_win = NULL ; }
if ( fkey_win ) { delwin ( fkey_win ) ; fkey_win = NULL ; }
/* overhead = status(1) + sep2(1) + input(INPUT_OUTER)
/* overhead = status(1) + sep2(1) + input(INPUT_OUTER) + fkey(1)
* + sep ( 1 ) if monitor visible */
int overhead = 1 + 1 + INPUT_OUTER + ( mon_visible ? 1 : 0 ) ;
int overhead = 1 + 1 + INPUT_OUTER + 1 + ( mon_visible ? 1 : 0 ) ;
int content_rows = LINES - overhead ;
if ( content_rows < 4 ) content_rows = 4 ;
@ -789,33 +793,83 @@ void create_windows(void)
leaveok ( input_win , FALSE ) ;
keypad ( input_win , TRUE ) ;
nodelay ( input_win , TRUE ) ;
row + = INPUT_OUTER ;
/* Function key hint bar */
fkey_win = newwin ( 1 , COLS , row , 0 ) ;
wbkgd ( fkey_win , COLOR_PAIR ( CP_STATUS ) ) ;
leaveok ( fkey_win , TRUE ) ;
}
void draw_status ( void )
{
if ( ! status_win ) return ;
werase ( status_win ) ;
wattron ( status_win , COLOR_PAIR ( CP_STATUS ) | A_BOLD ) ;
wbkgd ( status_win , COLOR_PAIR ( CP_STATUS ) ) ;
wattron ( status_win , COLOR_PAIR ( CP_STATUS ) ) ;
/* Left: program name */
mvwprintw ( status_win , 0 , 1 , " TermTCP %s " , VERSION ) ;
/* Center: connection info */
char center [ 80 ] ;
if ( connected & & current_host > = 0 ) {
int elapsed = ( int ) ( time ( NULL ) - connect_time ) ;
mvwprintw ( status_win , 0 , 0 ,
" TermTCP v%s | %s | %02d:%02d | Mon:%s%s | Ctrl+A=Menu " ,
VERSION ,
hosts [ current_host ] . name ,
elapsed / 60 , elapsed % 60 ,
mon_visible ? " ON " : " OFF " ,
logging ? " | LOG " : " " ) ;
snprintf ( center , sizeof ( center ) , " %s [%02d:%02d] " ,
hosts [ current_host ] . name , elapsed / 60 , elapsed % 60 ) ;
} else {
mvwprintw ( status_win , 0 , 0 ,
" TermTCP v%s | Not connected | Mon:%s | Ctrl+A=Menu " ,
VERSION , mon_visible ? " ON " : " OFF " ) ;
snprintf ( center , sizeof ( center ) , " Not Connected " ) ;
}
int cx = ( COLS - ( int ) strlen ( center ) ) / 2 ;
if ( cx > 0 ) mvwprintw ( status_win , 0 , cx , " %s " , center ) ;
/* Right: UTC time + log indicator */
time_t now = time ( NULL ) ;
struct tm * t = gmtime ( & now ) ;
char right [ 32 ] ;
snprintf ( right , sizeof ( right ) , " %s%02d:%02d UTC " ,
logging ? " LOG " : " " , t - > tm_hour , t - > tm_min ) ;
int rx = COLS - ( int ) strlen ( right ) ;
if ( rx > 0 ) mvwprintw ( status_win , 0 , rx , " %s " , right ) ;
wattroff ( status_win , COLOR_PAIR ( CP_STATUS ) | A_BOLD ) ;
wattroff ( status_win , COLOR_PAIR ( CP_STATUS ) ) ;
wrefresh ( status_win ) ;
}
static void draw_fkeys ( void )
{
if ( ! fkey_win ) return ;
werase ( fkey_win ) ;
static const struct { const char * key ; const char * desc ; } fkeys [ ] = {
{ " F1 " , " Help " } ,
{ " F3 " , " Connect " } ,
{ " F4 " , " Disconnect " } ,
{ " F5 " , " Forms " } ,
{ " F6 " , " Log " } ,
{ " F10 " , " Quit " } ,
} ;
static const int nfkeys = 6 ;
int col = 0 ;
for ( int i = 0 ; i < nfkeys & & col < COLS - 2 ; i + + ) {
wattron ( fkey_win , COLOR_PAIR ( CP_FKEY ) | A_BOLD ) ;
mvwprintw ( fkey_win , 0 , col , " %s " , fkeys [ i ] . key ) ;
col + = ( int ) strlen ( fkeys [ i ] . key ) ;
wattroff ( fkey_win , COLOR_PAIR ( CP_FKEY ) | A_BOLD ) ;
wattron ( fkey_win , COLOR_PAIR ( CP_STATUS ) ) ;
mvwprintw ( fkey_win , 0 , col , " %-*s " , ( int ) strlen ( fkeys [ i ] . desc ) + 1 , fkeys [ i ] . desc ) ;
col + = ( int ) strlen ( fkeys [ i ] . desc ) + 2 ;
wattroff ( fkey_win , COLOR_PAIR ( CP_STATUS ) ) ;
}
/* Fill remainder of bar */
wattron ( fkey_win , COLOR_PAIR ( CP_STATUS ) ) ;
for ( int c = col ; c < COLS ; c + + ) mvwaddch ( fkey_win , 0 , c , ' ' ) ;
wattroff ( fkey_win , COLOR_PAIR ( CP_STATUS ) ) ;
wrefresh ( fkey_win ) ;
}
/* Render stored lines from a ring buffer into an ncurses window.
* scroll_off = 0 shows the most recent lines ( live ) ; > 0 scrolls back . */
static void scrl_draw_window ( WINDOW * win , ScrlLine * buf , int count , int head ,
@ -1037,6 +1091,7 @@ static void refresh_all(void)
scrl_redraw_out ( ) ;
scrl_redraw_mon ( ) ;
draw_input ( ) ;
draw_fkeys ( ) ;
}
/* Full background repaint using redrawwin() — safe alternative to
@ -1049,6 +1104,7 @@ static void force_repaint(void)
if ( mon_win ) redrawwin ( mon_win ) ;
if ( sep2_win ) redrawwin ( sep2_win ) ;
if ( input_win ) redrawwin ( input_win ) ;
if ( fkey_win ) redrawwin ( fkey_win ) ;
refresh_all ( ) ;
}
@ -2756,6 +2812,50 @@ static void handle_key(int ch)
ctrl_a_menu ( ) ;
return ;
case KEY_F ( 1 ) :
show_help ( ) ;
return ;
case KEY_F ( 3 ) : {
int idx = pick_host ( " Select Host " ) ;
if ( idx > = 0 ) do_connect ( idx ) ;
return ;
}
case KEY_F ( 4 ) :
if ( connected ) {
tcp_disconnect ( ) ;
append_out ( " *** Disconnected \n " , 17 , COLOR_PAIR ( CP_RED ) ) ;
draw_status ( ) ;
}
return ;
case KEY_F ( 5 ) :
forms_menu ( ) ;
return ;
case KEY_F ( 6 ) :
if ( logging ) {
close_log ( ) ;
append_out ( " [Logging OFF] \n " , 14 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else if ( connected & & current_host > = 0 ) {
char prefix [ 120 ] ;
snprintf ( prefix , sizeof ( prefix ) , " %s_%s " ,
hosts [ current_host ] . host , hosts [ current_host ] . port ) ;
open_log ( prefix ) ;
append_out ( " [Logging ON] \n " , 13 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else {
append_out ( " Connect first to start logging. \n " , 32 , COLOR_PAIR ( CP_YELLOW ) ) ;
}
draw_status ( ) ;
return ;
case KEY_F ( 10 ) :
if ( connected ) tcp_disconnect ( ) ;
if ( logging ) close_log ( ) ;
endwin ( ) ;
exit ( 0 ) ;
case KEY_RESIZE :
endwin ( ) ; refresh ( ) ; clear ( ) ;
create_windows ( ) ;