From bbaa3db47599ba25949277e7075fa61ccc1c5a3c Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Tue, 15 Apr 2014 18:38:18 +1000 Subject: Change from using a bunch of callbacks to using one, with a structure and type. --- boxes.c | 94 +++++++++++++++++++++++++--------------------- dumbsh.c | 119 +++++++++++++++++++++++++++++++---------------------------- handlekeys.c | 37 +++++++++++++++---- handlekeys.h | 65 ++++++++++++++++++++------------ showkey.c | 91 +++++++++++++++++++++++++++------------------ 5 files changed, 240 insertions(+), 166 deletions(-) diff --git a/boxes.c b/boxes.c index ef3dcc5..53cea4a 100644 --- a/boxes.c +++ b/boxes.c @@ -1651,59 +1651,69 @@ struct CSI CSIcommands[] = {"R", termSize} // Parameters are cursor line and column. Note this may be sent at other times, not just during terminal resize. }; -// Callback for incoming CSI commands from the terminal. -static void handleCSI(long extra, char *command, int *params, int count) -{ - int j; - for (j = 0; j < (sizeof(CSIcommands) / sizeof(*CSIcommands)); j++) +// Callback for incoming sequences from the terminal. +static int handleEvent(long extra, struct keyevent *event) +{ + switch (event->type) { - if (strcmp(CSIcommands[j].code, command) == 0) + case HK_CSI : { - CSIcommands[j].func(extra, params, count); + int j; + + for (j = 0; j < ARRAY_LEN(CSIcommands); j++) + { + if (strcmp(CSIcommands[j].code, event->sequence) == 0) + { + CSIcommands[j].func(extra, event->params, event->count); + break; + } + } break; } - } -} -// Callback for incoming key sequences from the user. -static int handleKeySequence(long extra, char *sequence, int isTranslated) -{ - struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away. - struct keyCommand *commands = currentBox->view->content->context->modes[currentBox->view->mode].keys; - int j, l = strlen(sequence); + case HK_KEYS : + { + struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away. + struct keyCommand *commands = currentBox->view->content->context->modes[currentBox->view->mode].keys; + int j, l = strlen(event->sequence); - // Coz things might change out from under us, find the current view. - if (commandMode) view = commandLine; - else view = currentBox->view; + // Coz things might change out from under us, find the current view. + if (commandMode) view = commandLine; + else view = currentBox->view; - // Search for a key sequence bound to a command. - for (j = 0; commands[j].key; j++) - { - if (strncmp(commands[j].key, sequence, l) == 0) - { - // If it's a partial match, keep accumulating them. - if (strlen(commands[j].key) != l) - return 0; - else + // Search for a key sequence bound to a command. + for (j = 0; commands[j].key; j++) { - doCommand(view, commands[j].command); - return 1; + if (strncmp(commands[j].key, event->sequence, l) == 0) + { + // If it's a partial match, keep accumulating them. + if (strlen(commands[j].key) != l) + return 0; + else + { + doCommand(view, commands[j].command); + return 1; + } + } } + + // See if it's ordinary keys. + // NOTE - with vi style ordinary keys can be commands, + // but they would be found by the command check above first. + if (!event->isTranslated) + { + // TODO - Should check for tabs to, and insert them. + // Though better off having a function for that? + mooshStrings(view->line, event->sequence, view->iX, 0, !overWriteMode); + view->oW = formatLine(view, view->line->line, &(view->output)); + moveCursorRelative(view, strlen(event->sequence), 0, 0, 0); + updateLine(view); + } + break; } - } - // See if it's ordinary keys. - // NOTE - with vi style ordinary keys can be commands, - // but they would be found by the command check above first. - if (!isTranslated) - { - // TODO - Should check for tabs to, and insert them. - // Though better off having a function for that? - mooshStrings(view->line, sequence, view->iX, 0, !overWriteMode); - view->oW = formatLine(view, view->line->line, &(view->output)); - moveCursorRelative(view, strlen(sequence), 0, 0, 0); - updateLine(view); + default : break; } // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours. @@ -2481,7 +2491,7 @@ void boxes_main(void) updateLine(currentBox->view); // Run the main loop. - handle_keys((long) currentBox->view, handleKeySequence, handleCSI); + handle_keys((long) currentBox->view, handleEvent); // TODO - Should remember to turn off mouse reporting when we leave. diff --git a/dumbsh.c b/dumbsh.c index 058b03a..92ac46a 100644 --- a/dumbsh.c +++ b/dumbsh.c @@ -48,34 +48,6 @@ static void updateLine() fflush(stdout); } -// Callback for incoming CSI commands from the terminal. -static void handleCSI(long extra, char *command, int *params, int count) -{ - // Is it a cursor location report? - if (strcmp("R", command) == 0) - { - // Parameters are cursor line and column. - // NOTE - This may be sent at other times, not just during terminal resize. - // We are assuming here that it's a resize. - // The defaults are 1, which get ignored by the heuristic below. - int r = params[0], c = params[1]; - - // Check it's not an F3 key variation, coz some of them use - // the same CSI function command. - // This is a heuristic, we are checking against an unusable terminal size. - if ((2 == count) && (8 < r) && (8 < c)) - { - TT.h = r; - TT.w = c; - updateLine(); - } - } - // NOTE - The CSI differs from the sequence callback - // in not having to return anything. CSI sequences include a - // definite terminating byte, so no need for this callback - // to tell handle_keys to keep accumulating. -} - // The various commands. static void deleteChar() { @@ -184,43 +156,76 @@ static struct keyCommand simpleEmacsKeys[] = {"^P", prevHistory} }; -// Callback for incoming key sequences from the user. -static int handleKeySequence(long extra, char *sequence, int isTranslated) +// Callback for incoming sequences from the terminal. +static int handleEvent(long extra, struct keyevent *event) { - int j, l = strlen(sequence); - - // Search for a key sequence bound to a command. - for (j = 0; j < (sizeof(simpleEmacsKeys) / sizeof(*simpleEmacsKeys)); j++) + switch (event->type) { - if (strncmp(simpleEmacsKeys[j].key, sequence, l) == 0) + case HK_KEYS : { - // If it's a partial match, keep accumulating them. - if (strlen(simpleEmacsKeys[j].key) != l) - return 0; - else + int j, l = strlen(event->sequence); + + // Search for a key sequence bound to a command. + for (j = 0; j < ARRAY_LEN(simpleEmacsKeys); j++) { - if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler(); - return 1; + if (strncmp(simpleEmacsKeys[j].key, event->sequence, l) == 0) + { + // If it's a partial match, keep accumulating them. + if (strlen(simpleEmacsKeys[j].key) != l) + return 0; + else + { + if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler(); + return 1; + } + } } + + // See if it's ordinary keys. + // NOTE - with vi style ordinary keys can be commands, + // but they would be found by the command check above first. + if (!event->isTranslated) + { + if (TT.x < sizeof(toybuf)) + { + int j, l = strlen(event->sequence); + + for (j = strlen(toybuf); j >= TT.x; j--) + toybuf[j + l] = toybuf[j]; + for (j = 0; j < l; j++) + toybuf[TT.x + j] = event->sequence[j]; + TT.x += l; + updateLine(); + } + } + break; } - } - // See if it's ordinary keys. - // NOTE - with vi style ordinary keys can be commands, - // but they would be found by the command check above first. - if (!isTranslated) - { - if (TT.x < sizeof(toybuf)) + case HK_CSI : { - int j, l = strlen(sequence); - - for (j = strlen(toybuf); j >= TT.x; j--) - toybuf[j + l] = toybuf[j]; - for (j = 0; j < l; j++) - toybuf[TT.x + j] = sequence[j]; - TT.x += l; - updateLine(); + // Is it a cursor location report? + if (strcmp("R", event->sequence) == 0) + { + // Parameters are cursor line and column. + // NOTE - This may be sent at other times, not just during terminal resize. + // We are assuming here that it's a resize. + // The defaults are 1, which get ignored by the heuristic below. + int r = event->params[0], c = event->params[1]; + + // Check it's not an F3 key variation, coz some of them use + // the same CSI function command. + // This is a heuristic, we are checking against an unusable terminal size. + if ((2 == event->count) && (8 < r) && (8 < c)) + { + TT.h = r; + TT.w = c; + updateLine(); + } + break; + } } + + default : break; } // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours. @@ -269,7 +274,7 @@ void dumbsh_main(void) // Let's rock! updateLine(); - handle_keys(0, handleKeySequence, handleCSI); + handle_keys(0, handleEvent); // Clean up. tcsetattr(0, TCSANOW, &oldTermIo); diff --git a/handlekeys.c b/handlekeys.c index df18088..7501dbd 100644 --- a/handlekeys.c +++ b/handlekeys.c @@ -176,10 +176,9 @@ static void handleSIGWINCH(int signalNumber) sigWinch = 1; } -void handle_keys(long extra, - int (*handle_sequence)(long extra, char *sequence, int isTranslated), - void (*handle_CSI)(long extra, char *command, int *params, int count)) +void handle_keys(long extra, int (*handle_event)(long extra, struct keyevent *event)) { + struct keyevent event; fd_set selectFds; struct timespec timeOut; struct sigaction sigAction, oldSigAction; @@ -261,6 +260,12 @@ void handle_keys(long extra, else { buffIndex += j; + // Send raw keystrokes, mostly for things like showkey. + event.type = HK_RAW; + event.sequence = buffer; + event.isTranslated = 0; + handle_event(extra, &event); + if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer. { fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence); @@ -331,9 +336,18 @@ void handle_keys(long extra, if ('M' == buffer[1]) { - // TODO - We have a mouse report, which is CSI M ..., where the rest is + // We have a mouse report, which is CSI M ..., where the rest is // binary encoded, more or less. Not fitting into the CSI format. // To make things worse, can't tell how long this will be. + // So leave it up to the caller to tell us if they used it. + event.type = HK_MOUSE; + event.sequence = buffer; + event.isTranslated = 0; + if (handle_event(extra, &event)) + { + buffer[0] = buffIndex = 0; + sequence[0] = 0; + } } else { @@ -389,8 +403,12 @@ void handle_keys(long extra, t = csFinal + strlen(csFinal) - 1; if (('\x40' <= (*t)) && ((*t) <= '\x7e')) { - if (handle_CSI) - handle_CSI(extra, csFinal, csParams, p); + event.type = HK_CSI; + event.sequence = csFinal; + event.isTranslated = 1; + event.count = p; + event.params = csParams; + handle_event(extra, &event); buffer[0] = buffIndex = 0; sequence[0] = 0; } @@ -398,12 +416,15 @@ void handle_keys(long extra, } // Pass the result to the callback. - if ((handle_sequence) && (sequence[0] || buffer[0])) + if (sequence[0] || buffer[0]) { char b[strlen(sequence) + strlen(buffer) + 1]; sprintf(b, "%s%s", sequence, buffer); - if (handle_sequence(extra, b, (0 != sequence[0]))) + event.type = HK_KEYS; + event.sequence = b; + event.isTranslated = (0 != sequence[0]); + if (handle_event(extra, &event)) { buffer[0] = buffIndex = 0; sequence[0] = 0; diff --git a/handlekeys.h b/handlekeys.h index 9678131..868183f 100644 --- a/handlekeys.h +++ b/handlekeys.h @@ -3,6 +3,21 @@ * Copyright 2012 David Seikel */ +enum keyeventtype{ + HK_CSI, + HK_KEYS, + HK_MOUSE, + HK_RAW +}; + +struct keyevent { + enum keyeventtype type; // The type of this event. + char *sequence; // Either a translated sequence, or raw bytes. + int isTranslated; // Whether or not sequence is translated. + int count; // Number of entries in params. + int *params; // For CSI events, the decoded parameters. +}; + /* An input loop that handles keystrokes and terminal CSI commands. * * Reads stdin, trying to translate raw keystrokes into something more readable. @@ -17,42 +32,44 @@ * * handle_keys also sets up a SIGWINCH handler to catch terminal resizes, * and sends a request to the terminal to report it's current size when it gets - * a SIGWINCH. This is the main reason for handle_CSI, as those reports are + * a SIGWINCH. This is the main reason for HK_CSI, as those reports are * sent as CSI. It's still up to the user code to recognise and deal with the * terminal resize response, but at least it's nicely decoded for you. * * Arguments - * extra - arbitrary data that gets passed back to the callbacks. - * handle_sequence - a callback to handle keystroke sequences. - * handle_CSI - a callback to handle terminal CSI commands. + * handle_event - a callback to handle sequences. + * + * handle_event is called when a complete sequence has been accumulated. It is + * passed a keyevent structure. The type member of that structure determines + * what sort of event this is. What's in the rest of the keyevent depends on + * the type - * - * handle_sequence is called when a complete keystroke sequence has been - * accumulated. The sequence argument holds the accumulated keystrokes. - * The translated argument flags if any have been translated, otherwise you - * can assume it's all ordinary characters. + * HK_CSI + * sequence is the fully decoded CSI command, including the private and intermediate characters. + * isTranslated is 1, since the CSI command has been translated. + * count is the count of translated CSI parameters. + * params is an array of translateted CSI parameters. + * Empty parameters are set to -1, coz -1 parameters are not legal, + * and empty ones should default to something that is command dependant. * - * handle_keys should return 1 if the sequence has been dealt with, or ignored. - * It should return 0, if handle_keys should keep adding more + * HK_KEYS + * sequence the keystrokes as ASCII, either translated or not. + * isTranslated if 0, then sequence is ordinary keys, otherwise + * sequence is the names of keys, from the keys[] array. + * count and params are not used. + * + * For HK_KEYS handle_event should return 1 if the sequence has been dealt with, + * or ignored. It should return 0, if handle_keys should keep adding more * translated keystroke sequences on the end, and try again later. * 0 should really only be used if it's a partial match, and we need more * keys in the sequence to make a full match. * - * handle_CSI is called when a complete terminal CSI command has been - * detected. The command argument is the full CSI command code, including - * private and intermediate characters. The params argument is the decoded - * parameters from the command. The count argument is the number of decoded - * parameters. Empty parameters are set to -1, coz -1 parameters are not legal, - * and empty ones should default to something that is command dependant. - * - * NOTE - handle_CSI differs from handle_sequence in not having to - * return anything. CSI sequences include a definite terminating byte, - * so no need for this callback to tell handle_keys to keep accumulating. - * Some applications use a series of keystrokes for things, so they - * get accumulated until fully recognised by the user code. + * HK_MOUSE + * sequence is the raw bytes of the mouse report. The rest are not used. + * */ -void handle_keys(long extra, - int (*handle_sequence)(long extra, char *sequence, int isTranslated), - void (*handle_CSI)(long extra, char *command, int *params, int count)); +void handle_keys(long extra, int (*handle_event)(long extra, struct keyevent *event)); /* Call this when you want handle_keys to return. */ diff --git a/showkey.c b/showkey.c index 2f5947b..47a1a3d 100644 --- a/showkey.c +++ b/showkey.c @@ -34,24 +34,6 @@ GLOBALS( #define TT this.showkey -// Callback for incoming CSI commands from the terminal. -static void handleCSI(long extra, char *command, int *params, int count) -{ - int i; - - // Is it a cursor location report? - if (strcmp("R", command) == 0) - { - printf("CSI cursor position - line %d, column %d\r\n", params[0], params[1]); - return; - } - - printf("CSI command %s - ", command); - for (i = 0; i < count; i++) - printf("%d ", params[i]); - printf("\r\n"); -} - static void quit() { printf("Quitting.\r\n"); @@ -64,31 +46,70 @@ static struct keyCommand simpleKeys[] = {"^C", quit} }; -// Callback for incoming key sequences from the user. -static int handleKeySequence(long extra, char *sequence, int isTranslated) +// Callback for incoming sequences from the terminal. +static int handleEvent(long extra, struct keyevent *event) { - int j, l = strlen(sequence); - - if (isTranslated) - printf("TRANSLATED - "); - else - printf("KEY - "); - printf("%s\r\n", sequence); + int i; - // Search for a key sequence bound to a command. - for (j = 0; j < (sizeof(simpleKeys) / sizeof(*simpleKeys)); j++) + switch (event->type) { - if (strncmp(simpleKeys[j].key, sequence, l) == 0) + case HK_RAW : + { + printf("RAW "); + for (i = 0; event->sequence[i]; i++) + { + printf("(%x) ", (int) event->sequence[i]); + if (32 > event->sequence[i]) + printf("^%c, ", (int) event->sequence[i] + 'A' - 1); + else + printf("%c, ", (int) event->sequence[i]); + } + printf("-> "); + break; + } + + case HK_KEYS : { - // If it's a partial match, keep accumulating them. - if (strlen(simpleKeys[j].key) != l) - return 0; + int l = strlen(event->sequence); + + if (event->isTranslated) + printf("TRANSLATED - "); else + printf("KEY - "); + printf("%s\r\n", event->sequence); + + // Search for a key sequence bound to a command. + for (i = 0; i < ARRAY_LEN(simpleKeys); i++) + { + if (strncmp(simpleKeys[i].key, event->sequence, l) == 0) + { + // If it's a partial match, keep accumulating them. + if (strlen(simpleKeys[i].key) != l) + return 0; + else + if (simpleKeys[i].handler) simpleKeys[i].handler(); + } + } + break; + } + + case HK_CSI : + { + // Is it a cursor location report? + if (strcmp("R", event->sequence) == 0) { - if (simpleKeys[j].handler) simpleKeys[j].handler(); + printf("CSI cursor position - line %d, column %d\r\n", event->params[0], event->params[1]); return 1; } + + printf("CSI command %s - ", event->sequence); + for (i = 0; i < event->count; i++) + printf("%d ", event->params[i]); + printf("\r\n"); + break; } + + default : break; } return 1; @@ -117,7 +138,7 @@ void showkey_main(void) termIo.c_cc[VMIN]=1; tcsetattr(0, TCSANOW, &termIo); - handle_keys(0, handleKeySequence, handleCSI); + handle_keys(0, handleEvent); tcsetattr(0, TCSANOW, &oldTermIo); puts(""); -- cgit v1.1