From bead388d8156792e69b262eb48f139630e3df841 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 12 Nov 2016 15:25:10 +0530 Subject: [PATCH] More work on making Screen native --- kitty/control-codes.h | 65 +++++++++++++++++++++++ kitty/data-types.h | 5 ++ kitty/parser.c | 118 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 kitty/control-codes.h diff --git a/kitty/control-codes.h b/kitty/control-codes.h new file mode 100644 index 000000000..461ba24c9 --- /dev/null +++ b/kitty/control-codes.h @@ -0,0 +1,65 @@ +/* + * control_codes.h + * Copyright (C) 2016 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once + +// Space +#define SP ' ' + +// *Null*: Does nothing. +#define NUL 0 + +// *Bell*: Beeps. +#define BEL 0x07 + +// *Backspace*: Backspace one column, but not past the begining of the +// line. +#define BS 0x08 + +// *Horizontal tab*: Move cursor to the next tab stop, or to the end +// of the line if there is no earlier tab stop. +#define HT 0x09 + +// *Linefeed*: Give a line feed, and, if :data:`pyte.modes.LNM` (new +// line mode) is set also a carriage return. +#define LF 10 + +// *Vertical tab*: Same as :data:`LF`. +#define VT 0x0b +// *Form feed*: Same as :data:`LF`. +#define FF 0x0c + +// *Carriage return*: Move cursor to left margin on current line. +#define CR 13 + +// *Shift out*: Activate G1 character set. +#define SO 0x0e + +// *Shift in*: Activate G0 character set. +#define SI 0x0f + +// *Cancel*: Interrupt escape sequence. If received during an escape or +// control sequence, cancels the sequence and displays substitution +// character. +#define CAN 0x18 +// *Substitute*: Same as :data:`CAN`. +#define SUB 0x1a + +// *Escape*: Starts an escape sequence. +#define ESC 0x1b + +// *Delete*: Is ignored. +#define DEL 0x7f + +// *Control sequence introducer*: An equivalent for ``ESC [``. +#define CSI 0x9b + +// *String terminator*. +#define ST 0x9c + +// *Operating system command*. +#define OSC 0x9d diff --git a/kitty/data-types.h b/kitty/data-types.h index ecf7786cc..edf831e18 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -209,6 +209,11 @@ typedef struct { LineBuf *linebuf, *main_linebuf, *alt_linebuf; ChangeTracker *change_tracker; ScreenModes modes; + + uint8_t parser_buf[8192]; + unsigned int parser_state, parser_text_start; + bool parser_has_pending_text; + } Screen; Line* alloc_line(); diff --git a/kitty/parser.c b/kitty/parser.c index 9f3f72d79..71d7126d3 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -6,8 +6,107 @@ */ #include "data-types.h" +#include "control-codes.h" + extern PyTypeObject Screen_Type; +#define NORMAL_STATE 0 +#define ESC_STATE 1 +#define CSI_STATE 2 +#define OSC_STATE 3 +#define DCS_STATE 4 + +#define DECLARE_CH_SCREEN_HANDLER(name) extern bool screen_##name(Screen *screen, uint8_t ch); +DECLARE_CH_SCREEN_HANDLER(bell) +DECLARE_CH_SCREEN_HANDLER(backspace) +DECLARE_CH_SCREEN_HANDLER(tab) +DECLARE_CH_SCREEN_HANDLER(linefeed) +DECLARE_CH_SCREEN_HANDLER(carriage_return) +DECLARE_CH_SCREEN_HANDLER(shift_out) +DECLARE_CH_SCREEN_HANDLER(shift_in) +extern bool screen_draw(Screen *screen, uint8_t *buf, unsigned int buflen); + +static inline bool +read_text(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { + bool ret; + uint8_t ch; + + while(*pos < buflen) { + ch = buf[(*pos)++]; +#define DRAW_TEXT \ + if (screen->parser_has_pending_text) { \ + screen->parser_has_pending_text = false; \ + ret = screen_draw(screen, buf + screen->parser_text_start, (*pos) - screen->parser_text_start); \ + screen->parser_text_start = 0; \ + if (!ret) return false; \ + } + +#define CALL_SCREEN_HANDLER(name) \ + DRAW_TEXT; \ + if (!screen_##name(screen, ch)) return false; + +#define CHANGE_PARSER_STATE(state) screen->parser_state = state; return true; + switch(ch) { + case BEL: + CALL_SCREEN_HANDLER(bell); + case BS: + CALL_SCREEN_HANDLER(backspace); + case HT: + CALL_SCREEN_HANDLER(tab); + case LF: + case VT: + case FF: + CALL_SCREEN_HANDLER(linefeed); + case CR: + CALL_SCREEN_HANDLER(carriage_return); + case SO: + CALL_SCREEN_HANDLER(shift_out); + case SI: + CALL_SCREEN_HANDLER(shift_in); + case ESC: + CHANGE_PARSER_STATE(ESC_STATE); + case CSI: + CHANGE_PARSER_STATE(CSI_STATE); + case NUL: + case DEL: + break; + case OSC: + CHANGE_PARSER_STATE(OSC_STATE); + default: + if (!screen->parser_has_pending_text) { + screen->parser_has_pending_text = true; + screen->parser_text_start = (*pos) - 1; + } + } + } + DRAW_TEXT; + return true; +} + +static inline bool +read_esc(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { + screen->parser_state = NORMAL_STATE; + return true; +} + +static inline bool +read_csi(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { + screen->parser_state = NORMAL_STATE; + return true; +} + +static inline bool +read_osc(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { + screen->parser_state = NORMAL_STATE; + return true; +} + +static inline bool +read_dcs(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { + screen->parser_state = NORMAL_STATE; + return true; +} + PyObject* #ifdef DUMP_COMMANDS parse_bytes_dump(PyObject UNUSED *self, PyObject *args) { @@ -22,5 +121,24 @@ parse_bytes(PyObject UNUSED *self, PyObject *args) { #else if (!PyArg_ParseTuple(args, "O!y*", &Screen_Type, &screen, &pybuf)) return NULL; #endif + uint8_t *buf = pybuf.buf; + unsigned int i = 0; +#define CALL_HANDLER(name) \ + if (!name(screen, buf, pybuf.len, &i)) return NULL; break; \ + + while (i < pybuf.len) { + switch(screen->parser_state) { + case ESC_STATE: + CALL_HANDLER(read_esc); + case CSI_STATE: + CALL_HANDLER(read_csi); + case OSC_STATE: + CALL_HANDLER(read_osc); + case DCS_STATE: + CALL_HANDLER(read_dcs); + default: + CALL_HANDLER(read_text); + } + } Py_RETURN_NONE; }