jcs
/subtext
/amendments
/81
session: Handle arrow keys in session_field_input
Handle backspace and character insertion when cursor is not at the end
of a field.
Have session_input_char return a short so we can return custom codes
> 255 for keys or other events.
jcs made amendment 81 over 2 years ago
--- session.c Fri Feb 18 15:00:49 2022
+++ session.c Mon Feb 21 15:20:58 2022
@@ -72,7 +72,8 @@ session_run(struct uthread *uthread, void *arg)
Handle h;
char date[9];
struct tm *date_tm;
- unsigned char c, done = 0;
+ unsigned short c;
+ bool done = false;
size_t len;
/* until we negotiate otherwise */
@@ -127,7 +128,7 @@ get_another_char:
c = session_input_char(s);
if (s->ending)
break;
- if (c == '\r' || c == 0)
+ if (c == '\r' || c == 0 || c > 255)
goto get_another_char;
session_printf(s, "%c\r\n", c);
@@ -145,7 +146,7 @@ get_another_char:
/* goodbye */
session_output_string(s, "Goodbye!\r\n");
session_flush(s);
- done = 1;
+ done = true;
break;
case 'm':
case 'M':
@@ -322,30 +323,70 @@ session_output_template(struct session *session, const
return size;
}
-char
+unsigned short
session_input_char(struct session *session)
{
- unsigned char ret;
+ unsigned short ret;
+ short waiting_for = 1;
+ short consumed = 0;
wait_for_char:
- while (session->ibuflen == 0) {
+ while (session->ibuflen < waiting_for) {
session->node_funcs->input(session);
if (session->ending)
return 0;
if (session->abort_input)
return 0;
- if (session->ibuflen != 0)
+ if (session->ibuflen >= waiting_for)
break;
if (session->obuflen != 0 && session->chatting)
return 0;
uthread_yield();
}
+ if (session->ibuf[0] == '\33') {
+ /* look for a VT100 escape sequence */
+ if (session->ibuflen == 1) {
+ waiting_for = 2;
+ goto wait_for_char;
+ } else if (session->ibuf[1] == '[') {
+ if (session->ibuflen == 2) {
+ waiting_for = 3;
+ goto wait_for_char;
+ } else {
+ consumed = 3;
+ switch (session->ibuf[2]) {
+ case 'A':
+ ret = KEY_UP;
+ break;
+ case 'B':
+ ret = KEY_DOWN;
+ break;
+ case 'C':
+ ret = KEY_RIGHT;
+ break;
+ case 'D':
+ ret = KEY_LEFT;
+ break;
+ default:
+ /* probably not good to pass it through as-is */
+ ret = KEY_OTHER;
+ }
+
+ goto done_consuming;
+ }
+ }
+ }
+
ret = session->ibuf[0];
- if (session->ibuflen == 1)
+ consumed = 1;
+
+done_consuming:
+ if (session->ibuflen == consumed)
session->ibuflen = 0;
else {
- memmove(session->ibuf, session->ibuf + 1, session->ibuflen - 1);
+ memmove(session->ibuf, session->ibuf + consumed,
+ session->ibuflen - consumed);
session->ibuflen--;
}
@@ -370,14 +411,16 @@ session_field_input(struct session *session, unsigned
{
short ilen = 0, ipos = 0, lastlen = 0;
char *field;
- unsigned char c, redraw = 0;
+ unsigned short c;
+ unsigned char chc;
+ bool redraw = false;
session->abort_input = 0;
field = xmalloczero(size);
if (initial_input) {
- ipos = strlcpy(field, initial_input, size);
+ ipos = ilen = strlcpy(field, initial_input, size);
/* TODO: handle initial value being longer than width */
session_output_string(session, field);
session_flush(session);
@@ -398,48 +441,82 @@ session_field_input(struct session *session, unsigned
break;
case CONTROL_C: /* ^C */
goto field_input_bail;
- case 8: /* backspace / ^H */
- case 127: /* delete (through telnet) */
+ case BACKSPACE: /* ^H */
+ case DELETE: /* backspace through telnet */
if (ipos == 0)
continue;
if (ipos < ilen) {
/* l:3 p:2 f:ab[cursor]c -> l:2 p:2 f:a[cursor]c */
- redraw = 1;
- BlockMove((Ptr)(field + ipos), (Ptr)(field + ipos - 1),
- ilen - 1);
+ redraw = true;
+ memmove(field + ipos - 1, field + ipos, ilen - 1);
}
ipos--;
ilen--;
- field[ipos] = '\0';
+ field[ilen] = '\0';
+ session_output_string(session,
+ ansi(session, ANSI_BACKSPACE, ANSI_END));
+
if (redraw) {
- /* TODO */
- } else {
session_output_string(session,
- ansi(session, ANSI_BACKSPACE, ANSI_END));
- session_flush(session);
+ ansi(session, ANSI_SAVE_CURSOR, ANSI_END));
+ session_output(session, field + ipos,
+ ilen - ipos);
+ session_output(session, " ", 1);
+ session_output_string(session,
+ ansi(session, ANSI_RESTORE_SAVED_CURSOR, ANSI_END));
}
-
+
+ session_flush(session);
break;
case '\r':
case '\n':
return field;
+ case KEY_LEFT:
+ if (ipos == 0)
+ continue;
+ ipos--;
+ session_output_string(session,
+ ansi(session, ANSI_BACK_N, 1, ANSI_END));
+ session_flush(session);
+ break;
+ case KEY_RIGHT:
+ if (ipos == ilen)
+ continue;
+ ipos++;
+ session_output_string(session,
+ ansi(session, ANSI_FORWARD_N, 1, ANSI_END));
+ session_flush(session);
+ break;
default:
if (c < 32 || c > 127)
break;
if (ilen >= size - 1)
break;
- if (ipos <= ilen)
- /* TODO: move */
- ;
+ chc = c;
+ if (ipos < ilen)
+ memmove(field + ipos + 1, field + ipos, ilen - ipos);
- field[ipos] = c;
+ field[ipos] = chc;
ipos++;
ilen++;
- field[ipos] = '\0';
- session_output(session, mask ? &mask : (char *)&c, 1);
+ field[ilen] = '\0';
+ session_output(session, mask ? &mask : (char *)&chc, 1);
+
+ if (ipos < ilen) {
+ if (mask) {
+ /* TODO: repeat mask */
+ } else {
+ session_output_string(session,
+ ansi(session, ANSI_SAVE_CURSOR, ANSI_END));
+ session_output(session, field + ipos,
+ ilen - ipos);
+ session_output_string(session,
+ ansi(session, ANSI_RESTORE_SAVED_CURSOR, ANSI_END));
+ }
+ }
session_flush(session);
}
}
@@ -456,7 +533,8 @@ session_multiline_input(struct session *session, short
size_t size = 0, length = 0;
size_t pos = 0;
char *field = NULL;
- unsigned char c, redraw = 0;
+ unsigned short c;
+ bool redraw = false;
session->abort_input = 0;
@@ -486,14 +564,14 @@ session_multiline_input(struct session *session, short
goto multiline_input_bail;
case CONTROL_D: /* ^D */
return field;
- case 8: /* backspace / ^H */
- case 127: /* delete (through telnet) */
+ case BACKSPACE: /* ^H */
+ case DELETE: /* (backspace through telnet) */
if (pos == 0)
continue;
if (pos < length) {
/* l:3 p:2 f:ab[cursor]c -> l:2 p:2 f:a[cursor]c */
- redraw = 1;
+ redraw = true;
BlockMove((Ptr)(field + pos), (Ptr)(field + pos - 1),
length - 1);
}
@@ -504,6 +582,7 @@ session_multiline_input(struct session *session, short
if (redraw) {
/* TODO */
+ redraw = false;
} else {
session_output_string(session,
ansi(session, ANSI_BACKSPACE, ANSI_END));
--- session.h Thu Jan 27 13:53:41 2022
+++ session.h Mon Feb 21 13:50:47 2022
@@ -35,8 +35,16 @@ enum session_input_state {
#define CONTROL_C 3
#define CONTROL_D 4
+#define BACKSPACE 8
#define CONTROL_L 12
+#define DELETE 127
+#define KEY_UP (0x0100 | 'A')
+#define KEY_DOWN (0x0100 | 'B')
+#define KEY_RIGHT (0x0100 | 'C')
+#define KEY_LEFT (0x0100 | 'D')
+#define KEY_OTHER (0x0100 | 0xff)
+
struct node_funcs {
void (*setup)(struct session *session);
short (*input)(struct session *session);
@@ -62,7 +70,7 @@ struct session {
short obuflen;
short ibuflen;
enum session_input_state input_state;
- unsigned char last_input;
+ unsigned short last_input;
unsigned char abort_input;
unsigned long last_input_at;
unsigned short terminal_columns;
@@ -91,7 +99,7 @@ struct session * session_create(char *node, char *via,
struct node_funcs *node_funcs);
void session_close(struct session *session);
void session_idle(struct session *session);
-char session_input_char(struct session *session);
+unsigned short session_input_char(struct session *session);
void session_flush(struct session *session);
size_t session_output(struct session *session, const char *str, size_t len);
size_t session_output_string(struct session *session, const char *str);