An easy-to-use terminal library
git clone git://soophie.de/so-term
blob: 51f7ca28b9b962cdc46e2e2a553b336710350baa (5080 bytes)[doc][blame][raw]
1
#ifndef LIB_TERM_H2
#define LIB_TERM_H4
typedef enum {5
KEY_ENTER = 13,6
KEY_ESC = 27,7
KEY_BACKSPACE = 127,8
KEY_ARROW_LEFT = 1000,9
KEY_ARROW_RIGHT,10
KEY_ARROW_UP,11
KEY_ARROW_DOWN,12
KEY_DEL,13
KEY_HOME,14
KEY_END,15
KEY_PAGE_UP,16
KEY_PAGE_DOWN,17
} keycode_t;19
typedef struct {20
char *buffer;21
int len;22
} term_t;24
void term_panic(const char *s);25
void term_disable_raw_mode(void);26
void term_enable_raw_mode(void);27
int term_read_cursor_pos(int *rows, int *cols);28
int term_read_window_size(int *rows, int *cols);29
int term_poll_key(int timeout);30
int term_read_key(void);31
void term_write(char *str);32
void term_writef(const char *format, ...);33
void term_flush(void);35
#ifdef LIB_TERM_IMPL37
#include <stdio.h>38
#include <stdlib.h>39
#include <string.h>40
#include <unistd.h>41
#include <termios.h>42
#include <sys/ioctl.h>43
#include <errno.h>44
#include <stdarg.h>45
#include <poll.h>47
struct termios termios;48
term_t term = { NULL, 0 };50
void term_panic(const char *str) {51
write(STDOUT_FILENO, "\x1b[2J", 4);52
write(STDOUT_FILENO, "\x1b[H", 3);53
write(STDOUT_FILENO, "\x1b[?25h", 6);54
perror(str);55
exit(EXIT_FAILURE);56
}58
void term_disable_raw_mode(void) {59
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) == -1) {60
term_panic("tcsetattr");61
}62
}64
void term_enable_raw_mode(void) {65
if (tcgetattr(STDIN_FILENO, &termios) == -1) {66
term_panic("tcgetattr");67
}68
atexit(term_disable_raw_mode);69
struct termios raw = termios;70
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);71
raw.c_oflag &= ~(OPOST);72
raw.c_cflag |= (CS8);73
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);74
raw.c_cc[VMIN] = 0;75
raw.c_cc[VTIME] = 1;76
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {77
term_panic("tcsetattr");78
}79
}81
int term_read_cursor_pos(int *rows, int *cols) {82
char buf[32];83
unsigned int i = 0;84
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) {85
return -1;86
}87
while (i < sizeof(buf) - 1) {88
if (read(STDIN_FILENO, &buf[i], 1) != 1) {89
break;90
}91
if (buf[i] == 'R') {92
break;93
}94
i++;95
}96
buf[i] = '\0';97
if (buf[0] != '\x1b' || buf[1] != '[') {98
return -1;99
}100
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) {101
return -1;102
}103
return -0;104
}106
int term_read_window_size(int *rows, int *cols) {107
struct winsize ws;108
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {109
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) {110
return -1;111
}112
return term_read_cursor_pos(rows, cols);113
}114
else {115
*cols = ws.ws_col;116
*rows = ws.ws_row;117
return 0;118
}119
}121
int term_poll_key(int timeout) {122
struct pollfd fds[1];123
fds[0].fd = STDIN_FILENO;124
fds[0].events = POLLIN | POLLPRI;125
if (poll(fds, 1, timeout)) {126
return term_read_key();127
}128
return 0;129
}131
int term_read_key(void) {132
int len;133
char c;134
while ((len = read(STDIN_FILENO, &c, 1)) != 1) {135
if (len == -1 && errno != EAGAIN) {136
term_panic("read");137
}138
}139
if (c == '\x1b') {140
char seq[3];141
if (read(STDIN_FILENO, &seq[0], 1) != 1) {142
return '\x1b';143
}144
if (read(STDIN_FILENO, &seq[1], 1) != 1) {145
return '\x1b';146
}147
if (seq[0] == '[') {148
if (seq[1] >= '0' && seq[1] <= '9') {149
if (read(STDIN_FILENO, &seq[2], 1) != 1) {150
return '\x1b';151
}152
if (seq[2] == '~') {153
switch (seq[1]) {154
case '1':155
return KEY_HOME;156
case '3':157
return KEY_DEL;158
case '4':159
return KEY_END;160
case '5':161
return KEY_PAGE_UP;162
case '6':163
return KEY_PAGE_DOWN;164
case '7':165
return KEY_HOME;166
case '8':167
return KEY_END;168
}169
}170
}171
else {172
switch (seq[1]) {173
case 'A':174
return KEY_ARROW_UP;175
case 'B':176
return KEY_ARROW_DOWN;177
case 'C':178
return KEY_ARROW_RIGHT;179
case 'D':180
return KEY_ARROW_LEFT;181
case 'H':182
return KEY_HOME;183
case 'F':184
return KEY_END;185
}186
return '\x1b';187
}188
}189
else if (seq[0] == 'O') {190
switch (seq[1]) {191
case 'H':192
return KEY_HOME;193
case 'F':194
return KEY_END;195
}196
}197
}198
return c;199
}201
void term_write(char *str) {202
int str_len = strlen(str);203
char *new_buffer = realloc(term.buffer, sizeof(char) * (term.len + str_len + 1));204
if (new_buffer == NULL) {205
fprintf(stderr, "Cannot realloc memory!\n");206
return;207
}208
term.buffer = new_buffer;209
memcpy(term.buffer + term.len, str, str_len);210
term.len += str_len;211
term.buffer[term.len] = '\0';212
}214
void term_writef(const char *format, ...) {215
va_list args;216
va_start(args, format);217
char str[1000];218
vsnprintf(str, sizeof(str), format, args);219
term_write(str);220
va_end(args);221
}223
void term_flush(void) {224
if (term.len > 0) {225
write(STDOUT_FILENO, term.buffer, term.len);226
free(term.buffer);227
term.buffer = NULL;228
term.len = 0;229
}230
}232
#endif // LIB_TERM_IMPL233
#endif // LIB_TERM_H