| ... | ... |
@@ -87,9 +87,10 @@ char tty_getchar(tty_t *tty) {
|
| 87 | 87 |
} |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
-int tty_input_ready(tty_t *tty) {
|
|
| 90 |
+int tty_input_ready(tty_t *tty, int pending) {
|
|
| 91 | 91 |
fd_set readfs; |
| 92 |
- struct timeval tv = {0, 0};
|
|
| 92 |
+ struct timeval tv = {0, pending ? 500000 : 0};
|
|
| 93 |
+ FD_ZERO(&readfs); |
|
| 93 | 94 |
FD_SET(tty->fdin, &readfs); |
| 94 | 95 |
select(tty->fdin + 1, &readfs, NULL, NULL, &tv); |
| 95 | 96 |
return FD_ISSET(tty->fdin, &readfs); |
| ... | ... |
@@ -17,7 +17,7 @@ void tty_close(tty_t *tty); |
| 17 | 17 |
void tty_init(tty_t *tty, const char *tty_filename); |
| 18 | 18 |
void tty_getwinsz(tty_t *tty); |
| 19 | 19 |
char tty_getchar(tty_t *tty); |
| 20 |
-int tty_input_ready(tty_t *tty); |
|
| 20 |
+int tty_input_ready(tty_t *tty, int pending); |
|
| 21 | 21 |
|
| 22 | 22 |
void tty_setfg(tty_t *tty, int fg); |
| 23 | 23 |
void tty_setinvert(tty_t *tty); |
| ... | ... |
@@ -257,6 +257,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices, |
| 257 | 257 |
state->tty = tty; |
| 258 | 258 |
state->choices = choices; |
| 259 | 259 |
state->options = options; |
| 260 |
+ state->ambiguous_key_pending = 0; |
|
| 260 | 261 |
|
| 261 | 262 |
strcpy(state->input, ""); |
| 262 | 263 |
strcpy(state->search, ""); |
| ... | ... |
@@ -279,7 +280,9 @@ typedef struct {
|
| 279 | 280 |
|
| 280 | 281 |
#define KEY_CTRL(key) ((const char[]){((key) - ('@')), '\0'})
|
| 281 | 282 |
|
| 282 |
-static const keybinding_t keybindings[] = {{"\x7f", action_del_char}, /* DEL */
|
|
| 283 |
+static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC */
|
|
| 284 |
+ {"\x7f", action_del_char}, /* DEL */
|
|
| 285 |
+ |
|
| 283 | 286 |
{KEY_CTRL('H'), action_del_char}, /* Backspace (C-H) */
|
| 284 | 287 |
{KEY_CTRL('W'), action_del_word}, /* C-W */
|
| 285 | 288 |
{KEY_CTRL('U'), action_del_all}, /* C-U */
|
| ... | ... |
@@ -314,23 +317,40 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char}, /* DEL */
|
| 314 | 317 |
|
| 315 | 318 |
#undef KEY_CTRL |
| 316 | 319 |
|
| 317 |
-static void handle_input(tty_interface_t *state, const char *s) {
|
|
| 320 |
+static void handle_input(tty_interface_t *state, const char *s, int handle_ambiguous_key) {
|
|
| 321 |
+ state->ambiguous_key_pending = 0; |
|
| 322 |
+ |
|
| 318 | 323 |
char *input = state->input; |
| 319 | 324 |
strcat(state->input, s); |
| 320 | 325 |
|
| 321 |
- /* See if we have matched a keybinding */ |
|
| 326 |
+ /* Figure out if we have completed a keybinding and whether we're in the |
|
| 327 |
+ * middle of one (both can happen, because of Esc). */ |
|
| 328 |
+ int found_keybinding = -1; |
|
| 329 |
+ int in_middle = 0; |
|
| 322 | 330 |
for (int i = 0; keybindings[i].key; i++) {
|
| 323 |
- if (!strcmp(input, keybindings[i].key)) {
|
|
| 324 |
- keybindings[i].action(state); |
|
| 325 |
- strcpy(input, ""); |
|
| 326 |
- return; |
|
| 327 |
- } |
|
| 331 |
+ if (!strcmp(input, keybindings[i].key)) |
|
| 332 |
+ found_keybinding = i; |
|
| 333 |
+ else if (!strncmp(input, keybindings[i].key, strlen(state->input))) |
|
| 334 |
+ in_middle = 1; |
|
| 328 | 335 |
} |
| 329 | 336 |
|
| 330 |
- /* Check if we are in the middle of a keybinding */ |
|
| 331 |
- for (int i = 0; keybindings[i].key; i++) |
|
| 332 |
- if (!strncmp(input, keybindings[i].key, strlen(input))) |
|
| 333 |
- return; |
|
| 337 |
+ /* If we have an unambiguous keybinding, run it. */ |
|
| 338 |
+ if (found_keybinding != -1 && (!in_middle || handle_ambiguous_key)) {
|
|
| 339 |
+ keybindings[found_keybinding].action(state); |
|
| 340 |
+ strcpy(input, ""); |
|
| 341 |
+ return; |
|
| 342 |
+ } |
|
| 343 |
+ |
|
| 344 |
+ /* We could have a complete keybinding, or could be in the middle of one. |
|
| 345 |
+ * We'll need to wait a few milliseconds to find out. */ |
|
| 346 |
+ if (found_keybinding != -1 && in_middle) {
|
|
| 347 |
+ state->ambiguous_key_pending = 1; |
|
| 348 |
+ return; |
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 351 |
+ /* Wait for more if we are in the middle of a keybinding */ |
|
| 352 |
+ if (in_middle) |
|
| 353 |
+ return; |
|
| 334 | 354 |
|
| 335 | 355 |
/* No matching keybinding, add to search */ |
| 336 | 356 |
for (int i = 0; input[i]; i++) |
| ... | ... |
@@ -347,13 +367,21 @@ int tty_interface_run(tty_interface_t *state) {
|
| 347 | 367 |
for (;;) {
|
| 348 | 368 |
do {
|
| 349 | 369 |
char s[2] = {tty_getchar(state->tty), '\0'};
|
| 350 |
- handle_input(state, s); |
|
| 370 |
+ handle_input(state, s, 0); |
|
| 351 | 371 |
|
| 352 | 372 |
if (state->exit >= 0) |
| 353 | 373 |
return state->exit; |
| 354 | 374 |
|
| 355 | 375 |
draw(state); |
| 356 |
- } while (tty_input_ready(state->tty)); |
|
| 376 |
+ } while (tty_input_ready(state->tty, state->ambiguous_key_pending)); |
|
| 377 |
+ |
|
| 378 |
+ if (state->ambiguous_key_pending) {
|
|
| 379 |
+ char s[1] = ""; |
|
| 380 |
+ handle_input(state, s, 1); |
|
| 381 |
+ |
|
| 382 |
+ if (state->exit >= 0) |
|
| 383 |
+ return state->exit; |
|
| 384 |
+ } |
|
| 357 | 385 |
|
| 358 | 386 |
update_state(state); |
| 359 | 387 |
} |