so://phie

so-term

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

blob: ff2a0a441674bada5d6eccb8a259e762579777fe (4330 bytes)[doc][blame][raw]

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>

10

11#include "libterm.h"

12

13struct termios termios;

14term_t term = { NULL, 0 };

15

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

23

24void term_disable_raw_mode() {

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

26 term_panic("tcsetattr");

27 }

28}

29

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

46

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

71

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

86

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

96

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

166

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

179

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

188

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