so://phie

so-term

An easy-to-use terminal library
git clone git://soophie.de/so-term

blob: 51f7ca28b9b962cdc46e2e2a553b336710350baa (5080 bytes)[doc][blame][raw]

1#ifndef LIB_TERM_H

2#define LIB_TERM_H

3

4typedef 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;

18

19typedef struct {

20 char *buffer;

21 int len;

22} term_t;

23

24void term_panic(const char *s);

25void term_disable_raw_mode(void);

26void term_enable_raw_mode(void);

27int term_read_cursor_pos(int *rows, int *cols);

28int term_read_window_size(int *rows, int *cols);

29int term_poll_key(int timeout);

30int term_read_key(void);

31void term_write(char *str);

32void term_writef(const char *format, ...);

33void term_flush(void);

34

35#ifdef LIB_TERM_IMPL

36

37#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>

46

47struct termios termios;

48term_t term = { NULL, 0 };

49

50void 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}

57

58void term_disable_raw_mode(void) {

59 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) == -1) {

60 term_panic("tcsetattr");

61 }

62}

63

64void 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}

80

81int 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}

105

106int 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}

120

121int 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}

130

131int 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}

200

201void 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}

213

214void 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}

222

223void 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}

231

232#endif // LIB_TERM_IMPL

233#endif // LIB_TERM_H