1 #include <stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <stdarg.h>
6 #include <termios.h>
7 #include <sys/ioctl.h>
8 #include <sys/select.h>
9 #include <signal.h>
10 #include <errno.h>
11
12 #include "tty.h"
13
14 #include "../config.h"
15
tty_reset(tty_t * tty)16 void tty_reset(tty_t *tty) {
17 tcsetattr(tty->fdin, TCSANOW, &tty->original_termios);
18 }
19
tty_close(tty_t * tty)20 void tty_close(tty_t *tty) {
21 tty_reset(tty);
22 fclose(tty->fout);
23 close(tty->fdin);
24 }
25
handle_sigwinch(int sig)26 static void handle_sigwinch(int sig){
27 (void)sig;
28 }
29
tty_init(tty_t * tty,const char * tty_filename)30 void tty_init(tty_t *tty, const char *tty_filename) {
31 tty->fdin = open(tty_filename, O_RDONLY);
32 if (tty->fdin < 0) {
33 perror("Failed to open tty");
34 exit(EXIT_FAILURE);
35 }
36
37 tty->fout = fopen(tty_filename, "w");
38 if (!tty->fout) {
39 perror("Failed to open tty");
40 exit(EXIT_FAILURE);
41 }
42
43 if (setvbuf(tty->fout, NULL, _IOFBF, 4096)) {
44 perror("setvbuf");
45 exit(EXIT_FAILURE);
46 }
47
48 if (tcgetattr(tty->fdin, &tty->original_termios)) {
49 perror("tcgetattr");
50 exit(EXIT_FAILURE);
51 }
52
53 struct termios new_termios = tty->original_termios;
54
55 /*
56 * Disable all of
57 * ICANON Canonical input (erase and kill processing).
58 * ECHO Echo.
59 * ISIG Signals from control characters
60 * ICRNL Conversion of CR characters into NL
61 */
62 new_termios.c_iflag &= ~(ICRNL);
63 new_termios.c_lflag &= ~(ICANON | ECHO | ISIG);
64
65 if (tcsetattr(tty->fdin, TCSANOW, &new_termios))
66 perror("tcsetattr");
67
68 tty_getwinsz(tty);
69
70 tty_setnormal(tty);
71
72 signal(SIGWINCH, handle_sigwinch);
73 }
74
tty_getwinsz(tty_t * tty)75 void tty_getwinsz(tty_t *tty) {
76 struct winsize ws;
77 if (ioctl(fileno(tty->fout), TIOCGWINSZ, &ws) == -1) {
78 tty->maxwidth = 80;
79 tty->maxheight = 25;
80 } else {
81 tty->maxwidth = ws.ws_col;
82 tty->maxheight = ws.ws_row;
83 }
84 }
85
tty_getchar(tty_t * tty)86 char tty_getchar(tty_t *tty) {
87 char ch;
88 int size = read(tty->fdin, &ch, 1);
89 if (size < 0) {
90 perror("error reading from tty");
91 exit(EXIT_FAILURE);
92 } else if (size == 0) {
93 /* EOF */
94 exit(EXIT_FAILURE);
95 } else {
96 return ch;
97 }
98 }
99
tty_input_ready(tty_t * tty,long int timeout,int return_on_signal)100 int tty_input_ready(tty_t *tty, long int timeout, int return_on_signal) {
101 fd_set readfs;
102 FD_ZERO(&readfs);
103 FD_SET(tty->fdin, &readfs);
104
105 struct timespec ts = {timeout / 1000, (timeout % 1000) * 1000000};
106
107 sigset_t mask;
108 sigemptyset(&mask);
109 if (!return_on_signal)
110 sigaddset(&mask, SIGWINCH);
111
112 int err = pselect(
113 tty->fdin + 1,
114 &readfs,
115 NULL,
116 NULL,
117 timeout < 0 ? NULL : &ts,
118 return_on_signal ? NULL : &mask);
119
120 if (err < 0) {
121 if (errno == EINTR) {
122 return 0;
123 } else {
124 perror("select");
125 exit(EXIT_FAILURE);
126 }
127 } else {
128 return FD_ISSET(tty->fdin, &readfs);
129 }
130 }
131
tty_sgr(tty_t * tty,int code)132 static void tty_sgr(tty_t *tty, int code) {
133 tty_printf(tty, "%c%c%im", 0x1b, '[', code);
134 }
135
tty_setfg(tty_t * tty,int fg)136 void tty_setfg(tty_t *tty, int fg) {
137 if (tty->fgcolor != fg) {
138 tty_sgr(tty, 30 + fg);
139 tty->fgcolor = fg;
140 }
141 }
142
tty_setinvert(tty_t * tty)143 void tty_setinvert(tty_t *tty) {
144 tty_sgr(tty, 7);
145 }
146
tty_setunderline(tty_t * tty)147 void tty_setunderline(tty_t *tty) {
148 tty_sgr(tty, 4);
149 }
150
tty_setnormal(tty_t * tty)151 void tty_setnormal(tty_t *tty) {
152 tty_sgr(tty, 0);
153 tty->fgcolor = 9;
154 }
155
tty_setnowrap(tty_t * tty)156 void tty_setnowrap(tty_t *tty) {
157 tty_printf(tty, "%c%c?7l", 0x1b, '[');
158 }
159
tty_setwrap(tty_t * tty)160 void tty_setwrap(tty_t *tty) {
161 tty_printf(tty, "%c%c?7h", 0x1b, '[');
162 }
163
tty_newline(tty_t * tty)164 void tty_newline(tty_t *tty) {
165 tty_printf(tty, "%c%cK\n", 0x1b, '[');
166 }
167
tty_clearline(tty_t * tty)168 void tty_clearline(tty_t *tty) {
169 tty_printf(tty, "%c%cK", 0x1b, '[');
170 }
171
tty_setcol(tty_t * tty,int col)172 void tty_setcol(tty_t *tty, int col) {
173 tty_printf(tty, "%c%c%iG", 0x1b, '[', col + 1);
174 }
175
tty_moveup(tty_t * tty,int i)176 void tty_moveup(tty_t *tty, int i) {
177 tty_printf(tty, "%c%c%iA", 0x1b, '[', i);
178 }
179
tty_printf(tty_t * tty,const char * fmt,...)180 void tty_printf(tty_t *tty, const char *fmt, ...) {
181 va_list args;
182 va_start(args, fmt);
183 vfprintf(tty->fout, fmt, args);
184 va_end(args);
185 }
186
tty_flush(tty_t * tty)187 void tty_flush(tty_t *tty) {
188 fflush(tty->fout);
189 }
190
tty_getwidth(tty_t * tty)191 size_t tty_getwidth(tty_t *tty) {
192 return tty->maxwidth;
193 }
194
tty_getheight(tty_t * tty)195 size_t tty_getheight(tty_t *tty) {
196 return tty->maxheight;
197 }
198