skip to content
==> 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}