mirror of https://github.com/buserror/mii_emu.git
234 lines
7.1 KiB
C
234 lines
7.1 KiB
C
/*
|
|
* mish_client_input.c
|
|
*
|
|
* Copyright (C) 2020 Michel Pollet <buserror@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "mish_priv.h"
|
|
#include "mish_priv_cmd.h"
|
|
#include "mish.h"
|
|
|
|
/*
|
|
* Don't assume this handles every key combination in the world, it doesn't,
|
|
* it handles mostly the one I use most from bash!
|
|
* You like VI syntax, saaaaad story :-)
|
|
*/
|
|
//! Parse current input buffer
|
|
int
|
|
_mish_client_vt_parse_input(
|
|
struct mish_t *m,
|
|
struct mish_input_t *in,
|
|
uint8_t ich)
|
|
{
|
|
mish_client_p c = in->refcon;
|
|
|
|
/*
|
|
* if the sequence buffer is currently being flushed, we need to store
|
|
* the input until it's done.
|
|
* So lets store it into the input buffer, and once that output is finished
|
|
* we can process it as before.
|
|
*/
|
|
if (c->output.sqb->done)
|
|
return MISH_IN_STORE;
|
|
// now flush what was stored, plus the one we just received
|
|
for (int i = 0; i <= in->line->done; i++) {
|
|
uint8_t ch = i == in->line->done ? ich : in->line->line[i];
|
|
|
|
if (in->is_telnet) {
|
|
if (_mish_telnet_parse(c, ch))
|
|
continue;
|
|
}
|
|
if (!_mish_vt_sequence_char(&c->vts, ch))
|
|
continue;
|
|
// if no command is editing, create an empty one, add it to history
|
|
if (!c->cmd) {
|
|
_mish_line_reserve(&c->cmd, 4);
|
|
TAILQ_INSERT_TAIL(&in->backlog, c->cmd, self);
|
|
} else {
|
|
/*
|
|
* we need to detach the element if we're going to resize it,
|
|
* otherwise the list is completely fubar as the pointer changes
|
|
*/
|
|
if (c->cmd->size - c->cmd->len <= 4) {
|
|
mish_line_p next = TAILQ_NEXT(c->cmd, self);
|
|
TAILQ_REMOVE(&in->backlog, c->cmd, self);
|
|
_mish_line_reserve(&c->cmd, 4);
|
|
if (next)
|
|
TAILQ_INSERT_BEFORE(next, c->cmd, self);
|
|
else
|
|
TAILQ_INSERT_TAIL(&in->backlog, c->cmd, self);
|
|
}
|
|
}
|
|
switch (c->vts.seq) {
|
|
case MISH_VT_SEQ(CSI, '~'): {
|
|
mish_line_p cursor = c->bottom;
|
|
if (c->vts.p[0] == 1) // GNU screen HOME seq
|
|
goto kb_home;
|
|
else if (c->vts.p[0] == 4) // GNU screen END seq
|
|
goto kb_end;
|
|
if (c->vts.p[0] == 5) { // Page UP
|
|
for (int i = 0; i < c->window_size.h - 3 && cursor; i++)
|
|
cursor = TAILQ_PREV(cursor, mish_line_queue_t, self);
|
|
if (cursor) {
|
|
c->bottom = cursor;
|
|
c->flags |= MISH_CLIENT_UPDATE_WINDOW;
|
|
c->flags &= ~MISH_CLIENT_SCROLLING;
|
|
}
|
|
} else if (c->vts.p[0] == 6) { // down
|
|
for (int i = 0; i < c->window_size.h - 3 && cursor; i++)
|
|
cursor = TAILQ_NEXT(cursor, self);
|
|
c->bottom = cursor;
|
|
c->flags |= MISH_CLIENT_UPDATE_WINDOW;
|
|
if (!c->bottom)
|
|
c->flags |= MISH_CLIENT_SCROLLING;
|
|
}
|
|
} break;
|
|
case MISH_VT_SEQ(CSI, 'H'): { // Home
|
|
kb_home:
|
|
// don't bother if there's not enough backlog
|
|
if (m->backlog.size < c->window_size.h - 2)
|
|
break;
|
|
c->bottom = TAILQ_FIRST(&m->backlog.log);
|
|
for (i = 0; i < c->window_size.h - 2 - 1 && c->bottom; i++)
|
|
c->bottom = TAILQ_NEXT(c->bottom, self);
|
|
c->flags |= MISH_CLIENT_UPDATE_WINDOW;
|
|
if (c->bottom)
|
|
c->flags &= ~MISH_CLIENT_SCROLLING;
|
|
} break;
|
|
case MISH_VT_SEQ(CSI, 'F'): { // END
|
|
kb_end:
|
|
c->flags |= MISH_CLIENT_UPDATE_WINDOW | MISH_CLIENT_SCROLLING;
|
|
c->bottom = NULL;
|
|
} break;
|
|
case MISH_VT_SEQ(CSI, 'R'):
|
|
c->flags |= MISH_CLIENT_HAS_CURSOR_POS;
|
|
c->cursor_pos.y = c->vts.p[0];
|
|
c->cursor_pos.x = c->vts.p[1];
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 16): { // CTRL-P Prev history
|
|
if (TAILQ_FIRST(&in->backlog) != c->cmd) {
|
|
c->flags |= MISH_CLIENT_UPDATE_PROMPT;
|
|
c->cmd = TAILQ_PREV(c->cmd, mish_line_queue_t, self);
|
|
}
|
|
} break;
|
|
case MISH_VT_SEQ(RAW, 14): // CTRL-N Next history
|
|
if (TAILQ_LAST(&in->backlog, mish_line_queue_t) != c->cmd) {
|
|
c->flags |= MISH_CLIENT_UPDATE_PROMPT;
|
|
c->cmd = TAILQ_NEXT(c->cmd, self);
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 1): // CTRL-A Start of line
|
|
if (c->cmd->done) {
|
|
_mish_send_queue_fmt(c, "\033[%dD", c->cmd->done);
|
|
c->cmd->done = 0;
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 5): // CTRL-E End of Line
|
|
if (c->cmd->done < c->cmd->len) {
|
|
_mish_send_queue_fmt(c, "\033[%dC",
|
|
c->cmd->len - c->cmd->done);
|
|
c->cmd->done = c->cmd->len;
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 2): // CTRL-B Prev char
|
|
if (c->cmd->done) {
|
|
c->cmd->done--;
|
|
_mish_send_queue_fmt(c, "\033[%dD", 1);
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 6): // CTRL-F Next Char
|
|
if (c->cmd->done < c->cmd->len) {
|
|
c->cmd->done++;
|
|
_mish_send_queue_fmt(c, "\033[%dC", 1);
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 23): { // CTRL-W Delete prev word
|
|
int old_pos = c->cmd->done;
|
|
while (c->cmd->done && c->cmd->line[c->cmd->done-1] == ' ')
|
|
c->cmd->done--;
|
|
while (c->cmd->done && c->cmd->line[c->cmd->done-1] != ' ')
|
|
c->cmd->done--;
|
|
|
|
if (old_pos - c->cmd->done) {
|
|
int del = old_pos - c->cmd->done;
|
|
memmove(c->cmd->line + c->cmd->done,
|
|
c->cmd->line + old_pos,
|
|
c->cmd->len - old_pos + 1);
|
|
c->cmd->len -= del;
|
|
// move back del characters, and delete them
|
|
_mish_send_queue_fmt(c, "\033[%dD\033[%dP", del, del);
|
|
}
|
|
} break;
|
|
case MISH_VT_SEQ(RAW, 0x7f): // DEL
|
|
case MISH_VT_SEQ(RAW, 8): // CTRL-H
|
|
if (c->cmd->done) {
|
|
c->cmd->done--;
|
|
memmove(c->cmd->line + c->cmd->done,
|
|
c->cmd->line + c->cmd->done + 1,
|
|
c->cmd->len - c->cmd->done + 1);
|
|
c->cmd->len--;
|
|
// backspace plus Delete (1) Character
|
|
_mish_send_queue(c, "\x8\033[P");
|
|
}
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 11): // CTRL-K Kill rest of line
|
|
c->cmd->len = c->cmd->done;
|
|
c->cmd->line[c->cmd->len] = 0;
|
|
_mish_send_queue(c, "\033[K");
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 12): // CTRL-L Redraw
|
|
c->flags |= MISH_CLIENT_UPDATE_WINDOW;
|
|
break;
|
|
case MISH_VT_SEQ(RAW, 13): // CTRL-M aka return
|
|
c->cmd->line[c->cmd->len] = 0;
|
|
if (0) { // debug
|
|
fprintf(stdout, "CMD: '%.*s", c->cmd->done, c->cmd->line);
|
|
fprintf(stdout, "\033[7m%.*s\033[0m", 1,
|
|
c->cmd->line + c->cmd->done);
|
|
if (c->cmd->done < c->cmd->len)
|
|
fprintf(stdout, "%s", c->cmd->line + c->cmd->done+1);
|
|
fprintf(stdout, "'\n");
|
|
}
|
|
mish_cmd_call(c->cmd->line, c);
|
|
c->cmd = NULL; // new one
|
|
{ // reuse the last empty one
|
|
mish_line_p last = TAILQ_LAST(&in->backlog, mish_line_queue_t);
|
|
if (last && last->len == 0)
|
|
c->cmd = last;
|
|
}
|
|
c->flags |= MISH_CLIENT_UPDATE_PROMPT;
|
|
break;
|
|
default:
|
|
if (c->vts.seq & ~0xff) {
|
|
printf(MISH_COLOR_RED
|
|
"mish: Unknown sequence: %08x ", c->vts.seq);
|
|
for (int i = 0; i < c->vts.pc; i++)
|
|
printf(":%d", c->vts.p[i]);
|
|
printf("'%c%c'", c->vts.seq >> 8, c->vts.seq & 0xff);
|
|
printf(MISH_COLOR_RESET "\n");
|
|
}
|
|
break;
|
|
}
|
|
if (c->vts.glyph && c->vts.glyph >= ' ' && c->vts.glyph < 0x7f) {
|
|
memmove(c->cmd->line + c->cmd->done + 1,
|
|
c->cmd->line + c->cmd->done,
|
|
c->cmd->len - c->cmd->done);
|
|
c->cmd->line[c->cmd->done] = c->vts.glyph;
|
|
c->cmd->done++;
|
|
c->cmd->len++;
|
|
c->cmd->line[c->cmd->len] = 0;
|
|
// no need to explicitly insert, terminal should already be setup
|
|
_mish_send_queue_fmt(c, "%lc", c->vts.glyph);
|
|
}
|
|
}
|
|
// remove any buffered input.
|
|
in->line->len = 0;
|
|
return MISH_IN_SKIP;
|
|
}
|