3 * Copyright 2019 Bruce Hill
4 * Released under the MIT License
6 * Definitions of some basic terminal stuff, like reading keys
7 * and some terminal escape sequences.
16 #define KEY_F1 (0xFFFF-0)
17 #define KEY_F2 (0xFFFF-1)
18 #define KEY_F3 (0xFFFF-2)
19 #define KEY_F4 (0xFFFF-3)
20 #define KEY_F5 (0xFFFF-4)
21 #define KEY_F6 (0xFFFF-5)
22 #define KEY_F7 (0xFFFF-6)
23 #define KEY_F8 (0xFFFF-7)
24 #define KEY_F9 (0xFFFF-8)
25 #define KEY_F10 (0xFFFF-9)
26 #define KEY_F11 (0xFFFF-10)
27 #define KEY_F12 (0xFFFF-11)
28 #define KEY_INSERT (0xFFFF-12)
29 #define KEY_DELETE (0xFFFF-13)
30 #define KEY_HOME (0xFFFF-14)
31 #define KEY_END (0xFFFF-15)
32 #define KEY_PGUP (0xFFFF-16)
33 #define KEY_PGDN (0xFFFF-17)
34 #define KEY_ARROW_UP (0xFFFF-18)
35 #define KEY_ARROW_DOWN (0xFFFF-19)
36 #define KEY_ARROW_LEFT (0xFFFF-20)
37 #define KEY_ARROW_RIGHT (0xFFFF-21)
38 #define KEY_MOUSE_LEFT (0xFFFF-22)
39 #define KEY_MOUSE_RIGHT (0xFFFF-23)
40 #define KEY_MOUSE_MIDDLE (0xFFFF-24)
41 #define KEY_MOUSE_RELEASE (0xFFFF-25)
42 #define KEY_MOUSE_WHEEL_UP (0xFFFF-26)
43 #define KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)
44 #define KEY_MOUSE_DOUBLE_LEFT (0xFFFF-28)
46 /* These are all ASCII code points below SPACE character and a BACKSPACE key. */
47 #define KEY_CTRL_TILDE 0x00
48 #define KEY_CTRL_2 0x00 /* clash with 'CTRL_TILDE' */
49 #define KEY_CTRL_A 0x01
50 #define KEY_CTRL_B 0x02
51 #define KEY_CTRL_C 0x03
52 #define KEY_CTRL_D 0x04
53 #define KEY_CTRL_E 0x05
54 #define KEY_CTRL_F 0x06
55 #define KEY_CTRL_G 0x07
56 #define KEY_BACKSPACE 0x08
57 #define KEY_CTRL_H 0x08 /* clash with 'CTRL_BACKSPACE' */
59 #define KEY_CTRL_I 0x09 /* clash with 'TAB' */
60 #define KEY_CTRL_J 0x0A
61 #define KEY_CTRL_K 0x0B
62 #define KEY_CTRL_L 0x0C
63 #define KEY_ENTER 0x0D
64 #define KEY_CTRL_M 0x0D /* clash with 'ENTER' */
65 #define KEY_CTRL_N 0x0E
66 #define KEY_CTRL_O 0x0F
67 #define KEY_CTRL_P 0x10
68 #define KEY_CTRL_Q 0x11
69 #define KEY_CTRL_R 0x12
70 #define KEY_CTRL_S 0x13
71 #define KEY_CTRL_T 0x14
72 #define KEY_CTRL_U 0x15
73 #define KEY_CTRL_V 0x16
74 #define KEY_CTRL_W 0x17
75 #define KEY_CTRL_X 0x18
76 #define KEY_CTRL_Y 0x19
77 #define KEY_CTRL_Z 0x1A
79 #define KEY_CTRL_LSQ_BRACKET 0x1B /* clash with 'ESC' */
80 #define KEY_CTRL_3 0x1B /* clash with 'ESC' */
81 #define KEY_CTRL_4 0x1C
82 #define KEY_CTRL_BACKSLASH 0x1C /* clash with 'CTRL_4' */
83 #define KEY_CTRL_5 0x1D
84 #define KEY_CTRL_RSQ_BRACKET 0x1D /* clash with 'CTRL_5' */
85 #define KEY_CTRL_6 0x1E
86 #define KEY_CTRL_7 0x1F
87 #define KEY_CTRL_SLASH 0x1F /* clash with 'CTRL_7' */
88 #define KEY_CTRL_UNDERSCORE 0x1F /* clash with 'CTRL_7' */
89 #define KEY_SPACE 0x20
90 #define KEY_BACKSPACE2 0x7F
91 #define KEY_CTRL_8 0x7F /* clash with 'BACKSPACE2' */
94 // Terminal escape sequences:
97 #define T_SHOW_CURSOR "25"
98 #define T_MOUSE_XY "1000"
99 #define T_MOUSE_CELL "1002"
100 #define T_MOUSE_SGR "1006"
101 #define T_ALT_SCREEN "1049"
102 #define T_ON(opt) CSI "?" opt "h"
103 #define T_OFF(opt) CSI "?" opt "l"
105 #define move_cursor(f, x, y) fprintf((f), CSI "%d;%dH", (int)(y)+1, (int)(x)+1)
109 int bgetkey(FILE *in, int *mouse_x, int *mouse_y, int timeout);
110 const char *bkeyname(int key);
112 static inline int nextchar(int fd, int timeout)
115 struct pollfd pfd = {fd, POLLIN, 0};
116 if (poll(&pfd, 1, timeout) > 0)
117 return read(fd, &c, 1) == 1 ? c : -1;
122 * Get one key of input from the given file. Returns -1 on failure.
123 * If mouse_x or mouse_y are non-null and a mouse event occurs, they will be
124 * set to the position of the mouse (0-indexed).
126 int bgetkey(FILE *in, int *mouse_x, int *mouse_y, int timeout)
129 int numcode = 0, super = 0;
130 int c = nextchar(fd, timeout);
137 c = nextchar(fd, ESC_DELAY);
138 // Actual escape key:
143 case '\x1b': ++super; goto escape;
144 case '[': goto CSI_start;
151 c = nextchar(fd, ESC_DELAY);
156 case 'A': return KEY_ARROW_UP;
157 case 'B': return KEY_ARROW_DOWN;
158 case 'C': return KEY_ARROW_RIGHT;
159 case 'D': return KEY_ARROW_LEFT;
160 case 'F': return KEY_END;
161 case 'H': return KEY_HOME;
164 case 3: return KEY_DELETE;
165 case 4: return KEY_END;
166 case 5: return KEY_PGUP;
167 case 6: return KEY_PGDN;
168 case 15: return KEY_F5;
169 case 17: return KEY_F6;
170 case 18: return KEY_F7;
171 case 19: return KEY_F8;
172 case 20: return KEY_F9;
173 case 21: return KEY_F10;
174 case 23: return KEY_F11;
175 case 24: return KEY_F12;
178 case '<': { // Mouse clicks
179 int buttons = 0, x = 0, y = 0;
181 while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
182 buttons = buttons * 10 + (buf - '0');
183 if (buf != ';') return -1;
184 while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
185 x = x * 10 + (buf - '0');
186 if (buf != ';') return -1;
187 while (read(fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
188 y = y * 10 + (buf - '0');
189 if (buf != 'm' && buf != 'M') return -1;
191 if (mouse_x) *mouse_x = x - 1;
192 if (mouse_y) *mouse_y = y - 1;
195 return KEY_MOUSE_RELEASE;
197 case 64: return KEY_MOUSE_WHEEL_UP;
198 case 65: return KEY_MOUSE_WHEEL_DOWN;
199 case 0: return KEY_MOUSE_LEFT;
200 case 1: return KEY_MOUSE_RIGHT;
201 case 2: return KEY_MOUSE_MIDDLE;
207 if ('0' <= c && c <= '9') {
209 numcode = 10*numcode + (c - '0');
219 switch (nextchar(fd, ESC_DELAY)) {
220 case 'P': return KEY_F1;
221 case 'Q': return KEY_F2;
222 case 'R': return KEY_F3;
223 case 'S': return KEY_F4;
230 * Return the name of a key, if one exists and is different from the key itself
231 * (i.e. bkeyname('c') == NULL, bkeyname(' ') == "Space")
233 const char *bkeyname(int key)
235 // TODO: currently only the keys I'm using are named
237 case KEY_F1: return "F1";
238 case KEY_F2: return "F2";
239 case KEY_F3: return "F3";
240 case KEY_F4: return "F4";
241 case KEY_F5: return "F5";
242 case KEY_F6: return "F6";
243 case KEY_F7: return "F7";
244 case KEY_F8: return "F8";
245 case KEY_F9: return "F9";
246 case KEY_F10: return "F10";
247 case KEY_F11: return "F11";
248 case KEY_F12: return "F12";
249 case KEY_INSERT: return "Insert";
250 case KEY_DELETE: return "Delete";
251 case KEY_HOME: return "Home";
252 case KEY_END: return "End";
253 case KEY_PGUP: return "PgUp";
254 case KEY_PGDN: return "PgDn";
255 case KEY_ARROW_UP: return "Up";
256 case KEY_ARROW_DOWN: return "Down";
257 case KEY_ARROW_LEFT: return "Left";
258 case KEY_ARROW_RIGHT: return "Right";
259 case KEY_MOUSE_LEFT: return "Left click";
260 case KEY_MOUSE_RIGHT: return "Right click";
261 case KEY_MOUSE_MIDDLE: return "Middle click";
262 case KEY_MOUSE_RELEASE: return "Mouse release";
263 case KEY_MOUSE_WHEEL_UP: return "Mouse wheel up";
264 case KEY_MOUSE_WHEEL_DOWN: return "Mouse wheel down";
265 case KEY_MOUSE_DOUBLE_LEFT: return "Double left click";
266 case KEY_CTRL_TILDE: return "Ctrl-[2~]";
267 case KEY_CTRL_A: return "Ctrl-a";
268 case KEY_CTRL_B: return "Ctrl-b";
269 case KEY_CTRL_C: return "Ctrl-c";
270 case KEY_CTRL_D: return "Ctrl-d";
271 case KEY_CTRL_E: return "Ctrl-e";
272 case KEY_CTRL_F: return "Ctrl-f";
273 case KEY_CTRL_G: return "Ctrl-g";
274 case KEY_CTRL_H: return "Ctrl-h";
275 case KEY_TAB: return "Tab";
276 case KEY_CTRL_J: return "Ctrl-j";
277 case KEY_CTRL_K: return "Ctrl-k";
278 case KEY_CTRL_L: return "Ctrl-l";
279 case KEY_ENTER: return "Enter";
280 case KEY_CTRL_N: return "Ctrl-n";
281 case KEY_CTRL_O: return "Ctrl-o";
282 case KEY_CTRL_P: return "Ctrl-p";
283 case KEY_CTRL_Q: return "Ctrl-q";
284 case KEY_CTRL_R: return "Ctrl-r";
285 case KEY_CTRL_S: return "Ctrl-s";
286 case KEY_CTRL_T: return "Ctrl-t";
287 case KEY_CTRL_U: return "Ctrl-u";
288 case KEY_CTRL_V: return "Ctrl-v";
289 case KEY_CTRL_W: return "Ctrl-w";
290 case KEY_CTRL_X: return "Ctrl-x";
291 case KEY_CTRL_Y: return "Ctrl-y";
292 case KEY_CTRL_Z: return "Ctrl-z";
293 case KEY_ESC: return "Esc";
294 case KEY_CTRL_BACKSLASH: return "Ctrl-[4\\]";
295 case KEY_CTRL_RSQ_BRACKET: return "Ctrl-[5]]";
296 case KEY_CTRL_6: return "Ctrl-6";
297 case KEY_CTRL_SLASH: return "Ctrl-[7/_]";
298 case KEY_SPACE: return "Space";
299 case KEY_BACKSPACE2: return "Backspace";
305 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1