mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:44:38 +08:00
tui: dedicated MOTD renderer (M7-5)
The MOTD used to ride on tui_render_command_output's coattails — text
got stuffed into client->command_output prefixed by "=== 公告 / MOTD ==="
and rendered under a reverse-video " COMMAND OUTPUT " title bar. Two
different things (a transient command result vs. a one-shot service
notice) wearing the same chrome.
Now MOTD has its own renderer with its own aesthetic:
╭─ 公告 / MOTD ────────────────────────────────╮
欢迎来到 TNT 公共聊天室。
请互相尊重,不要刷屏。
管理员:m1ng
╰─ 按任意键继续 / press any key ────────────────╯
- title chip embedded in the top border (bright cyan)
- footer hint embedded in the bottom border (dim grey)
- 2-column left padding on body lines, blank top/bottom pad rows
- dim cyan borders, no full-line reverse anywhere
Wiring:
- new tui_render_motd() declared in tui.h
- new client_t.show_motd flag selects the renderer; command_output
remains the text storage (no extra buffer needed)
- input.c MOTD path sets show_motd = true and calls tui_render_motd()
- handle_key's dismiss path clears show_motd alongside command_output
- main-loop redraw dispatch checks show_motd before command_output[0]
This commit is contained in:
parent
2610bba76d
commit
d3ebe25973
4 changed files with 104 additions and 5 deletions
|
|
@ -25,6 +25,7 @@ typedef struct client {
|
|||
int command_history_count;
|
||||
int command_history_pos;
|
||||
char command_output[2048];
|
||||
bool show_motd; /* command_output holds MOTD text */
|
||||
char exec_command[MAX_EXEC_COMMAND_LEN];
|
||||
char ssh_login[MAX_USERNAME_LEN];
|
||||
time_t connect_time;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ void tui_render_help(struct client *client);
|
|||
/* Render the command output screen */
|
||||
void tui_render_command_output(struct client *client);
|
||||
|
||||
/* Render the MOTD screen. Reads the message text from
|
||||
* client->command_output (shared storage); the show_motd flag selects
|
||||
* this renderer over tui_render_command_output. */
|
||||
void tui_render_motd(struct client *client);
|
||||
|
||||
/* Render the input line */
|
||||
void tui_render_input(struct client *client, const char *input);
|
||||
|
||||
|
|
|
|||
13
src/input.c
13
src/input.c
|
|
@ -194,9 +194,10 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
|||
return true; /* Key consumed */
|
||||
}
|
||||
|
||||
/* Handle command output display */
|
||||
/* Handle command output / MOTD display: any key dismisses */
|
||||
if (client->command_output[0] != '\0') {
|
||||
client->command_output[0] = '\0';
|
||||
client->show_motd = false;
|
||||
client->mode = MODE_NORMAL;
|
||||
tui_render_screen(client);
|
||||
return true; /* Key consumed */
|
||||
|
|
@ -447,9 +448,11 @@ void input_run_session(client_t *client) {
|
|||
fclose(motd_fp);
|
||||
if (motd_len > 0) {
|
||||
motd_buf[motd_len] = '\0';
|
||||
snprintf(client->command_output, sizeof(client->command_output),
|
||||
"=== 公告 / MOTD ===\n%s", motd_buf);
|
||||
tui_render_command_output(client);
|
||||
snprintf(client->command_output,
|
||||
sizeof(client->command_output),
|
||||
"%s", motd_buf);
|
||||
client->show_motd = true;
|
||||
tui_render_motd(client);
|
||||
seen_update_seq = room_get_update_seq(g_room);
|
||||
goto main_loop;
|
||||
}
|
||||
|
|
@ -491,6 +494,8 @@ main_loop:
|
|||
|
||||
if (client->show_help) {
|
||||
tui_render_help(client);
|
||||
} else if (client->show_motd) {
|
||||
tui_render_motd(client);
|
||||
} else if (client->command_output[0] != '\0') {
|
||||
tui_render_command_output(client);
|
||||
} else {
|
||||
|
|
|
|||
90
src/tui.c
90
src/tui.c
|
|
@ -543,7 +543,95 @@ void tui_render_command_output(client_t *client) {
|
|||
client_send(client, buffer, pos);
|
||||
}
|
||||
|
||||
/* Get help text based on language */
|
||||
/* Render the MOTD screen.
|
||||
*
|
||||
* A framed banner with a title chip embedded in the top border and an
|
||||
* "any key to continue" hint embedded in the bottom border, MOTD body
|
||||
* left-padded inside. Dismissed by handle_key like any other modal
|
||||
* (sets command_output[0]='\0' and show_motd=false).
|
||||
*
|
||||
* Lighter aesthetic than tui_render_command_output: no full-line reverse,
|
||||
* dim borders, two blank lines of breathing room above and below the
|
||||
* body so the announcement reads as a notice rather than a console dump. */
|
||||
void tui_render_motd(client_t *client) {
|
||||
if (!client || !client->connected) return;
|
||||
|
||||
int rw = client->width;
|
||||
int rh = client->height;
|
||||
if (rw < 10) rw = 10;
|
||||
if (rh < 4) rh = 4;
|
||||
|
||||
char buffer[4096];
|
||||
size_t pos = 0;
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_CLEAR ANSI_HOME);
|
||||
|
||||
/* Top border: ╭─ 公告 / MOTD ──...──╮ */
|
||||
const char *title = " 公告 / MOTD ";
|
||||
int title_w = utf8_string_width(title);
|
||||
int top_dash_fill = rw - 2 - title_w - 1; /* 2 corners, 1 leading ─ */
|
||||
if (top_dash_fill < 0) top_dash_fill = 0;
|
||||
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\033[2;36m╭─");
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\033[0;1;36m%s\033[2;36m", title);
|
||||
for (int i = 0; i < top_dash_fill; i++) {
|
||||
buffer_append_bytes(buffer, sizeof(buffer), &pos, "─", strlen("─"));
|
||||
}
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "╮\033[0m\r\n");
|
||||
|
||||
/* Top breathing-room line */
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\r\n");
|
||||
|
||||
/* Body lines (left-pad 2 cols, truncate to inner width) */
|
||||
char body_copy[2048];
|
||||
strncpy(body_copy, client->command_output, sizeof(body_copy) - 1);
|
||||
body_copy[sizeof(body_copy) - 1] = '\0';
|
||||
|
||||
int body_lines = 0;
|
||||
int max_body_lines = rh - 4; /* top border + top pad + bottom pad + bottom border */
|
||||
if (max_body_lines < 1) max_body_lines = 1;
|
||||
|
||||
char *line = strtok(body_copy, "\n");
|
||||
while (line && body_lines < max_body_lines) {
|
||||
char truncated[1024];
|
||||
strncpy(truncated, line, sizeof(truncated) - 1);
|
||||
truncated[sizeof(truncated) - 1] = '\0';
|
||||
|
||||
int avail = rw - 4; /* 2 cols padding each side */
|
||||
if (avail < 4) avail = 4;
|
||||
if (utf8_string_width(truncated) > avail) {
|
||||
utf8_truncate(truncated, avail);
|
||||
}
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, " %s\r\n", truncated);
|
||||
body_lines++;
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
/* Fill empty space up to the bottom border */
|
||||
int used_rows = 1 /*top*/ + 1 /*pad*/ + body_lines + 1 /*pad*/ + 1 /*bottom*/;
|
||||
int filler_rows = rh - used_rows;
|
||||
if (filler_rows < 0) filler_rows = 0;
|
||||
for (int i = 0; i < filler_rows; i++) {
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\r\n");
|
||||
}
|
||||
|
||||
/* Bottom breathing-room line */
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\r\n");
|
||||
|
||||
/* Bottom border: ╰─ 按任意键继续 ─...─╯ */
|
||||
const char *footer = " 按任意键继续 / press any key ";
|
||||
int footer_w = utf8_string_width(footer);
|
||||
int bot_dash_fill = rw - 2 - footer_w - 1;
|
||||
if (bot_dash_fill < 0) bot_dash_fill = 0;
|
||||
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\033[2;36m╰─");
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "\033[0;2;37m%s\033[2;36m", footer);
|
||||
for (int i = 0; i < bot_dash_fill; i++) {
|
||||
buffer_append_bytes(buffer, sizeof(buffer), &pos, "─", strlen("─"));
|
||||
}
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos, "╯\033[0m");
|
||||
|
||||
client_send(client, buffer, pos);
|
||||
}
|
||||
const char* tui_get_help_text(help_lang_t lang) {
|
||||
if (lang == LANG_EN) {
|
||||
return "TERMINAL CHAT ROOM - HELP\n"
|
||||
|
|
|
|||
Loading…
Reference in a new issue