An easy-to-use terminal library
git clone git://soophie.de/so-term
1
# libterm3
An easy-to-use terminal library
1
#include <stdio.h>2
#include <stdlib.h>3
#include <string.h>4
#include <unistd.h>5
#include <termios.h>6
#include <sys/ioctl.h>7
#include <errno.h>8
#include <stdarg.h>9
#include <poll.h>11
#include "term.h"13
struct termios termios;14
term_t term = { NULL, 0 };16
void term_panic(const char *str) {17
write(STDOUT_FILENO, "\x1b[2J", 4);18
write(STDOUT_FILENO, "\x1b[H", 3);19
write(STDOUT_FILENO, "\x1b[?25h", 6);20
perror(str);21
exit(EXIT_FAILURE);22
}24
void term_disable_raw_mode() {25
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) == -1) {26
term_panic("tcsetattr");27
}28
}30
void term_enable_raw_mode() {31
if (tcgetattr(STDIN_FILENO, &termios) == -1) {32
term_panic("tcgetattr");33
}34
atexit(term_disable_raw_mode);35
struct termios raw = termios;36
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);37
raw.c_oflag &= ~(OPOST);38
raw.c_cflag |= (CS8);39
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);40
raw.c_cc[VMIN] = 0;41
raw.c_cc[VTIME] = 1;42
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {43
term_panic("tcsetattr");44
}45
}47
int term_read_cursor_pos(int *rows, int *cols) {48
char buf[32];49
unsigned int i = 0;50
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) {51
return -1;52
}53
while (i < sizeof(buf) - 1) {54
if (read(STDIN_FILENO, &buf[i], 1) != 1) {55
break;56
}57
if (buf[i] == 'R') {58
break;59
}60
i++;61
}62
buf[i] = '\0';63
if (buf[0] != '\x1b' || buf[1] != '[') {64
return -1;65
}66
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) {67
return -1;68
}69
return -0;70
}72
int term_read_window_size(int *rows, int *cols) {73
struct winsize ws;74
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {75
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) {76
return -1;77
}78
return term_read_cursor_pos(rows, cols);79
}80
else {81
*cols = ws.ws_col;82
*rows = ws.ws_row;83
return 0;84
}85
}87
int term_poll_key(int timeout) {88
struct pollfd fds[1];89
fds[0].fd = STDIN_FILENO;90
fds[0].events = POLLIN | POLLPRI;91
if (poll(fds, 1, timeout)) {92
return term_read_key();93
}94
return 0;95
}97
int term_read_key(void) {98
int len;99
char c;100
while ((len = read(STDIN_FILENO, &c, 1)) != 1) {101
if (len == -1 && errno != EAGAIN) {102
term_panic("read");103
}104
}105
if (c == '\x1b') {106
char seq[3];107
if (read(STDIN_FILENO, &seq[0], 1) != 1) {108
return '\x1b';109
}110
if (read(STDIN_FILENO, &seq[1], 1) != 1) {111
return '\x1b';112
}113
if (seq[0] == '[') {114
if (seq[1] >= '0' && seq[1] <= '9') {115
if (read(STDIN_FILENO, &seq[2], 1) != 1) {116
return '\x1b';117
}118
if (seq[2] == '~') {119
switch (seq[1]) {120
case '1':121
return HOME_KEY;122
case '3':123
return DEL_KEY;124
case '4':125
return END_KEY;126
case '5':127
return PAGE_UP;128
case '6':129
return PAGE_DOWN;130
case '7':131
return HOME_KEY;132
case '8':133
return END_KEY;134
}135
}136
}137
else {138
switch (seq[1]) {139
case 'A':140
return ARROW_UP;141
case 'B':142
return ARROW_DOWN;143
case 'C':144
return ARROW_RIGHT;145
case 'D':146
return ARROW_LEFT;147
case 'H':148
return HOME_KEY;149
case 'F':150
return END_KEY;151
}152
return '\x1b';153
}154
}155
else if (seq[0] == 'O') {156
switch (seq[1]) {157
case 'H':158
return HOME_KEY;159
case 'F':160
return END_KEY;161
}162
}163
}164
return c;165
}167
void term_write(char *str) {168
int str_len = strlen(str);169
char *new_buffer = realloc(term.buffer, sizeof(char) * (term.len + str_len + 1));170
if (new_buffer == NULL) {171
fprintf(stderr, "Cannot realloc memory!\n");172
return;173
}174
term.buffer = new_buffer;175
memcpy(term.buffer + term.len, str, str_len);176
term.len += str_len;177
term.buffer[term.len] = '\0';178
}180
void term_writef(const char *format, ...) {181
va_list args;182
va_start(args, format);183
char str[1000];184
vsnprintf(str, sizeof(str), format, args);185
term_write(str);186
va_end(args);187
}189
void term_flush(void) {190
if (term.len > 0) {191
write(STDOUT_FILENO, term.buffer, term.len);192
free(term.buffer);193
term.buffer = NULL;194
term.len = 0;195
}196
}
1
#pragma once3
typedef enum {4
ESC = 27,5
BACKSPACE = 127,6
ARROW_LEFT = 1000,7
ARROW_RIGHT,8
ARROW_UP,9
ARROW_DOWN,10
DEL_KEY,11
HOME_KEY,12
END_KEY,13
PAGE_UP,14
PAGE_DOWN,15
} keycode_t;17
typedef struct {18
char *buffer;19
int len;20
} term_t;22
void term_panic(const char *s);23
void term_disable_raw_mode(void);24
void term_enable_raw_mode(void);25
int term_read_cursor_pos(int *rows, int *cols);26
int term_read_window_size(int *rows, int *cols);27
int term_poll_key(int timeout);28
int term_read_key(void);29
void term_write(char *str);30
void term_writef(const char *format, ...);31
void term_flush(void);