diff options
-rw-r--r-- | dumbsh.c | 16 | ||||
-rw-r--r-- | handlekeys.c | 114 |
2 files changed, 83 insertions, 47 deletions
@@ -2,7 +2,7 @@ | |||
2 | * | 2 | * |
3 | * Copyright 2014 David Seikel <won_fang@yahoo.com.au> | 3 | * Copyright 2014 David Seikel <won_fang@yahoo.com.au> |
4 | * | 4 | * |
5 | * Not a real shell, so doesn't follow any standards, | 5 | * Not a real shell, so doesn't follow any standards, |
6 | * coz it wont implement them anyway. | 6 | * coz it wont implement them anyway. |
7 | 7 | ||
8 | USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN)) | 8 | USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN)) |
@@ -48,7 +48,8 @@ static void updateLine() | |||
48 | fflush(stdout); | 48 | fflush(stdout); |
49 | TT.y = TT.h; | 49 | TT.y = TT.h; |
50 | } | 50 | } |
51 | printf("\x1B[%d;0H%-*s\x1B[%d;%dH", TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1); | 51 | printf("\x1B[%d;0H%-*s\x1B[%d;%dH", |
52 | TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1); | ||
52 | fflush(stdout); | 53 | fflush(stdout); |
53 | } | 54 | } |
54 | 55 | ||
@@ -58,11 +59,14 @@ static void handleCSI(long extra, char *command, int *params, int count) | |||
58 | // Is it a cursor location report? | 59 | // Is it a cursor location report? |
59 | if (strcmp("R", command) == 0) | 60 | if (strcmp("R", command) == 0) |
60 | { | 61 | { |
61 | // Parameters are cursor line and column. Note this may be sent at other times, not just during terminal resize. | 62 | // Parameters are cursor line and column. |
63 | // NOTE - This may be sent at other times, not just during terminal resize. | ||
64 | // We are assuming here that it's a resize. | ||
62 | // The defaults are 1, which get ignored by the heuristic below. | 65 | // The defaults are 1, which get ignored by the heuristic below. |
63 | int r = params[0], c = params[1]; | 66 | int r = params[0], c = params[1]; |
64 | 67 | ||
65 | // Check it's not an F3 key variation, coz some of them use the same CSI function code. | 68 | // Check it's not an F3 key variation, coz some of them use |
69 | // the same CSI function command. | ||
66 | // This is a heuristic, we are checking against an unusable terminal size. | 70 | // This is a heuristic, we are checking against an unusable terminal size. |
67 | if ((2 == count) && (8 < r) && (8 < c)) | 71 | if ((2 == count) && (8 < r) && (8 < c)) |
68 | { | 72 | { |
@@ -96,8 +100,8 @@ static void backSpaceChar() | |||
96 | } | 100 | } |
97 | } | 101 | } |
98 | 102 | ||
99 | // This is where we would actually deal with what ever command the user had typed in. | 103 | // This is where we would actually deal with |
100 | // For now we just move on. | 104 | // what ever command the user had typed in. |
101 | // For now we just move on to the next line. | 105 | // For now we just move on to the next line. |
102 | // TODO - We would want to redirect I/O, capture some keys (^C), | 106 | // TODO - We would want to redirect I/O, capture some keys (^C), |
103 | // but pass the rest on. | 107 | // but pass the rest on. |
diff --git a/handlekeys.c b/handlekeys.c index 2fc6437..322d41e 100644 --- a/handlekeys.c +++ b/handlekeys.c | |||
@@ -18,7 +18,8 @@ struct key | |||
18 | char *name; | 18 | char *name; |
19 | }; | 19 | }; |
20 | 20 | ||
21 | // This table includes some variations I have found on some terminals, and the MC "Esc digit" versions. | 21 | // This table includes some variations I have found on some terminals, |
22 | // and the MC "Esc digit" versions. | ||
22 | // http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. | 23 | // http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. |
23 | // TODO - Don't think I got all the linux console variations. | 24 | // TODO - Don't think I got all the linux console variations. |
24 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. | 25 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. |
@@ -26,13 +27,15 @@ struct key | |||
26 | // TODO - Add other miscelany that does not use an escape sequence. | 27 | // TODO - Add other miscelany that does not use an escape sequence. |
27 | 28 | ||
28 | // This is sorted by type, though there is some overlap. | 29 | // This is sorted by type, though there is some overlap. |
29 | // Human typing speeds wont need binary searching speeds on this small table. | 30 | // Human typing speeds wont need fast searching speeds on this small table. |
30 | // So simple wins out over speed, and sorting by terminal type wins the simple test. | 31 | // So simple wins out over speed, and sorting by terminal type wins |
32 | // the simple test. | ||
31 | static struct key keys[] = | 33 | static struct key keys[] = |
32 | { | 34 | { |
33 | // Control characters. | 35 | // Control characters. |
34 | // {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. | 36 | // Commented out coz it's the C string terminator, and may confuse things. |
35 | {"\x01", "^A"}, // SOH Apparently sometimes sent as Home | 37 | //{"\x00", "^@"}, // NUL |
38 | {"\x01", "^A"}, // SOH Apparently sometimes sent as Home. | ||
36 | {"\x02", "^B"}, // STX | 39 | {"\x02", "^B"}, // STX |
37 | {"\x03", "^C"}, // ETX SIGINT Emacs and vi. | 40 | {"\x03", "^C"}, // ETX SIGINT Emacs and vi. |
38 | {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. | 41 | {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. |
@@ -40,17 +43,17 @@ static struct key keys[] = | |||
40 | {"\x06", "^F"}, // ACK | 43 | {"\x06", "^F"}, // ACK |
41 | {"\x07", "^G"}, // BEL | 44 | {"\x07", "^G"}, // BEL |
42 | {"\x08", "Del"}, // BS Delete key, usually. | 45 | {"\x08", "Del"}, // BS Delete key, usually. |
43 | {"\x09", "Tab"}, // HT Tab key. | 46 | {"\x09", "Tab"}, // HT |
44 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. | 47 | {"\x0A", "Return"}, // LF Roxterm translates Ctrl-M to this. |
45 | {"\x0B", "^K"}, // VT | 48 | {"\x0B", "^K"}, // VT |
46 | {"\x0C", "^L"}, // FF | 49 | {"\x0C", "^L"}, // FF |
47 | {"\x0D", "^M"}, // CR Other Return key, usually. | 50 | {"\x0D", "^M"}, // CR Other Return key, usually. |
48 | {"\x0E", "^N"}, // SO | 51 | {"\x0E", "^N"}, // SO |
49 | {"\x0F", "^O"}, // SI DISCARD | 52 | {"\x0F", "^O"}, // SI DISCARD |
50 | {"\x10", "^P"}, // DLE | 53 | {"\x10", "^P"}, // DLE |
51 | {"\x11", "^Q"}, // DC1 SIGCONT Vi, and made up commands in MC, which seem to work anyway. | 54 | {"\x11", "^Q"}, // DC1 SIGCONT Vi. |
52 | {"\x12", "^R"}, // DC2 | 55 | {"\x12", "^R"}, // DC2 |
53 | {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi, so much for "can't be caught". | 56 | {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi. |
54 | {"\x14", "^T"}, // DC4 SIGINFO STATUS | 57 | {"\x14", "^T"}, // DC4 SIGINFO STATUS |
55 | {"\x15", "^U"}, // NAK KILL character | 58 | {"\x15", "^U"}, // NAK KILL character |
56 | {"\x16", "^V"}, // SYN LNEXT | 59 | {"\x16", "^V"}, // SYN LNEXT |
@@ -58,17 +61,21 @@ static struct key keys[] = | |||
58 | {"\x18", "^X"}, // CAN KILL character | 61 | {"\x18", "^X"}, // CAN KILL character |
59 | {"\x19", "^Y"}, // EM DSUSP SIGTSTP | 62 | {"\x19", "^Y"}, // EM DSUSP SIGTSTP |
60 | {"\x1A", "^Z"}, // SUB SIGTSTP | 63 | {"\x1A", "^Z"}, // SUB SIGTSTP |
61 | // {"\x1B", "^["}, // ESC Esc key. Commented out coz it's the ANSI start byte in the below multibyte keys. Handled in the code with a timeout. | 64 | // Commented out coz it's the ANSI start byte in the below multibyte keys. |
62 | {"\x1C", "^\\"}, // FS SIGQUIT Some say ^D is SIGQUIT, but my tests say it's this. | 65 | // Handled in the code with a timeout. |
66 | //{"\x1B", "^["}, // ESC Esc key. | ||
67 | {"\x1C", "^\\"}, // FS SIGQUIT | ||
63 | {"\x1D", "^]"}, // GS | 68 | {"\x1D", "^]"}, // GS |
64 | {"\x1E", "^^"}, // RS | 69 | {"\x1E", "^^"}, // RS |
65 | {"\x1F", "^_"}, // US | 70 | {"\x1F", "^_"}, // US |
66 | {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? | 71 | {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? |
67 | // {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". Commented out for the same reason Esc is. | 72 | // Commented out for the same reason Esc is. |
73 | //{"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". | ||
68 | 74 | ||
69 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. | 75 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. |
70 | // Even though we have a proper CSI parser, these should still be in this table. | 76 | // Even though we have a proper CSI parser, |
71 | // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. | 77 | // these should still be in this table. Coz we would need a table anyway |
78 | // in the CSI parser, so might as well keep them with the others. | ||
72 | // Also, less code, no need to have a separate scanner for that other table. | 79 | // Also, less code, no need to have a separate scanner for that other table. |
73 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. | 80 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. |
74 | {"\x9B\x32~", "Ins"}, | 81 | {"\x9B\x32~", "Ins"}, |
@@ -113,7 +120,8 @@ static struct key keys[] = | |||
113 | {"\x9B\x32\x33;2~", "Shift F11"}, | 120 | {"\x9B\x32\x33;2~", "Shift F11"}, |
114 | {"\x9B\x32\x34;2~", "Shift F12"}, | 121 | {"\x9B\x32\x34;2~", "Shift F12"}, |
115 | 122 | ||
116 | // "Normal" Some terminals are special, and it seems they only have four function keys. | 123 | // "Normal" Some terminals are special, and it seems they only have |
124 | // four function keys. | ||
117 | {"\x9B\x41", "Up"}, | 125 | {"\x9B\x41", "Up"}, |
118 | {"\x9B\x42", "Down"}, | 126 | {"\x9B\x42", "Down"}, |
119 | {"\x9B\x43", "Right"}, | 127 | {"\x9B\x43", "Right"}, |
@@ -161,9 +169,12 @@ static struct key keys[] = | |||
161 | {"\x1BO1;2S", "Shift F4"}, | 169 | {"\x1BO1;2S", "Shift F4"}, |
162 | 170 | ||
163 | // MC "Esc digit" specials. | 171 | // MC "Esc digit" specials. |
164 | // NOTE - The MC Esc variations might not be such a good idea, other programs want the Esc key for other things. | 172 | // NOTE - The MC Esc variations might not be such a good idea, other programs |
165 | // Notably seems that "Esc somekey" is used in place of "Alt somekey" AKA "Meta somekey" coz apparently some OSes swallow those. | 173 | // want the Esc key for other things. |
166 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". | 174 | // Notably seems that "Esc somekey" is used in place of "Alt somekey" |
175 | // AKA "Meta somekey" coz apparently some OSes swallow those. | ||
176 | // Conversely, some terminals send "Esc somekey" when you do | ||
177 | // "Alt somekey". | ||
167 | // MC Esc variants might be used on Macs for other things? | 178 | // MC Esc variants might be used on Macs for other things? |
168 | {"\x1B\x31", "F1"}, | 179 | {"\x1B\x31", "F1"}, |
169 | {"\x1B\x32", "F2"}, | 180 | {"\x1B\x32", "F2"}, |
@@ -186,8 +197,11 @@ static void handleSIGWINCH(int signalNumber) | |||
186 | } | 197 | } |
187 | 198 | ||
188 | // TODO - Unhandled complications - | 199 | // TODO - Unhandled complications - |
189 | // Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands. | 200 | // Less and more have the "ZZ" command, but nothing else seems to have |
190 | void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)) | 201 | // multi ordinary character commands. |
202 | void handle_keys(long extra, | ||
203 | int (*handle_sequence)(long extra, char *sequence), | ||
204 | void (*handle_CSI)(long extra, char *command, int *params, int count)) | ||
191 | { | 205 | { |
192 | fd_set selectFds; | 206 | fd_set selectFds; |
193 | struct timespec timeOut; | 207 | struct timespec timeOut; |
@@ -201,13 +215,16 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
201 | 215 | ||
202 | // Terminals send the SIGWINCH signal when they resize. | 216 | // Terminals send the SIGWINCH signal when they resize. |
203 | memset(&sigAction, 0, sizeof(sigAction)); | 217 | memset(&sigAction, 0, sizeof(sigAction)); |
204 | sigAction.sa_flags = SA_RESTART;// Useless if we are using poll. | ||
205 | if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) perror_exit("can't set signal handler SIGWINCH"); | ||
206 | sigAction.sa_handler = handleSIGWINCH; | 218 | sigAction.sa_handler = handleSIGWINCH; |
219 | sigAction.sa_flags = SA_RESTART; // Useless if we are using poll. | ||
220 | if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) | ||
221 | perror_exit("can't set signal handler for SIGWINCH"); | ||
207 | sigemptyset(&signalMask); | 222 | sigemptyset(&signalMask); |
208 | sigaddset(&signalMask, SIGWINCH); | 223 | sigaddset(&signalMask, SIGWINCH); |
209 | 224 | ||
210 | // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. | 225 | // TODO - OS buffered keys might be a problem, but we can't do the |
226 | // usual timestamp filter for now. | ||
227 | |||
211 | stillRunning = 1; | 228 | stillRunning = 1; |
212 | while (stillRunning) | 229 | while (stillRunning) |
213 | { | 230 | { |
@@ -218,20 +235,26 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
218 | FD_SET(0, &selectFds); | 235 | FD_SET(0, &selectFds); |
219 | timeOut.tv_sec = 0; timeOut.tv_nsec = 100000000; // One tenth of a second. | 236 | timeOut.tv_sec = 0; timeOut.tv_nsec = 100000000; // One tenth of a second. |
220 | 237 | ||
221 | // TODO - A bit unstable at the moment, something makes it go into a horrid CPU eating edit line flicker mode sometimes. And / or vi mode can crash on exit (stack smash). | 238 | // TODO - A bit unstable at the moment, something makes it go into |
239 | // a horrid CPU eating edit line flicker mode sometimes. And / or vi mode | ||
240 | // can crash on exit (stack smash). | ||
222 | // This might be fixed now. | 241 | // This might be fixed now. |
223 | 242 | ||
224 | // We got a "terminal size changed" signal, ask the terminal how big it is now. | 243 | // We got a "terminal size changed" signal, ask the terminal |
244 | // how big it is now. | ||
225 | if (sigWinch) | 245 | if (sigWinch) |
226 | { | 246 | { |
227 | // Send - save cursor position, down 999, right 999, request cursor position, restore cursor position. | 247 | // Send - save cursor position, down 999, right 999, |
248 | // request cursor position, restore cursor position. | ||
228 | fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); | 249 | fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); |
229 | fflush(stdout); | 250 | fflush(stdout); |
230 | sigWinch = 0; | 251 | sigWinch = 0; |
231 | } | 252 | } |
232 | 253 | ||
233 | // TODO - Should only ask for a time out after we get an Escape, or the user requested time ticks. | 254 | // TODO - Should only ask for a time out after we get an Escape, or |
234 | // I wanted to use poll, but that would mean using ppoll, which is Linux only, and involves defining swear words to get it. | 255 | // the user requested time ticks. |
256 | // I wanted to use poll, but that would mean using ppoll, which is | ||
257 | // Linux only, and involves defining swear words to get it. | ||
235 | p = pselect(0 + 1, &selectFds, NULL, NULL, &timeOut, &signalMask); | 258 | p = pselect(0 + 1, &selectFds, NULL, NULL, &timeOut, &signalMask); |
236 | if (0 > p) | 259 | if (0 > p) |
237 | { | 260 | { |
@@ -239,21 +262,26 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
239 | continue; | 262 | continue; |
240 | perror_exit("poll"); | 263 | perror_exit("poll"); |
241 | } | 264 | } |
242 | else if (0 == p) // A timeout, trigger a time event. | 265 | else if (0 == p) // A timeout, trigger a time event. |
243 | { | 266 | { |
244 | if ((0 == buffer[1]) && ('\x1B' == buffer[0])) | 267 | if ((0 == buffer[1]) && ('\x1B' == buffer[0])) |
245 | { | 268 | { |
246 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | 269 | // After a short delay to check, this is a real Escape key, |
247 | // TODO - so far the only uses of this have the escape at the start, but maybe a strcat is needed instead later? | 270 | // not part of an escape sequence, so deal with it. |
271 | // TODO - So far the only uses of this have the escape at the start, | ||
272 | // but maybe a strcat is needed instead later? | ||
248 | strcpy(sequence, "^["); | 273 | strcpy(sequence, "^["); |
249 | buffer[0] = buffIndex = 0; | 274 | buffer[0] = buffIndex = 0; |
250 | } | 275 | } |
251 | // TODO - Call some sort of timer tick callback. This wont be a precise timed event, but don't think we need one. | 276 | // TODO - Call some sort of timer tick callback. This wont be |
277 | // a precise timed event, but don't think we need one. | ||
252 | } | 278 | } |
253 | else if ((0 < p) && FD_ISSET(0, &selectFds)) | 279 | else if ((0 < p) && FD_ISSET(0, &selectFds)) |
254 | { | 280 | { |
255 | // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. | 281 | // I am assuming that we get the input atomically, each multibyte key |
256 | // If that's not true (which is entirely likely), then we have to get complicated with circular buffers and stuff, or just one byte at a time. | 282 | // fits neatly into one read. |
283 | // If that's not true (which is entirely likely), then we have to get | ||
284 | // complicated with circular buffers and stuff, or just one byte at a time. | ||
257 | j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); | 285 | j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); |
258 | if (j < 0) // An error happened. | 286 | if (j < 0) // An error happened. |
259 | { | 287 | { |
@@ -301,9 +329,9 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
301 | } | 329 | } |
302 | 330 | ||
303 | // Check for known key sequences. | 331 | // Check for known key sequences. |
304 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | 332 | // For a real timeout checked Esc, buffer is now empty, so this for loop |
305 | // While it's true we could avoid it by checking, the user already had to wait for a time out, and this loop wont take THAT long. | 333 | // wont find it anyway. While it's true we could avoid it by checking, |
306 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. | 334 | // the user already had to wait for a time out, and this loop wont take THAT long. |
307 | for (j = 0; j < (sizeof(keys) / sizeof(*keys)); j++) | 335 | for (j = 0; j < (sizeof(keys) / sizeof(*keys)); j++) |
308 | { | 336 | { |
309 | if (strcmp(keys[j].code, buffer) == 0) | 337 | if (strcmp(keys[j].code, buffer) == 0) |
@@ -343,7 +371,8 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
343 | 371 | ||
344 | if ('M' == buffer[1]) | 372 | if ('M' == buffer[1]) |
345 | { | 373 | { |
346 | // TODO - We have a mouse report, which is CSI M ..., where the rest is binary encoded, more or less. Not fitting into the CSI format. | 374 | // TODO - We have a mouse report, which is CSI M ..., where the rest is |
375 | // binary encoded, more or less. Not fitting into the CSI format. | ||
347 | } | 376 | } |
348 | else | 377 | else |
349 | { | 378 | { |
@@ -379,10 +408,12 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
379 | // Only stomp on the ; if it's really the ;. | 408 | // Only stomp on the ; if it's really the ;. |
380 | if (t) | 409 | if (t) |
381 | buffer[j] = 0; | 410 | buffer[j] = 0; |
382 | // Empty parameters are default parameters, so only deal with non defaults. | 411 | // Empty parameters are default parameters, so only deal with |
412 | // non defaults. | ||
383 | if (';' != buffer[csIndex] || (!t)) | 413 | if (';' != buffer[csIndex] || (!t)) |
384 | { | 414 | { |
385 | // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. | 415 | // TODO - Might be ":" in the number somewhere, but we are not |
416 | // expecting any in anything we do. | ||
386 | csParams[p] = atoi(&buffer[csIndex]); | 417 | csParams[p] = atoi(&buffer[csIndex]); |
387 | } | 418 | } |
388 | p++; | 419 | p++; |
@@ -399,7 +430,8 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), | |||
399 | } | 430 | } |
400 | 431 | ||
401 | csi = 0; | 432 | csi = 0; |
402 | // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. | 433 | // Wether or not it's a CSI we understand, it's been handled either here |
434 | // or in the key sequence scanning above. | ||
403 | buffer[0] = buffIndex = 0; | 435 | buffer[0] = buffIndex = 0; |
404 | } | 436 | } |
405 | 437 | ||