==> src/term.c <==1/* termios raw mode and the xterm private modes, as crossterm sets and unsets them. */
2#include "tui/term.h"
3
4#include <errno.h>
5#include <poll.h>
6#include <signal.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/ioctl.h>
10#include <termios.h>
11#include <unistd.h>
12
13static struct termios saved;
14static bool raw;
15static volatile sig_atomic_t winch;
16
17static void on_winch(int sig) {
18 (void)sig;
19 winch = 1;
20}
21
22bool tui_term_write(const char *buf, size_t len) {
23 while (len) {
24 ssize_t n = write(STDOUT_FILENO, buf, len);
25 if (n < 0) {
26 if (errno == EINTR) continue;
27 return false;
28 }
29 buf += n;
30 len -= (size_t)n;
31 }
32 return true;
33}
34
35bool tui_term_enter(void) {
36 if (raw) return true;
37 if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) return false;
38 if (tcgetattr(STDIN_FILENO, &saved) != 0) return false;
39 struct termios t = saved;
40 /* cfmakeraw (crossterm's enable_raw_mode). */
41 t.c_iflag &= ~(tcflag_t)(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
42 t.c_oflag &= ~(tcflag_t)OPOST;
43 t.c_lflag &= ~(tcflag_t)(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
44 t.c_cflag &= ~(tcflag_t)(CSIZE | PARENB);
45 t.c_cflag |= CS8;
46 t.c_cc[VMIN] = 1;
47 t.c_cc[VTIME] = 0;
48 if (tcsetattr(STDIN_FILENO, TCSANOW, &t) != 0) return false;
49 raw = true;
50 struct sigaction sa;
51 memset(&sa, 0, sizeof sa);
52 sa.sa_handler = on_winch;
53 sigaction(SIGWINCH, &sa, NULL);
54 /* Alternate screen, hidden cursor, cleared. */
55 static const char on[] = "\x1b[?1049h\x1b[?25l\x1b[2J\x1b[H";
56 tui_term_write(on, sizeof on - 1);
57 return true;
58}
59
60void tui_term_leave(void) {
61 if (!raw) return;
62 static const char off[] = "\x1b[0m\x1b[?25h\x1b[?1049l";
63 tui_term_write(off, sizeof off - 1);
64 tcsetattr(STDIN_FILENO, TCSANOW, &saved);
65 raw = false;
66}
67
68bool tui_term_size(uint16_t *width, uint16_t *height) {
69 struct winsize ws;
70 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != 0 && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != 0) return false;
71 *width = ws.ws_col;
72 *height = ws.ws_row;
73 return true;
74}
75
76bool tui_term_resized(void) {
77 if (!winch) return false;
78 winch = 0;
79 return true;
80}
81
82long tui_term_read(char *buf, size_t cap, int timeout_ms) {
83 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
84 int r = poll(&p, 1, timeout_ms);
85 if (r < 0) return errno == EINTR ? 0 : -1;
86 if (r == 0) return 0;
87 ssize_t n = read(STDIN_FILENO, buf, cap);
88 if (n < 0) return errno == EINTR || errno == EAGAIN ? 0 : -1;
89 if (n == 0) return -1;
90 return n;
91}