summaryrefslogtreecommitdiff
path: root/lab04/tui.c
diff options
context:
space:
mode:
Diffstat (limited to 'lab04/tui.c')
-rw-r--r--lab04/tui.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/lab04/tui.c b/lab04/tui.c
new file mode 100644
index 0000000..d5abdd2
--- /dev/null
+++ b/lab04/tui.c
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#include "tui.h"
+
+#include <poll.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+
+struct tui_state {
+ struct termios saved_termios;
+ bool termios_saved;
+ bool raw_mode;
+ bool alternate_screen;
+};
+
+static struct tui_state tui_state;
+
+static int tui_read_byte(void)
+{
+ unsigned char byte;
+ ssize_t count = read(STDIN_FILENO, &byte, sizeof(byte));
+
+ return count == (ssize_t)sizeof(byte) ? byte : -1;
+}
+
+bool tui_init(void)
+{
+ if (tcgetattr(STDIN_FILENO, &tui_state.saved_termios) < 0) {
+ return false;
+ }
+ tui_state.termios_saved = true;
+
+ struct termios raw = tui_state.saved_termios;
+ cfmakeraw(&raw);
+
+ raw.c_oflag |= OPOST | ONLCR;
+
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) {
+ tui_state.termios_saved = false;
+ return false;
+ }
+ tui_state.raw_mode = true;
+
+ /* DEC private mode 1049: 切换到备用屏幕缓冲区 */
+ fputs("\033[?1049h\033[H\033[?25l", stdout);
+ fflush(stdout);
+ tui_state.alternate_screen = true;
+ return true;
+}
+
+void tui_shutdown(void)
+{
+ if (tui_state.alternate_screen) {
+ tui_show_cursor();
+ fputs("\033[?1049l", stdout);
+ fflush(stdout);
+ tui_state.alternate_screen = false;
+ }
+
+ if (tui_state.raw_mode && tui_state.termios_saved) {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tui_state.saved_termios);
+ tui_state.raw_mode = false;
+ }
+}
+
+void tui_clear(void)
+{
+ fputs("\033[2J\033[H", stdout);
+}
+
+void tui_hide_cursor(void)
+{
+ fputs("\033[?25l", stdout);
+}
+
+void tui_show_cursor(void)
+{
+ fputs("\033[?25h", stdout);
+}
+
+void tui_printf(const char *format, ...)
+{
+ va_list arguments;
+
+ va_start(arguments, format);
+ vprintf(format, arguments);
+ va_end(arguments);
+}
+
+struct tui_event tui_read_key(void)
+{
+ int character = tui_read_byte();
+ if (character < 0) {
+ return (struct tui_event){ .kind = TUI_KEY_ESCAPE };
+ }
+
+ if (character == '\033') {
+ struct pollfd input = {
+ .fd = STDIN_FILENO,
+ .events = POLLIN,
+ };
+ if (poll(&input, 1, 25) <= 0) {
+ return (struct tui_event){ .kind = TUI_KEY_ESCAPE };
+ }
+
+ int sequence = tui_read_byte();
+ if (sequence == '[') {
+ int code = tui_read_byte();
+ switch (code) {
+ case 'A':
+ return (struct tui_event){ .kind = TUI_KEY_UP };
+ case 'B':
+ return (struct tui_event){ .kind = TUI_KEY_DOWN };
+ case 'C':
+ return (struct tui_event){ .kind = TUI_KEY_RIGHT };
+ case 'D':
+ return (struct tui_event){ .kind = TUI_KEY_LEFT };
+ default:
+ break;
+ }
+ }
+ return (struct tui_event){ .kind = TUI_KEY_ESCAPE };
+ }
+
+ if (character == '\r' || character == '\n') {
+ return (struct tui_event){ .kind = TUI_KEY_ENTER };
+ }
+ if (character == '\b' || character == 127) {
+ return (struct tui_event){ .kind = TUI_KEY_BACKSPACE };
+ }
+ return (struct tui_event){
+ .kind = TUI_KEY_CHARACTER,
+ .character = character,
+ };
+}
+
+bool tui_read_line(const char *prompt, char *buffer, size_t capacity)
+{
+ size_t length = 0;
+
+ if (capacity == 0) {
+ return false;
+ }
+ tui_show_cursor();
+ buffer[0] = '\0';
+ if (prompt && *prompt) {
+ tui_printf("%s", prompt);
+ }
+ fflush(stdout);
+
+ for (;;) {
+ struct tui_event event = tui_read_key();
+ if (event.kind == TUI_KEY_ENTER) {
+ fputc('\n', stdout);
+ tui_hide_cursor();
+ return true;
+ }
+ if (event.kind == TUI_KEY_ESCAPE) {
+ fputc('\n', stdout);
+ tui_hide_cursor();
+ return false;
+ }
+ if (event.kind == TUI_KEY_BACKSPACE) {
+ if (length > 0) {
+ buffer[--length] = '\0';
+ fputs("\b \b", stdout);
+ fflush(stdout);
+ }
+ continue;
+ }
+ if (event.kind == TUI_KEY_CHARACTER &&
+ event.character >= 32 && event.character != 127 &&
+ length + 1 < capacity) {
+ buffer[length++] = (char)event.character;
+ buffer[length] = '\0';
+ fputc(event.character, stdout);
+ fflush(stdout);
+ }
+ }
+}
+
+void tui_wait(void)
+{
+ fputs("\n\n按任意键返回...", stdout);
+ fflush(stdout);
+ (void)tui_read_key();
+}