aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--boxes.c94
-rw-r--r--dumbsh.c119
-rw-r--r--handlekeys.c37
-rw-r--r--handlekeys.h65
-rw-r--r--showkey.c91
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[] =
1651 {"R", termSize} // Parameters are cursor line and column. Note this may be sent at other times, not just during terminal resize. 1651 {"R", termSize} // Parameters are cursor line and column. Note this may be sent at other times, not just during terminal resize.
1652}; 1652};
1653 1653
1654// Callback for incoming CSI commands from the terminal.
1655static void handleCSI(long extra, char *command, int *params, int count)
1656{
1657 int j;
1658 1654
1659 for (j = 0; j < (sizeof(CSIcommands) / sizeof(*CSIcommands)); j++) 1655// Callback for incoming sequences from the terminal.
1656static int handleEvent(long extra, struct keyevent *event)
1657{
1658 switch (event->type)
1660 { 1659 {
1661 if (strcmp(CSIcommands[j].code, command) == 0) 1660 case HK_CSI :
1662 { 1661 {
1663 CSIcommands[j].func(extra, params, count); 1662 int j;
1663
1664 for (j = 0; j < ARRAY_LEN(CSIcommands); j++)
1665 {
1666 if (strcmp(CSIcommands[j].code, event->sequence) == 0)
1667 {
1668 CSIcommands[j].func(extra, event->params, event->count);
1669 break;
1670 }
1671 }
1664 break; 1672 break;
1665 } 1673 }
1666 }
1667}
1668 1674
1669// Callback for incoming key sequences from the user. 1675 case HK_KEYS :
1670static int handleKeySequence(long extra, char *sequence, int isTranslated) 1676 {
1671{ 1677 struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away.
1672 struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away. 1678 struct keyCommand *commands = currentBox->view->content->context->modes[currentBox->view->mode].keys;
1673 struct keyCommand *commands = currentBox->view->content->context->modes[currentBox->view->mode].keys; 1679 int j, l = strlen(event->sequence);
1674 int j, l = strlen(sequence);
1675 1680
1676 // Coz things might change out from under us, find the current view. 1681 // Coz things might change out from under us, find the current view.
1677 if (commandMode) view = commandLine; 1682 if (commandMode) view = commandLine;
1678 else view = currentBox->view; 1683 else view = currentBox->view;
1679 1684
1680 // Search for a key sequence bound to a command. 1685 // Search for a key sequence bound to a command.
1681 for (j = 0; commands[j].key; j++) 1686 for (j = 0; commands[j].key; j++)
1682 {
1683 if (strncmp(commands[j].key, sequence, l) == 0)
1684 {
1685 // If it's a partial match, keep accumulating them.
1686 if (strlen(commands[j].key) != l)
1687 return 0;
1688 else
1689 { 1687 {
1690 doCommand(view, commands[j].command); 1688 if (strncmp(commands[j].key, event->sequence, l) == 0)
1691 return 1; 1689 {
1690 // If it's a partial match, keep accumulating them.
1691 if (strlen(commands[j].key) != l)
1692 return 0;
1693 else
1694 {
1695 doCommand(view, commands[j].command);
1696 return 1;
1697 }
1698 }
1692 } 1699 }
1700
1701 // See if it's ordinary keys.
1702 // NOTE - with vi style ordinary keys can be commands,
1703 // but they would be found by the command check above first.
1704 if (!event->isTranslated)
1705 {
1706 // TODO - Should check for tabs to, and insert them.
1707 // Though better off having a function for that?
1708 mooshStrings(view->line, event->sequence, view->iX, 0, !overWriteMode);
1709 view->oW = formatLine(view, view->line->line, &(view->output));
1710 moveCursorRelative(view, strlen(event->sequence), 0, 0, 0);
1711 updateLine(view);
1712 }
1713 break;
1693 } 1714 }
1694 }
1695 1715
1696 // See if it's ordinary keys. 1716 default : break;
1697 // NOTE - with vi style ordinary keys can be commands,
1698 // but they would be found by the command check above first.
1699 if (!isTranslated)
1700 {
1701 // TODO - Should check for tabs to, and insert them.
1702 // Though better off having a function for that?
1703 mooshStrings(view->line, sequence, view->iX, 0, !overWriteMode);
1704 view->oW = formatLine(view, view->line->line, &(view->output));
1705 moveCursorRelative(view, strlen(sequence), 0, 0, 0);
1706 updateLine(view);
1707 } 1717 }
1708 1718
1709 // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours. 1719 // 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)
2481 updateLine(currentBox->view); 2491 updateLine(currentBox->view);
2482 2492
2483 // Run the main loop. 2493 // Run the main loop.
2484 handle_keys((long) currentBox->view, handleKeySequence, handleCSI); 2494 handle_keys((long) currentBox->view, handleEvent);
2485 2495
2486 // TODO - Should remember to turn off mouse reporting when we leave. 2496 // TODO - Should remember to turn off mouse reporting when we leave.
2487 2497
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()
48 fflush(stdout); 48 fflush(stdout);
49} 49}
50 50
51// Callback for incoming CSI commands from the terminal.
52static void handleCSI(long extra, char *command, int *params, int count)
53{
54 // Is it a cursor location report?
55 if (strcmp("R", command) == 0)
56 {
57 // Parameters are cursor line and column.
58 // NOTE - This may be sent at other times, not just during terminal resize.
59 // We are assuming here that it's a resize.
60 // The defaults are 1, which get ignored by the heuristic below.
61 int r = params[0], c = params[1];
62
63 // Check it's not an F3 key variation, coz some of them use
64 // the same CSI function command.
65 // This is a heuristic, we are checking against an unusable terminal size.
66 if ((2 == count) && (8 < r) && (8 < c))
67 {
68 TT.h = r;
69 TT.w = c;
70 updateLine();
71 }
72 }
73 // NOTE - The CSI differs from the sequence callback
74 // in not having to return anything. CSI sequences include a
75 // definite terminating byte, so no need for this callback
76 // to tell handle_keys to keep accumulating.
77}
78
79// The various commands. 51// The various commands.
80static void deleteChar() 52static void deleteChar()
81{ 53{
@@ -184,43 +156,76 @@ static struct keyCommand simpleEmacsKeys[] =
184 {"^P", prevHistory} 156 {"^P", prevHistory}
185}; 157};
186 158
187// Callback for incoming key sequences from the user. 159// Callback for incoming sequences from the terminal.
188static int handleKeySequence(long extra, char *sequence, int isTranslated) 160static int handleEvent(long extra, struct keyevent *event)
189{ 161{
190 int j, l = strlen(sequence); 162 switch (event->type)
191
192 // Search for a key sequence bound to a command.
193 for (j = 0; j < (sizeof(simpleEmacsKeys) / sizeof(*simpleEmacsKeys)); j++)
194 { 163 {
195 if (strncmp(simpleEmacsKeys[j].key, sequence, l) == 0) 164 case HK_KEYS :
196 { 165 {
197 // If it's a partial match, keep accumulating them. 166 int j, l = strlen(event->sequence);
198 if (strlen(simpleEmacsKeys[j].key) != l) 167
199 return 0; 168 // Search for a key sequence bound to a command.
200 else 169 for (j = 0; j < ARRAY_LEN(simpleEmacsKeys); j++)
201 { 170 {
202 if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler(); 171 if (strncmp(simpleEmacsKeys[j].key, event->sequence, l) == 0)
203 return 1; 172 {
173 // If it's a partial match, keep accumulating them.
174 if (strlen(simpleEmacsKeys[j].key) != l)
175 return 0;
176 else
177 {
178 if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler();
179 return 1;
180 }
181 }
204 } 182 }
183
184 // See if it's ordinary keys.
185 // NOTE - with vi style ordinary keys can be commands,
186 // but they would be found by the command check above first.
187 if (!event->isTranslated)
188 {
189 if (TT.x < sizeof(toybuf))
190 {
191 int j, l = strlen(event->sequence);
192
193 for (j = strlen(toybuf); j >= TT.x; j--)
194 toybuf[j + l] = toybuf[j];
195 for (j = 0; j < l; j++)
196 toybuf[TT.x + j] = event->sequence[j];
197 TT.x += l;
198 updateLine();
199 }
200 }
201 break;
205 } 202 }
206 }
207 203
208 // See if it's ordinary keys. 204 case HK_CSI :
209 // NOTE - with vi style ordinary keys can be commands,
210 // but they would be found by the command check above first.
211 if (!isTranslated)
212 {
213 if (TT.x < sizeof(toybuf))
214 { 205 {
215 int j, l = strlen(sequence); 206 // Is it a cursor location report?
216 207 if (strcmp("R", event->sequence) == 0)
217 for (j = strlen(toybuf); j >= TT.x; j--) 208 {
218 toybuf[j + l] = toybuf[j]; 209 // Parameters are cursor line and column.
219 for (j = 0; j < l; j++) 210 // NOTE - This may be sent at other times, not just during terminal resize.
220 toybuf[TT.x + j] = sequence[j]; 211 // We are assuming here that it's a resize.
221 TT.x += l; 212 // The defaults are 1, which get ignored by the heuristic below.
222 updateLine(); 213 int r = event->params[0], c = event->params[1];
214
215 // Check it's not an F3 key variation, coz some of them use
216 // the same CSI function command.
217 // This is a heuristic, we are checking against an unusable terminal size.
218 if ((2 == event->count) && (8 < r) && (8 < c))
219 {
220 TT.h = r;
221 TT.w = c;
222 updateLine();
223 }
224 break;
225 }
223 } 226 }
227
228 default : break;
224 } 229 }
225 230
226 // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours. 231 // 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)
269 274
270 // Let's rock! 275 // Let's rock!
271 updateLine(); 276 updateLine();
272 handle_keys(0, handleKeySequence, handleCSI); 277 handle_keys(0, handleEvent);
273 278
274 // Clean up. 279 // Clean up.
275 tcsetattr(0, TCSANOW, &oldTermIo); 280 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)
176 sigWinch = 1; 176 sigWinch = 1;
177} 177}
178 178
179void handle_keys(long extra, 179void handle_keys(long extra, int (*handle_event)(long extra, struct keyevent *event))
180 int (*handle_sequence)(long extra, char *sequence, int isTranslated),
181 void (*handle_CSI)(long extra, char *command, int *params, int count))
182{ 180{
181 struct keyevent event;
183 fd_set selectFds; 182 fd_set selectFds;
184 struct timespec timeOut; 183 struct timespec timeOut;
185 struct sigaction sigAction, oldSigAction; 184 struct sigaction sigAction, oldSigAction;
@@ -261,6 +260,12 @@ void handle_keys(long extra,
261 else 260 else
262 { 261 {
263 buffIndex += j; 262 buffIndex += j;
263 // Send raw keystrokes, mostly for things like showkey.
264 event.type = HK_RAW;
265 event.sequence = buffer;
266 event.isTranslated = 0;
267 handle_event(extra, &event);
268
264 if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer. 269 if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer.
265 { 270 {
266 fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence); 271 fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence);
@@ -331,9 +336,18 @@ void handle_keys(long extra,
331 336
332 if ('M' == buffer[1]) 337 if ('M' == buffer[1])
333 { 338 {
334 // TODO - We have a mouse report, which is CSI M ..., where the rest is 339 // We have a mouse report, which is CSI M ..., where the rest is
335 // binary encoded, more or less. Not fitting into the CSI format. 340 // binary encoded, more or less. Not fitting into the CSI format.
336 // To make things worse, can't tell how long this will be. 341 // To make things worse, can't tell how long this will be.
342 // So leave it up to the caller to tell us if they used it.
343 event.type = HK_MOUSE;
344 event.sequence = buffer;
345 event.isTranslated = 0;
346 if (handle_event(extra, &event))
347 {
348 buffer[0] = buffIndex = 0;
349 sequence[0] = 0;
350 }
337 } 351 }
338 else 352 else
339 { 353 {
@@ -389,8 +403,12 @@ void handle_keys(long extra,
389 t = csFinal + strlen(csFinal) - 1; 403 t = csFinal + strlen(csFinal) - 1;
390 if (('\x40' <= (*t)) && ((*t) <= '\x7e')) 404 if (('\x40' <= (*t)) && ((*t) <= '\x7e'))
391 { 405 {
392 if (handle_CSI) 406 event.type = HK_CSI;
393 handle_CSI(extra, csFinal, csParams, p); 407 event.sequence = csFinal;
408 event.isTranslated = 1;
409 event.count = p;
410 event.params = csParams;
411 handle_event(extra, &event);
394 buffer[0] = buffIndex = 0; 412 buffer[0] = buffIndex = 0;
395 sequence[0] = 0; 413 sequence[0] = 0;
396 } 414 }
@@ -398,12 +416,15 @@ void handle_keys(long extra,
398 } 416 }
399 417
400 // Pass the result to the callback. 418 // Pass the result to the callback.
401 if ((handle_sequence) && (sequence[0] || buffer[0])) 419 if (sequence[0] || buffer[0])
402 { 420 {
403 char b[strlen(sequence) + strlen(buffer) + 1]; 421 char b[strlen(sequence) + strlen(buffer) + 1];
404 422
405 sprintf(b, "%s%s", sequence, buffer); 423 sprintf(b, "%s%s", sequence, buffer);
406 if (handle_sequence(extra, b, (0 != sequence[0]))) 424 event.type = HK_KEYS;
425 event.sequence = b;
426 event.isTranslated = (0 != sequence[0]);
427 if (handle_event(extra, &event))
407 { 428 {
408 buffer[0] = buffIndex = 0; 429 buffer[0] = buffIndex = 0;
409 sequence[0] = 0; 430 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 @@
3 * Copyright 2012 David Seikel <won_fang@yahoo.com.au> 3 * Copyright 2012 David Seikel <won_fang@yahoo.com.au>
4 */ 4 */
5 5
6enum keyeventtype{
7 HK_CSI,
8 HK_KEYS,
9 HK_MOUSE,
10 HK_RAW
11};
12
13struct keyevent {
14 enum keyeventtype type; // The type of this event.
15 char *sequence; // Either a translated sequence, or raw bytes.
16 int isTranslated; // Whether or not sequence is translated.
17 int count; // Number of entries in params.
18 int *params; // For CSI events, the decoded parameters.
19};
20
6/* An input loop that handles keystrokes and terminal CSI commands. 21/* An input loop that handles keystrokes and terminal CSI commands.
7 * 22 *
8 * Reads stdin, trying to translate raw keystrokes into something more readable. 23 * Reads stdin, trying to translate raw keystrokes into something more readable.
@@ -17,42 +32,44 @@
17 * 32 *
18 * handle_keys also sets up a SIGWINCH handler to catch terminal resizes, 33 * handle_keys also sets up a SIGWINCH handler to catch terminal resizes,
19 * and sends a request to the terminal to report it's current size when it gets 34 * and sends a request to the terminal to report it's current size when it gets
20 * a SIGWINCH. This is the main reason for handle_CSI, as those reports are 35 * a SIGWINCH. This is the main reason for HK_CSI, as those reports are
21 * sent as CSI. It's still up to the user code to recognise and deal with the 36 * sent as CSI. It's still up to the user code to recognise and deal with the
22 * terminal resize response, but at least it's nicely decoded for you. 37 * terminal resize response, but at least it's nicely decoded for you.
23 * 38 *
24 * Arguments - 39 * Arguments -
25 * extra - arbitrary data that gets passed back to the callbacks. 40 * extra - arbitrary data that gets passed back to the callbacks.
26 * handle_sequence - a callback to handle keystroke sequences. 41 * handle_event - a callback to handle sequences.
27 * handle_CSI - a callback to handle terminal CSI commands. 42 *
43 * handle_event is called when a complete sequence has been accumulated. It is
44 * passed a keyevent structure. The type member of that structure determines
45 * what sort of event this is. What's in the rest of the keyevent depends on
46 * the type -
28 * 47 *
29 * handle_sequence is called when a complete keystroke sequence has been 48 * HK_CSI
30 * accumulated. The sequence argument holds the accumulated keystrokes. 49 * sequence is the fully decoded CSI command, including the private and intermediate characters.
31 * The translated argument flags if any have been translated, otherwise you 50 * isTranslated is 1, since the CSI command has been translated.
32 * can assume it's all ordinary characters. 51 * count is the count of translated CSI parameters.
52 * params is an array of translateted CSI parameters.
53 * Empty parameters are set to -1, coz -1 parameters are not legal,
54 * and empty ones should default to something that is command dependant.
33 * 55 *
34 * handle_keys should return 1 if the sequence has been dealt with, or ignored. 56 * HK_KEYS
35 * It should return 0, if handle_keys should keep adding more 57 * sequence the keystrokes as ASCII, either translated or not.
58 * isTranslated if 0, then sequence is ordinary keys, otherwise
59 * sequence is the names of keys, from the keys[] array.
60 * count and params are not used.
61 *
62 * For HK_KEYS handle_event should return 1 if the sequence has been dealt with,
63 * or ignored. It should return 0, if handle_keys should keep adding more
36 * translated keystroke sequences on the end, and try again later. 64 * translated keystroke sequences on the end, and try again later.
37 * 0 should really only be used if it's a partial match, and we need more 65 * 0 should really only be used if it's a partial match, and we need more
38 * keys in the sequence to make a full match. 66 * keys in the sequence to make a full match.
39 * 67 *
40 * handle_CSI is called when a complete terminal CSI command has been 68 * HK_MOUSE
41 * detected. The command argument is the full CSI command code, including 69 * sequence is the raw bytes of the mouse report. The rest are not used.
42 * private and intermediate characters. The params argument is the decoded 70 *
43 * parameters from the command. The count argument is the number of decoded
44 * parameters. Empty parameters are set to -1, coz -1 parameters are not legal,
45 * and empty ones should default to something that is command dependant.
46 *
47 * NOTE - handle_CSI differs from handle_sequence in not having to
48 * return anything. CSI sequences include a definite terminating byte,
49 * so no need for this callback to tell handle_keys to keep accumulating.
50 * Some applications use a series of keystrokes for things, so they
51 * get accumulated until fully recognised by the user code.
52 */ 71 */
53void handle_keys(long extra, 72void handle_keys(long extra, int (*handle_event)(long extra, struct keyevent *event));
54 int (*handle_sequence)(long extra, char *sequence, int isTranslated),
55 void (*handle_CSI)(long extra, char *command, int *params, int count));
56 73
57 74
58/* Call this when you want handle_keys to return. */ 75/* 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(
34#define TT this.showkey 34#define TT this.showkey
35 35
36 36
37// Callback for incoming CSI commands from the terminal.
38static void handleCSI(long extra, char *command, int *params, int count)
39{
40 int i;
41
42 // Is it a cursor location report?
43 if (strcmp("R", command) == 0)
44 {
45 printf("CSI cursor position - line %d, column %d\r\n", params[0], params[1]);
46 return;
47 }
48
49 printf("CSI command %s - ", command);
50 for (i = 0; i < count; i++)
51 printf("%d ", params[i]);
52 printf("\r\n");
53}
54
55static void quit() 37static void quit()
56{ 38{
57 printf("Quitting.\r\n"); 39 printf("Quitting.\r\n");
@@ -64,31 +46,70 @@ static struct keyCommand simpleKeys[] =
64 {"^C", quit} 46 {"^C", quit}
65}; 47};
66 48
67// Callback for incoming key sequences from the user. 49// Callback for incoming sequences from the terminal.
68static int handleKeySequence(long extra, char *sequence, int isTranslated) 50static int handleEvent(long extra, struct keyevent *event)
69{ 51{
70 int j, l = strlen(sequence); 52 int i;
71
72 if (isTranslated)
73 printf("TRANSLATED - ");
74 else
75 printf("KEY - ");
76 printf("%s\r\n", sequence);
77 53
78 // Search for a key sequence bound to a command. 54 switch (event->type)
79 for (j = 0; j < (sizeof(simpleKeys) / sizeof(*simpleKeys)); j++)
80 { 55 {
81 if (strncmp(simpleKeys[j].key, sequence, l) == 0) 56 case HK_RAW :
57 {
58 printf("RAW ");
59 for (i = 0; event->sequence[i]; i++)
60 {
61 printf("(%x) ", (int) event->sequence[i]);
62 if (32 > event->sequence[i])
63 printf("^%c, ", (int) event->sequence[i] + 'A' - 1);
64 else
65 printf("%c, ", (int) event->sequence[i]);
66 }
67 printf("-> ");
68 break;
69 }
70
71 case HK_KEYS :
82 { 72 {
83 // If it's a partial match, keep accumulating them. 73 int l = strlen(event->sequence);
84 if (strlen(simpleKeys[j].key) != l) 74
85 return 0; 75 if (event->isTranslated)
76 printf("TRANSLATED - ");
86 else 77 else
78 printf("KEY - ");
79 printf("%s\r\n", event->sequence);
80
81 // Search for a key sequence bound to a command.
82 for (i = 0; i < ARRAY_LEN(simpleKeys); i++)
83 {
84 if (strncmp(simpleKeys[i].key, event->sequence, l) == 0)
85 {
86 // If it's a partial match, keep accumulating them.
87 if (strlen(simpleKeys[i].key) != l)
88 return 0;
89 else
90 if (simpleKeys[i].handler) simpleKeys[i].handler();
91 }
92 }
93 break;
94 }
95
96 case HK_CSI :
97 {
98 // Is it a cursor location report?
99 if (strcmp("R", event->sequence) == 0)
87 { 100 {
88 if (simpleKeys[j].handler) simpleKeys[j].handler(); 101 printf("CSI cursor position - line %d, column %d\r\n", event->params[0], event->params[1]);
89 return 1; 102 return 1;
90 } 103 }
104
105 printf("CSI command %s - ", event->sequence);
106 for (i = 0; i < event->count; i++)
107 printf("%d ", event->params[i]);
108 printf("\r\n");
109 break;
91 } 110 }
111
112 default : break;
92 } 113 }
93 114
94 return 1; 115 return 1;
@@ -117,7 +138,7 @@ void showkey_main(void)
117 termIo.c_cc[VMIN]=1; 138 termIo.c_cc[VMIN]=1;
118 tcsetattr(0, TCSANOW, &termIo); 139 tcsetattr(0, TCSANOW, &termIo);
119 140
120 handle_keys(0, handleKeySequence, handleCSI); 141 handle_keys(0, handleEvent);
121 142
122 tcsetattr(0, TCSANOW, &oldTermIo); 143 tcsetattr(0, TCSANOW, &oldTermIo);
123 puts(""); 144 puts("");