--- lbbs/src/screen.c 2025/10/16 11:26:16 1.110 +++ lbbs/src/screen.c 2025/11/08 09:05:59 1.128 @@ -1,18 +1,10 @@ -/*************************************************************************** - screen.c - description - ------------------- - Copyright : (C) 2004-2025 by Leaflet - Email : leaflet@leafok.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * screen + * - advanced telnet-based user interactive input / output features + * + * Copyright (C) 2004-2025 Leaflet + */ #include "bbs.h" #include "common.h" @@ -29,36 +21,30 @@ #include #include #include +#include #include #include #include #include -#define ACTIVE_BOARD_HEIGHT 8 +const char CTRL_SEQ_CLR_LINE[] = "\033[K"; -#define STR_TOP_LEFT_MAX_LEN 80 -#define STR_TOP_MIDDLE_MAX_LEN 40 -#define STR_TOP_RIGHT_MAX_LEN 80 +static const int ACTIVE_BOARD_HEIGHT = 8; -static const char *get_time_str(char *s, size_t len) +static const int STR_TOP_LEFT_MAX_LEN = 80; +static const int STR_TOP_MIDDLE_MAX_LEN = 40; +static const int STR_TOP_RIGHT_MAX_LEN = 80; + +static size_t get_time_str(char *s, size_t len) { - static const char *weekday[] = { - "天", "一", "二", "三", "四", "五", "六"}; time_t curtime; struct tm local_tm; time(&curtime); localtime_r(&curtime, &local_tm); - size_t j = strftime(s, len, "%b %d %H:%M 星期", &local_tm); - - if (j == 0 || j + strlen(weekday[local_tm.tm_wday]) + 1 > len) - { - return NULL; - } - - strncat(s, weekday[local_tm.tm_wday], len - 1 - j); + size_t j = strftime(s, len, "%m/%d %H:%M %Z", &local_tm); - return s; + return j; } void moveto(int row, int col) @@ -102,15 +88,32 @@ void clearscr() moveto(0, 0); } -int press_any_key() +inline int press_any_key() +{ + return press_any_key_ex(" \033[1;33m按任意键继续...\033[m", 60); +} + +int press_any_key_ex(const char *msg, int sec) { + int ch = 0; + int duration = 0; + time_t t_begin = time(NULL); + moveto(SCREEN_ROWS, 0); clrtoeol(); - prints(" \033[1;33m按任意键继续...\033[0;37m"); + prints(msg); iflush(); - return igetch_t(MIN(MAX_DELAY_TIME, 60)); + igetch_reset(); + + do + { + ch = igetch_t(sec - duration); + duration = (int)(time(NULL) - t_begin); + } while (!SYS_server_exit && ch == 0 && duration < 60); + + return ch; } void set_input_echo(int echo) @@ -118,26 +121,24 @@ void set_input_echo(int echo) if (echo) { outc('\x83'); // ASCII code 131 - iflush(); } else { - // outc ('\x85'); // ASCII code 133 + // outc ('\x85'); // ASCII code 133 prints("\xff\xfb\x01\xff\xfb\x03"); - iflush(); - igetch(0); - igetch_reset(); } + iflush(); } -static int _str_input(char *buffer, int buf_size, int max_display_len, int echo_mode) +static int _str_input(char *buffer, int buf_size, int max_display_len, enum io_echo_t echo_mode) { int ch; int offset = 0; int eol; int display_len; - char input_str[4]; + char input_str[5]; int str_len = 0; + wchar_t wcs[2]; char c; buffer[buf_size - 1] = '\0'; @@ -147,11 +148,10 @@ static int _str_input(char *buffer, int while (!SYS_server_exit) { - ch = igetch_t(MIN(MAX_DELAY_TIME, 60)); + ch = igetch_t(MIN(BBS_max_user_idle_time, 60)); if (ch == CR) { - igetch_reset(); break; } else if (ch == KEY_TIMEOUT || ch == KEY_NULL) // timeout or broken pipe @@ -169,7 +169,7 @@ static int _str_input(char *buffer, int offset--; if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8 { - while (offset > 0 && (buffer[offset] & 0b11000000) != 0b11000000) + while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0) { offset--; } @@ -190,14 +190,14 @@ static int _str_input(char *buffer, int else if ((ch & 0xff80) == 0x80) // head of multi-byte character { str_len = 0; - c = (char)(ch & 0b11110000); - while (c & 0b10000000) + c = (char)(ch & 0xf0); + while (c & 0x80) { input_str[str_len] = (char)(ch - 256); str_len++; - c = (c & 0b01111111) << 1; + c = (c & 0x7f) << 1; - if ((c & 0b10000000) == 0) // Input completed + if ((c & 0x80) == 0) // Input completed { break; } @@ -213,13 +213,18 @@ static int _str_input(char *buffer, int break; } } + input_str[str_len] = '\0'; if (str_len == 0) // Incomplete input { continue; } - if (offset + str_len > buf_size - 1 || display_len + 2 > max_display_len) // No enough space for Chinese character + if (mbstowcs(wcs, input_str, 1) == (size_t)-1) + { + log_error("mbstowcs() error\n"); + } + if (offset + str_len > buf_size - 1 || display_len + (UTF8_fixed_width ? 2 : wcwidth(wcs[0])) > max_display_len) // No enough space for Chinese character { outc('\a'); iflush(); @@ -275,7 +280,7 @@ static int _str_input(char *buffer, int return offset; } -int str_input(char *buffer, int buf_size, int echo_mode) +int str_input(char *buffer, int buf_size, enum io_echo_t echo_mode) { int len; @@ -287,7 +292,7 @@ int str_input(char *buffer, int buf_size iflush(); return len; -}; +} int get_data(int row, int col, char *prompt, char *buffer, int buf_size, int max_display_len) { @@ -297,8 +302,10 @@ int get_data(int row, int col, char *pro int offset = 0; int eol; int display_len; - char input_str[4]; + char input_str[5]; int str_len = 0; + wchar_t wcs[2]; + int wc_len; char c; buffer[buf_size - 1] = '\0'; @@ -318,11 +325,10 @@ int get_data(int row, int col, char *pro while (!SYS_server_exit) { - ch = igetch_t(MIN(MAX_DELAY_TIME, 60)); + ch = igetch_t(MIN(BBS_max_user_idle_time, 60)); if (ch == CR) { - igetch_reset(); break; } else if (ch == KEY_TIMEOUT || ch == KEY_NULL) // timeout or broken pipe @@ -341,13 +347,23 @@ int get_data(int row, int col, char *pro offset--; if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8 { - while (offset > 0 && (buffer[offset] & 0b11000000) != 0b11000000) + while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0) { str_len++; offset--; } - display_len--; - col_cur--; + + if (mbstowcs(wcs, buffer + offset, 1) == (size_t)-1) + { + log_error("mbstowcs() error\n"); + } + wc_len = (UTF8_fixed_width ? 2 : wcwidth(wcs[0])); + + if (wc_len == 2) + { + display_len--; + col_cur--; + } } memmove(buffer + offset, buffer + offset + str_len, (size_t)(len - offset - str_len)); @@ -371,11 +387,11 @@ int get_data(int row, int col, char *pro if ((buffer[offset] & 0x80) == 0x80) // head of multi-byte character { str_len = 0; - c = (char)(buffer[offset] & 0b11110000); - while (c & 0b10000000) + c = (char)(buffer[offset] & 0xf0); + while (c & 0x80) { str_len++; - c = (c & 0b01111111) << 1; + c = (c & 0x7f) << 1; } display_len--; } @@ -405,12 +421,22 @@ int get_data(int row, int col, char *pro offset--; if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8 { - while (offset > 0 && (buffer[offset] & 0b11000000) != 0b11000000) + while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0) { str_len++; offset--; } - col_cur--; + + if (mbstowcs(wcs, buffer + offset, 1) == (size_t)-1) + { + log_error("mbstowcs() error\n"); + } + wc_len = (UTF8_fixed_width ? 2 : wcwidth(wcs[0])); + + if (wc_len == 2) + { + col_cur--; + } } col_cur--; @@ -426,13 +452,23 @@ int get_data(int row, int col, char *pro str_len = 0; if ((buffer[offset] & 0x80) == 0x80) // head of multi-byte character { - c = (char)(buffer[offset] & 0b11110000); - while (c & 0b10000000) + c = (char)(buffer[offset] & 0xf0); + while (c & 0x80) { str_len++; - c = (c & 0b01111111) << 1; + c = (c & 0x7f) << 1; + } + + if (mbstowcs(wcs, buffer + offset, 1) == (size_t)-1) + { + log_error("mbstowcs() error\n"); + } + wc_len = (UTF8_fixed_width ? 2 : wcwidth(wcs[0])); + + if (wc_len == 2) + { + col_cur++; } - col_cur++; } else { @@ -478,14 +514,14 @@ int get_data(int row, int col, char *pro else if ((ch & 0xff80) == 0x80) // head of multi-byte character { str_len = 0; - c = (char)(ch & 0b11110000); - while (c & 0b10000000) + c = (char)(ch & 0xf0); + while (c & 0x80) { input_str[str_len] = (char)(ch - 256); str_len++; - c = (c & 0b01111111) << 1; + c = (c & 0x7f) << 1; - if ((c & 0b10000000) == 0) // Input completed + if ((c & 0x80) == 0) // Input completed { break; } @@ -501,13 +537,21 @@ int get_data(int row, int col, char *pro break; } } + input_str[str_len] = '\0'; if (str_len == 0) // Incomplete input { continue; } - if (len + str_len > buf_size - 1 || display_len + 2 > max_display_len) // No enough space for Chinese character + if (mbstowcs(wcs, input_str, 1) == (size_t)-1) + { + log_error("mbstowcs() error\n"); + } + wc_len = (UTF8_fixed_width ? 2 : wcwidth(wcs[0])); + + if (len + str_len > buf_size - 1 || + display_len + wc_len > max_display_len) // No enough space for Chinese character { outc('\a'); iflush(); @@ -518,13 +562,13 @@ int get_data(int row, int col, char *pro memcpy(buffer + offset, input_str, (size_t)str_len); len += str_len; buffer[len] = '\0'; - display_len += 2; + display_len += wc_len; moveto(row, col_cur); prints("%s", buffer + offset); prints("%*s", max_display_len - display_len, ""); - col_cur += 2; + col_cur += wc_len; moveto(row, col_cur); iflush(); @@ -595,7 +639,7 @@ int display_data(const void *p_data, lon loop = 1; while (!SYS_server_exit && loop) { - if (eof_exit > 0 && line_current >= display_line_total && display_line_total <= screen_row_total) + if (eof_exit > 0 && line_current >= display_line_total) { if (eof_exit == 1) { @@ -650,7 +694,7 @@ int display_data(const void *p_data, lon input_ok = 0; while (!SYS_server_exit && !input_ok) { - ch = igetch_t(MAX_DELAY_TIME); + ch = igetch_t(BBS_max_user_idle_time); input_ok = 1; if (ch != KEY_NULL && ch != KEY_TIMEOUT) @@ -704,8 +748,6 @@ int display_data(const void *p_data, lon output_end_row = SCREEN_ROWS - 1; // Legacy Fterm only works with this line break; case CR: - igetch_reset(); - case KEY_SPACE: case KEY_DOWN: if (line_current + (screen_row_total - (output_current_row - screen_begin_row)) >= display_line_total) // Reach end { @@ -733,6 +775,7 @@ int display_data(const void *p_data, lon output_end_row = SCREEN_ROWS - 1; clrline(output_current_row, SCREEN_ROWS); break; + case KEY_SPACE: case KEY_PGDN: if (line_current + screen_row_total - (output_current_row - screen_begin_row) >= display_line_total) // Reach end { @@ -806,7 +849,7 @@ cleanup: return ch; } -static int display_file_key_handler(int *p_key, DISPLAY_CTX *p_ctx) +int display_file_key_handler(int *p_key, DISPLAY_CTX *p_ctx) { switch (*p_key) { @@ -889,24 +932,26 @@ int show_top(const char *str_left, const int show_bottom(const char *msg) { char str_time[LINE_BUFFER_LEN]; + int len_str_time; time_t time_online; struct tm *tm_online; char msg_f[LINE_BUFFER_LEN]; int eol; - int msg_len; + int len_msg; int len; int len_username; char str_tm_online[LINE_BUFFER_LEN]; + int len_str_tm_online; - get_time_str(str_time, sizeof(str_time)); + len_str_time = (int)get_time_str(str_time, sizeof(str_time)); msg_f[0] = '\0'; - msg_len = 0; + len_msg = 0; if (msg != NULL) { strncpy(msg_f, msg, sizeof(msg_f) - 1); msg_f[sizeof(msg_f) - 1] = '\0'; - len = split_line(msg_f, 23, &eol, &msg_len, 1); + len = split_line(msg_f, 23, &eol, &len_msg, 1); msg_f[len] = '\0'; } @@ -917,20 +962,22 @@ int show_bottom(const char *msg) if (tm_online->tm_mday > 1) { snprintf(str_tm_online, sizeof(str_tm_online), - "\033[36m%2d\033[33m天\033[36m%2d\033[33m时", - tm_online->tm_mday - 1, tm_online->tm_hour); + "\033[36m%d\033[33md \033[36m%d\033[33m:\033[36m%.2d", + tm_online->tm_mday - 1, tm_online->tm_hour, tm_online->tm_min); } else { snprintf(str_tm_online, sizeof(str_tm_online), - "\033[36m%2d\033[33m时\033[36m%2d\033[33m分", + "\033[36m%d\033[33m:\033[36m%.2d", tm_online->tm_hour, tm_online->tm_min); } + len_str_tm_online = str_length(str_tm_online, 1); moveto(SCREEN_ROWS, 0); clrtoeol(); - prints("\033[1;44;33m时间[\033[36m%s\033[33m]%s%*s \033[33m帐号[\033[36m%s\033[33m][%s\033[33m]\033[m", - str_time, msg_f, 38 - msg_len - len_username, "", BBS_username, str_tm_online); + prints("\033[1;44;33m时间[\033[36m%s\033[33m]%s%*s \033[33m用户[\033[36m%s\033[33m][%s\033[33m]\033[m", + str_time, msg_f, 65 - len_str_time - len_msg - len_username - len_str_tm_online, + "", BBS_username, str_tm_online); return 0; }