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