diff options
Diffstat (limited to '')
| -rw-r--r-- | handlekeys.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/handlekeys.c b/handlekeys.c new file mode 100644 index 0000000..70ecfd7 --- /dev/null +++ b/handlekeys.c | |||
| @@ -0,0 +1,419 @@ | |||
| 1 | /* handlekeys.c - Generic terminal input handler. | ||
| 2 | * | ||
| 3 | * Copyright 2012 David Seikel <won_fang@yahoo.com.au> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include "toys.h" | ||
| 7 | #include "handlekeys.h" | ||
| 8 | |||
| 9 | struct key | ||
| 10 | { | ||
| 11 | char *code; | ||
| 12 | char *name; | ||
| 13 | }; | ||
| 14 | |||
| 15 | // This table includes some variations I have found on some terminals, and the MC "Esc digit" versions. | ||
| 16 | // http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. | ||
| 17 | // TODO - Don't think I got all the linux console variations. | ||
| 18 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. | ||
| 19 | // TODO - tmux messes with the shift function keys somehow. | ||
| 20 | // TODO - Add other miscelany that does not use an escape sequence. | ||
| 21 | |||
| 22 | // This is sorted by type, though there is some overlap. | ||
| 23 | // Human typing speeds wont need binary searching speeds on this small table. | ||
| 24 | // So simple wins out over speed, and sorting by terminal type wins the simple test. | ||
| 25 | static struct key keys[] = | ||
| 26 | { | ||
| 27 | // Control characters. | ||
| 28 | // {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. | ||
| 29 | {"\x01", "^A"}, // SOH Apparently sometimes sent as Home | ||
| 30 | {"\x02", "^B"}, // STX | ||
| 31 | {"\x03", "^C"}, // ETX SIGINT Emacs and vi. | ||
| 32 | {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. | ||
| 33 | {"\x05", "^E"}, // ENQ Apparently sometimes sent as End | ||
| 34 | {"\x06", "^F"}, // ACK | ||
| 35 | {"\x07", "^G"}, // BEL | ||
| 36 | {"\x08", "Del"}, // BS Delete key, usually. | ||
| 37 | {"\x09", "Tab"}, // HT Tab key. | ||
| 38 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. | ||
| 39 | {"\x0B", "^K"}, // VT | ||
| 40 | {"\x0C", "^L"}, // FF | ||
| 41 | {"\x0D", "^M"}, // CR Other Return key, usually. | ||
| 42 | {"\x0E", "^N"}, // SO | ||
| 43 | {"\x0F", "^O"}, // SI DISCARD | ||
| 44 | {"\x10", "^P"}, // DLE | ||
| 45 | {"\x11", "^Q"}, // DC1 SIGCONT Vi, and made up commands in MC, which seem to work anyway. | ||
| 46 | {"\x12", "^R"}, // DC2 | ||
| 47 | {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi, so much for "can't be caught". | ||
| 48 | {"\x14", "^T"}, // DC4 SIGINFO STATUS | ||
| 49 | {"\x15", "^U"}, // NAK KILL character | ||
| 50 | {"\x16", "^V"}, // SYN LNEXT | ||
| 51 | {"\x17", "^W"}, // ETB WERASE | ||
| 52 | {"\x18", "^X"}, // CAN KILL character | ||
| 53 | {"\x19", "^Y"}, // EM DSUSP SIGTSTP | ||
| 54 | {"\x1A", "^Z"}, // SUB SIGTSTP | ||
| 55 | // {"\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. | ||
| 56 | {"\x1C", "^\\"}, // FS SIGQUIT Some say ^D is SIGQUIT, but my tests say it's this. | ||
| 57 | {"\x1D", "^]"}, // GS | ||
| 58 | {"\x1E", "^^"}, // RS | ||
| 59 | {"\x1F", "^_"}, // US | ||
| 60 | {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? | ||
| 61 | // {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". Commented out for the same reason Esc is. | ||
| 62 | |||
| 63 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. | ||
| 64 | // Even though we have a proper CSI parser, these should still be in this table. | ||
| 65 | // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. | ||
| 66 | // Also, less code, no need to have a separate scanner for that other table. | ||
| 67 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. | ||
| 68 | {"\x9B\x32~", "Ins"}, | ||
| 69 | {"\x9B\x33~", "Del"}, | ||
| 70 | {"\x9B\x34~", "End"}, // Duplicate, think I've seen this somewhere. | ||
| 71 | {"\x9B\x35~", "PgUp"}, | ||
| 72 | {"\x9B\x36~", "PgDn"}, | ||
| 73 | {"\x9B\x37~", "Home"}, | ||
| 74 | {"\x9B\x38~", "End"}, | ||
| 75 | {"\x9B\x31\x31~", "F1"}, | ||
| 76 | {"\x9B\x31\x32~", "F2"}, | ||
| 77 | {"\x9B\x31\x33~", "F3"}, | ||
| 78 | {"\x9B\x31\x34~", "F4"}, | ||
| 79 | {"\x9B\x31\x35~", "F5"}, | ||
| 80 | {"\x9B\x31\x37~", "F6"}, | ||
| 81 | {"\x9B\x31\x38~", "F7"}, | ||
| 82 | {"\x9B\x31\x39~", "F8"}, | ||
| 83 | {"\x9B\x32\x30~", "F9"}, | ||
| 84 | {"\x9B\x32\x31~", "F10"}, | ||
| 85 | {"\x9B\x32\x33~", "F11"}, | ||
| 86 | {"\x9B\x32\x34~", "F12"}, | ||
| 87 | |||
| 88 | // As above, ";2" means shift modifier. | ||
| 89 | {"\x9B\x31;2~", "Shift Home"}, | ||
| 90 | {"\x9B\x32;2~", "Shift Ins"}, | ||
| 91 | {"\x9B\x33;2~", "Shift Del"}, | ||
| 92 | {"\x9B\x34;2~", "Shift End"}, | ||
| 93 | {"\x9B\x35;2~", "Shift PgUp"}, | ||
| 94 | {"\x9B\x36;2~", "Shift PgDn"}, | ||
| 95 | {"\x9B\x37;2~", "Shift Home"}, | ||
| 96 | {"\x9B\x38;2~", "Shift End"}, | ||
| 97 | {"\x9B\x31\x31;2~", "Shift F1"}, | ||
| 98 | {"\x9B\x31\x32;2~", "Shift F2"}, | ||
| 99 | {"\x9B\x31\x33;2~", "Shift F3"}, | ||
| 100 | {"\x9B\x31\x34;2~", "Shift F4"}, | ||
| 101 | {"\x9B\x31\x35;2~", "Shift F5"}, | ||
| 102 | {"\x9B\x31\x37;2~", "Shift F6"}, | ||
| 103 | {"\x9B\x31\x38;2~", "Shift F7"}, | ||
| 104 | {"\x9B\x31\x39;2~", "Shift F8"}, | ||
| 105 | {"\x9B\x32\x30;2~", "Shift F9"}, | ||
| 106 | {"\x9B\x32\x31;2~", "Shift F10"}, | ||
| 107 | {"\x9B\x32\x33;2~", "Shift F11"}, | ||
| 108 | {"\x9B\x32\x34;2~", "Shift F12"}, | ||
| 109 | |||
| 110 | // "Normal" Some terminals are special, and it seems they only have four function keys. | ||
| 111 | {"\x9B\x41", "Up"}, | ||
| 112 | {"\x9B\x42", "Down"}, | ||
| 113 | {"\x9B\x43", "Right"}, | ||
| 114 | {"\x9B\x44", "Left"}, | ||
| 115 | {"\x9B\x46", "End"}, | ||
| 116 | {"\x9BH", "Home"}, | ||
| 117 | {"\x9BP", "F1"}, | ||
| 118 | {"\x9BQ", "F2"}, | ||
| 119 | {"\x9BR", "F3"}, | ||
| 120 | {"\x9BS", "F4"}, | ||
| 121 | {"\x9B\x31;2P", "Shift F1"}, | ||
| 122 | {"\x9B\x31;2Q", "Shift F2"}, | ||
| 123 | {"\x9B\x31;2R", "Shift F3"}, | ||
| 124 | {"\x9B\x31;2S", "Shift F4"}, | ||
| 125 | |||
| 126 | // "Application" Esc O is known as SS3 | ||
| 127 | {"\x1BOA", "Up"}, | ||
| 128 | {"\x1BOB", "Down"}, | ||
| 129 | {"\x1BOC", "Right"}, | ||
| 130 | {"\x1BOD", "Left"}, | ||
| 131 | {"\x1BOF", "End"}, | ||
| 132 | {"\x1BOH", "Home"}, | ||
| 133 | {"\x1BOn", "Del"}, | ||
| 134 | {"\x1BOp", "Ins"}, | ||
| 135 | {"\x1BOq", "End"}, | ||
| 136 | {"\x1BOw", "Home"}, | ||
| 137 | {"\x1BOP", "F1"}, | ||
| 138 | {"\x1BOO", "F2"}, | ||
| 139 | {"\x1BOR", "F3"}, | ||
| 140 | {"\x1BOS", "F4"}, | ||
| 141 | {"\x1BOT", "F5"}, | ||
| 142 | // These two conflict with the above four function key variations. | ||
| 143 | {"\x9BR", "F6"}, | ||
| 144 | {"\x9BS", "F7"}, | ||
| 145 | {"\x9BT", "F8"}, | ||
| 146 | {"\x9BU", "F9"}, | ||
| 147 | {"\x9BV", "F10"}, | ||
| 148 | {"\x9BW", "F11"}, | ||
| 149 | {"\x9BX", "F12"}, | ||
| 150 | |||
| 151 | // Can't remember, but saw them somewhere. | ||
| 152 | {"\x1BO1;2P", "Shift F1"}, | ||
| 153 | {"\x1BO1;2Q", "Shift F2"}, | ||
| 154 | {"\x1BO1;2R", "Shift F3"}, | ||
| 155 | {"\x1BO1;2S", "Shift F4"}, | ||
| 156 | |||
| 157 | // MC "Esc digit" specials. | ||
| 158 | // NOTE - The MC Esc variations might not be such a good idea, other programs want the Esc key for other things. | ||
| 159 | // Notably seems that "Esc somekey" is used in place of "Alt somekey" AKA "Meta somekey" coz apparently some OSes swallow those. | ||
| 160 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". | ||
| 161 | // MC Esc variants might be used on Macs for other things? | ||
| 162 | {"\x1B\x31", "F1"}, | ||
| 163 | {"\x1B\x32", "F2"}, | ||
| 164 | {"\x1B\x33", "F3"}, | ||
| 165 | {"\x1B\x34", "F4"}, | ||
| 166 | {"\x1B\x35", "F5"}, | ||
| 167 | {"\x1B\x36", "F6"}, | ||
| 168 | {"\x1B\x37", "F7"}, | ||
| 169 | {"\x1B\x38", "F8"}, | ||
| 170 | {"\x1B\x39", "F9"}, | ||
| 171 | {"\x1B\x30", "F10"}, | ||
| 172 | |||
| 173 | /* TODO - Rob says - | ||
| 174 | ...you don't need a NULL terminator for | ||
| 175 | an array, you can do sizeof(table)/sizeof(*table). Divide the size of | ||
| 176 | the table (in bytes) by the size of a member of the table (in bytes) to | ||
| 177 | get the number of entries. | ||
| 178 | |||
| 179 | I should try that trick. Seems to work, let's do that everywhere. | ||
| 180 | */ | ||
| 181 | |||
| 182 | {NULL, NULL} | ||
| 183 | }; | ||
| 184 | |||
| 185 | static volatile sig_atomic_t sigWinch; | ||
| 186 | static int stillRunning; | ||
| 187 | |||
| 188 | static void handleSignals(int signo) | ||
| 189 | { | ||
| 190 | sigWinch = 1; | ||
| 191 | } | ||
| 192 | |||
| 193 | // TODO - Unhandled complications - | ||
| 194 | // Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands. | ||
| 195 | void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)) | ||
| 196 | { | ||
| 197 | fd_set selectFds; | ||
| 198 | struct timespec timeout; | ||
| 199 | struct sigaction sigAction, oldSigAction; | ||
| 200 | sigset_t signalMask; | ||
| 201 | char buffer[20], sequence[20]; | ||
| 202 | int buffIndex = 0; | ||
| 203 | |||
| 204 | buffer[0] = 0; | ||
| 205 | sequence[0] = 0; | ||
| 206 | |||
| 207 | // Terminals send the SIGWINCH signal when they resize. | ||
| 208 | memset(&sigAction, 0, sizeof(sigAction)); | ||
| 209 | sigAction.sa_handler = handleSignals; | ||
| 210 | sigAction.sa_flags = SA_RESTART;// Useless if we are using poll. | ||
| 211 | if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) perror_exit("can't set signal handler SIGWINCH"); | ||
| 212 | sigemptyset(&signalMask); | ||
| 213 | sigaddset(&signalMask, SIGWINCH); | ||
| 214 | |||
| 215 | // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. | ||
| 216 | stillRunning = 1; | ||
| 217 | while (stillRunning) | ||
| 218 | { | ||
| 219 | int j, p, csi = 0; | ||
| 220 | |||
| 221 | // Apparently it's more portable to reset these each time. | ||
| 222 | FD_ZERO(&selectFds); | ||
| 223 | FD_SET(0, &selectFds); | ||
| 224 | timeout.tv_sec = 0; timeout.tv_nsec = 100000000; // One tenth of a second. | ||
| 225 | |||
| 226 | // 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). | ||
| 227 | // This might be fixed now. | ||
| 228 | |||
| 229 | // We got a "terminal size changed" signal, ask the terminal how big it is now. | ||
| 230 | if (sigWinch) | ||
| 231 | { | ||
| 232 | // Send - save cursor position, down 999, right 999, request cursor position, restore cursor position. | ||
| 233 | fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); | ||
| 234 | fflush(stdout); | ||
| 235 | sigWinch = 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | // TODO - Should only ask for a time out after we get an Escape, or the user requested time ticks. | ||
| 239 | // I wanted to use poll, but that would mean using ppoll, which is Linux only, and involves defining swear words to get it. | ||
| 240 | p = pselect(0 + 1, &selectFds, NULL, NULL, &timeout, &signalMask); | ||
| 241 | if (0 > p) | ||
| 242 | { | ||
| 243 | if (EINTR == errno) | ||
| 244 | continue; | ||
| 245 | perror_exit("poll"); | ||
| 246 | } | ||
| 247 | else if (0 == p) // A timeout, trigger a time event. | ||
| 248 | { | ||
| 249 | if ((0 == buffer[1]) && ('\x1B' == buffer[0])) | ||
| 250 | { | ||
| 251 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | ||
| 252 | // TODO - so far the only uses of this have the escape at the start, but maybe a strcat is needed instead later? | ||
| 253 | strcpy(sequence, "^["); | ||
| 254 | buffer[0] = buffIndex = 0; | ||
| 255 | } | ||
| 256 | // TODO - Call some sort of timer tick callback. This wont be a precise timed event, but don't think we need one. | ||
| 257 | } | ||
| 258 | else if ((0 < p) && FD_ISSET(0, &selectFds)) | ||
| 259 | { | ||
| 260 | // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. | ||
| 261 | // 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. | ||
| 262 | j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); | ||
| 263 | if (j < 0) // An error happened. | ||
| 264 | { | ||
| 265 | // For now, just ignore errors. | ||
| 266 | fprintf(stderr, "input error on %d\n", p); | ||
| 267 | fflush(stderr); | ||
| 268 | } | ||
| 269 | else if (j == 0) // End of file. | ||
| 270 | { | ||
| 271 | stillRunning = 0; | ||
| 272 | fprintf(stderr, "EOF\n"); | ||
| 273 | for (j = 0; buffer[j + 1]; j++) | ||
| 274 | fprintf(stderr, "(%x), ", (int) buffer[j]); | ||
| 275 | fflush(stderr); | ||
| 276 | } | ||
| 277 | else | ||
| 278 | { | ||
| 279 | buffIndex += j; | ||
| 280 | if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer. | ||
| 281 | { | ||
| 282 | fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence); | ||
| 283 | for (j = 0; buffer[j + 1]; j++) | ||
| 284 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | ||
| 285 | fflush(stderr); | ||
| 286 | buffIndex = 0; | ||
| 287 | } | ||
| 288 | buffer[buffIndex] = 0; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | // Check if it's a CSI before we check for the known key sequences. | ||
| 293 | if ('\x9B' == buffer[0]) | ||
| 294 | csi = 1; | ||
| 295 | if (('\x1B' == buffer[0]) && ('[' == buffer[1])) | ||
| 296 | csi = 2; | ||
| 297 | if (('\xC2' == buffer[0]) && ('\x9B' == buffer[1])) | ||
| 298 | csi = 2; | ||
| 299 | if (2 == csi) | ||
| 300 | { | ||
| 301 | buffer[0] = '\x9B'; | ||
| 302 | for (j = 1; buffer[j]; j++) | ||
| 303 | buffer[j] = buffer[j + 1]; | ||
| 304 | buffIndex--; | ||
| 305 | csi = 1; | ||
| 306 | } | ||
| 307 | |||
| 308 | // Check for known key sequences. | ||
| 309 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | ||
| 310 | // 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. | ||
| 311 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. | ||
| 312 | { | ||
| 313 | if (strcmp(keys[j].code, buffer) == 0) | ||
| 314 | { | ||
| 315 | strcat(sequence, keys[j].name); | ||
| 316 | buffer[0] = buffIndex = 0; | ||
| 317 | csi = 0; | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | // Find out if it's a CSI sequence that's not in the known key sequences. | ||
| 323 | if (csi) | ||
| 324 | { | ||
| 325 | /* ECMA-048 section 5.2 defines this, and is unreadable. | ||
| 326 | General CSI format - CSI [private] n1 ; n2 [extra] final | ||
| 327 | private 0x3c to 0x3f "<=>?" if first byte is one of these, this is a private command, if it's one of the other n1 ones, it's not private. | ||
| 328 | n1 0x30 to 0x3f "01234567890:;<=>?" ASCII digits forming a "number" | ||
| 329 | 0x3a [:] used for floats, not expecting any. Could also be used as some other sort of inter digit separator. | ||
| 330 | 0x3b [;] separates the parameters | ||
| 331 | extra 0x20 to 0x2f [ !"#$%&'()*+,-./] Can be multiple, likely isn't. | ||
| 332 | final 0x40 to 0x7e "@A .. Z[\]^_`a .. z{|}~" it's private if 0x70 to 0x7e "p .. z{|}~" | ||
| 333 | Though the "private" ~ is used for key codes. | ||
| 334 | We also have SS3 "\x1BO" for other keys, but that's not a CSI. | ||
| 335 | C0 controls, DEL (0x7f), or high characters are undefined. | ||
| 336 | TODO So abort the current CSI and start from scratch. | ||
| 337 | */ | ||
| 338 | |||
| 339 | char *t, csFinal[8]; | ||
| 340 | int csIndex = 1, csParams[8]; | ||
| 341 | |||
| 342 | csFinal[0] = 0; | ||
| 343 | p = 0; | ||
| 344 | // Unspecified params default to a value that is command dependant. | ||
| 345 | // However, they will never be negative, so we can use -1 to flag a default value. | ||
| 346 | for (j = 0; j < (sizeof(csParams) / sizeof(*csParams)); j++) | ||
| 347 | csParams[j] = -1; | ||
| 348 | |||
| 349 | if ('M' == buffer[1]) | ||
| 350 | { | ||
| 351 | // 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. | ||
| 352 | } | ||
| 353 | else | ||
| 354 | { | ||
| 355 | // Check for the private bit. | ||
| 356 | if (index("<=>?", buffer[1])) | ||
| 357 | { | ||
| 358 | csFinal[0] = buffer[1]; | ||
| 359 | csFinal[1] = 0; | ||
| 360 | csIndex++; | ||
| 361 | } | ||
| 362 | |||
| 363 | // Decode parameters. | ||
| 364 | j = csIndex; | ||
| 365 | do | ||
| 366 | { | ||
| 367 | // So we know when we get to the end of parameter space. | ||
| 368 | t = index("01234567890:;<=>?", buffer[j + 1]); | ||
| 369 | // See if we passed a paremeter. | ||
| 370 | if ((';' == buffer[j]) || (!t)) | ||
| 371 | { | ||
| 372 | // Only stomp on the ; if it's really the ;. | ||
| 373 | if (t) | ||
| 374 | buffer[j] = 0; | ||
| 375 | // Empty parameters are default parameters, so only deal with non defaults. | ||
| 376 | if (';' != buffer[csIndex] || (!t)) | ||
| 377 | { | ||
| 378 | // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. | ||
| 379 | csParams[p] = atoi(&buffer[csIndex]); | ||
| 380 | } | ||
| 381 | p++; | ||
| 382 | csIndex = j + 1; | ||
| 383 | } | ||
| 384 | j++; | ||
| 385 | } | ||
| 386 | while (t); | ||
| 387 | |||
| 388 | // Get the final command sequence, and pass it to the callback. | ||
| 389 | strcat(csFinal, &buffer[csIndex]); | ||
| 390 | if (handle_CSI) | ||
| 391 | handle_CSI(extra, csFinal, csParams, p); | ||
| 392 | } | ||
| 393 | |||
| 394 | csi = 0; | ||
| 395 | // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. | ||
| 396 | buffer[0] = buffIndex = 0; | ||
| 397 | } | ||
| 398 | |||
| 399 | // Pass the result to the callback. | ||
| 400 | if ((handle_sequence) && (sequence[0] || buffer[0])) | ||
| 401 | { | ||
| 402 | char b[strlen(sequence) + strlen(buffer) + 1]; | ||
| 403 | |||
| 404 | sprintf(b, "%s%s", sequence, buffer); | ||
| 405 | if (handle_sequence(extra, b)) | ||
| 406 | { | ||
| 407 | sequence[0] = 0; | ||
| 408 | buffer[0] = buffIndex = 0; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | sigaction(SIGWINCH, &oldSigAction, NULL); | ||
| 414 | } | ||
| 415 | |||
| 416 | void handle_keys_quit() | ||
| 417 | { | ||
| 418 | stillRunning = 0; | ||
| 419 | } | ||
