2019-06-15 22:22:44 +02:00
|
|
|
#include "vt.h"
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2019-07-15 15:42:00 +02:00
|
|
|
#include <unistd.h>
|
2019-06-15 22:22:44 +02:00
|
|
|
|
2020-08-20 19:25:35 +02:00
|
|
|
#if defined(FOOT_GRAPHEME_CLUSTERING)
|
|
|
|
|
#include <utf8proc.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
#define LOG_MODULE "vt"
|
2019-07-03 20:21:03 +02:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-15 22:22:44 +02:00
|
|
|
#include "log.h"
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
#include "char32.h"
|
2020-08-20 19:25:35 +02:00
|
|
|
#include "config.h"
|
2019-06-15 22:22:44 +02:00
|
|
|
#include "csi.h"
|
2020-01-12 11:55:22 +01:00
|
|
|
#include "dcs.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
#include "debug.h"
|
2019-07-19 09:55:07 +02:00
|
|
|
#include "osc.h"
|
2021-12-26 16:42:53 +01:00
|
|
|
#include "sixel.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
#include "util.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
#include "xmalloc.h"
|
2019-08-30 22:08:37 +02:00
|
|
|
|
2019-11-29 23:59:24 +01:00
|
|
|
#define UNHANDLED() LOG_DBG("unhandled: %s", esc_as_string(term, final))
|
2019-07-30 21:42:46 +02:00
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
/* https://vt100.net/emu/dec_ansi_parser */
|
|
|
|
|
|
|
|
|
|
enum state {
|
|
|
|
|
STATE_GROUND,
|
2019-06-23 13:28:55 +02:00
|
|
|
STATE_ESCAPE,
|
|
|
|
|
STATE_ESCAPE_INTERMEDIATE,
|
|
|
|
|
|
|
|
|
|
STATE_CSI_ENTRY,
|
|
|
|
|
STATE_CSI_PARAM,
|
|
|
|
|
STATE_CSI_INTERMEDIATE,
|
|
|
|
|
STATE_CSI_IGNORE,
|
|
|
|
|
|
|
|
|
|
STATE_OSC_STRING,
|
|
|
|
|
|
|
|
|
|
STATE_DCS_ENTRY,
|
|
|
|
|
STATE_DCS_PARAM,
|
|
|
|
|
STATE_DCS_INTERMEDIATE,
|
|
|
|
|
STATE_DCS_IGNORE,
|
|
|
|
|
STATE_DCS_PASSTHROUGH,
|
2019-06-15 22:22:44 +02:00
|
|
|
|
2019-06-23 13:28:55 +02:00
|
|
|
STATE_SOS_PM_APC_STRING,
|
|
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
STATE_UTF8_21,
|
|
|
|
|
STATE_UTF8_31,
|
|
|
|
|
STATE_UTF8_32,
|
|
|
|
|
STATE_UTF8_41,
|
|
|
|
|
STATE_UTF8_42,
|
|
|
|
|
STATE_UTF8_43,
|
2019-06-15 22:22:44 +02:00
|
|
|
};
|
|
|
|
|
|
2019-11-05 13:55:43 +01:00
|
|
|
#if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG && 0
|
2019-06-15 22:22:44 +02:00
|
|
|
static const char *const state_names[] = {
|
|
|
|
|
[STATE_GROUND] = "ground",
|
|
|
|
|
|
2019-06-23 13:28:55 +02:00
|
|
|
[STATE_ESCAPE] = "escape",
|
|
|
|
|
[STATE_ESCAPE_INTERMEDIATE] = "escape intermediate",
|
|
|
|
|
|
|
|
|
|
[STATE_CSI_ENTRY] = "CSI entry",
|
|
|
|
|
[STATE_CSI_PARAM] = "CSI param",
|
|
|
|
|
[STATE_CSI_INTERMEDIATE] = "CSI intermediate",
|
|
|
|
|
[STATE_CSI_IGNORE] = "CSI ignore",
|
|
|
|
|
|
|
|
|
|
[STATE_OSC_STRING] = "OSC string",
|
|
|
|
|
|
|
|
|
|
[STATE_DCS_ENTRY] = "DCS entry",
|
|
|
|
|
[STATE_DCS_PARAM] = "DCS param",
|
|
|
|
|
[STATE_DCS_INTERMEDIATE] = "DCS intermediate",
|
|
|
|
|
[STATE_DCS_IGNORE] = "DCS ignore",
|
|
|
|
|
[STATE_DCS_PASSTHROUGH] = "DCS passthrough",
|
|
|
|
|
|
|
|
|
|
[STATE_SOS_PM_APC_STRING] = "sos/pm/apc string",
|
|
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
[STATE_UTF8_21] = "UTF8 2-byte 1/2",
|
|
|
|
|
[STATE_UTF8_31] = "UTF8 3-byte 1/3",
|
|
|
|
|
[STATE_UTF8_32] = "UTF8 3-byte 2/3",
|
2019-06-15 22:22:44 +02:00
|
|
|
};
|
2019-12-20 22:10:27 +01:00
|
|
|
#endif
|
2019-06-15 22:22:44 +02:00
|
|
|
|
2019-11-30 00:02:19 +01:00
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
2019-07-04 19:35:01 +02:00
|
|
|
static const char *
|
|
|
|
|
esc_as_string(struct terminal *term, uint8_t final)
|
2019-06-23 13:36:20 +02:00
|
|
|
{
|
2019-07-04 19:35:01 +02:00
|
|
|
static char msg[1024];
|
2019-07-10 16:04:25 +02:00
|
|
|
int c = snprintf(msg, sizeof(msg), "\\E");
|
2019-06-23 13:36:20 +02:00
|
|
|
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
for (size_t i = 0; i < sizeof(term->vt.private); i++) {
|
|
|
|
|
char value = (term->vt.private >> (i * 8)) & 0xff;
|
|
|
|
|
if (value == 0)
|
2019-07-19 09:56:59 +02:00
|
|
|
break;
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
c += snprintf(&msg[c], sizeof(msg) - c, "%c", value);
|
2019-07-19 09:56:59 +02:00
|
|
|
}
|
2019-06-23 13:36:20 +02:00
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(term->vt.params.idx == 0);
|
2019-07-15 13:39:19 +02:00
|
|
|
|
2019-11-05 10:39:36 +01:00
|
|
|
snprintf(&msg[c], sizeof(msg) - c, "%c", final);
|
2019-07-04 19:35:01 +02:00
|
|
|
return msg;
|
|
|
|
|
|
|
|
|
|
}
|
2019-11-30 00:02:19 +01:00
|
|
|
#endif
|
2019-07-04 19:35:01 +02:00
|
|
|
|
2019-07-07 16:32:18 +02:00
|
|
|
static void
|
2019-12-20 23:27:15 +01:00
|
|
|
action_ignore(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_clear(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
term->vt.params.idx = 0;
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
term->vt.private = 0;
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_execute(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("execute: 0x%02x", c);
|
|
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 7-bit C0 control characters
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
case '\0':
|
|
|
|
|
break;
|
|
|
|
|
|
2020-07-14 09:11:17 +02:00
|
|
|
case '\a':
|
|
|
|
|
/* BEL - bell */
|
2020-10-08 19:55:32 +02:00
|
|
|
term_bell(term);
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\b':
|
|
|
|
|
/* backspace */
|
2021-01-15 18:40:07 +01:00
|
|
|
#if 0
|
|
|
|
|
/*
|
2024-02-06 12:36:45 +01:00
|
|
|
* This is the "correct" BS behavior. However, it doesn't play
|
2021-01-15 18:40:07 +01:00
|
|
|
* nicely with bw/auto_left_margin, hence the alternative
|
|
|
|
|
* implementation below.
|
|
|
|
|
*
|
2024-02-06 12:36:45 +01:00
|
|
|
* Note that it breaks vttest "1. Test of cursor movements ->
|
|
|
|
|
* Test of autowrap"
|
2021-01-15 18:40:07 +01:00
|
|
|
*/
|
|
|
|
|
term_cursor_left(term, 1);
|
|
|
|
|
#else
|
2020-10-02 21:40:30 +02:00
|
|
|
if (term->grid->cursor.lcf)
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2021-04-08 12:58:47 +02:00
|
|
|
else {
|
|
|
|
|
/* Reverse wrap */
|
|
|
|
|
if (unlikely(term->grid->cursor.point.col == 0) &&
|
|
|
|
|
likely(term->reverse_wrap && term->auto_margin))
|
|
|
|
|
{
|
|
|
|
|
if (term->grid->cursor.point.row <= term->scroll_region.start) {
|
2024-02-06 12:36:45 +01:00
|
|
|
/* Don't wrap past, or inside, the scrolling region(?) */
|
2021-04-08 12:58:47 +02:00
|
|
|
} else
|
|
|
|
|
term_cursor_to(
|
|
|
|
|
term,
|
|
|
|
|
term->grid->cursor.point.row - 1,
|
|
|
|
|
term->cols - 1);
|
|
|
|
|
} else
|
|
|
|
|
term_cursor_left(term, 1);
|
|
|
|
|
}
|
2021-01-15 18:40:07 +01:00
|
|
|
#endif
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-07-14 09:11:17 +02:00
|
|
|
case '\t': {
|
2019-12-20 23:27:15 +01:00
|
|
|
/* HT - horizontal tab */
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
int start_col = term->grid->cursor.point.col;
|
2019-12-20 23:27:15 +01:00
|
|
|
int new_col = term->cols - 1;
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
tll_foreach(term->tab_stops, it) {
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
if (it->item > start_col) {
|
2019-12-20 23:27:15 +01:00
|
|
|
new_col = it->item;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
xassert(new_col >= start_col);
|
|
|
|
|
xassert(new_col < term->cols);
|
|
|
|
|
|
|
|
|
|
struct row *row = term->grid->cur_row;
|
|
|
|
|
|
2021-06-07 21:16:38 +02:00
|
|
|
bool emit_tab_char = (row->cells[start_col].wc == 0 ||
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
row->cells[start_col].wc == U' ');
|
2021-06-07 21:16:38 +02:00
|
|
|
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
/* Check if all cells from here until the next tab stop are empty */
|
2021-06-07 21:16:38 +02:00
|
|
|
for (const struct cell *cell = &row->cells[start_col + 1];
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
cell < &row->cells[new_col];
|
|
|
|
|
cell++)
|
|
|
|
|
{
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
if (!(cell->wc == 0 || cell->wc == U' ')) {
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
emit_tab_char = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Emit a tab in current cell, and write spaces to the
|
|
|
|
|
* subsequent cells, all the way until the next tab stop.
|
|
|
|
|
*/
|
|
|
|
|
if (emit_tab_char) {
|
|
|
|
|
row->dirty = true;
|
|
|
|
|
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
row->cells[start_col].wc = U'\t';
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
row->cells[start_col].attrs.clean = 0;
|
|
|
|
|
|
|
|
|
|
for (struct cell *cell = &row->cells[start_col + 1];
|
|
|
|
|
cell < &row->cells[new_col];
|
|
|
|
|
cell++)
|
|
|
|
|
{
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
cell->wc = U' ';
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
cell->attrs.clean = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-14 10:47:17 +02:00
|
|
|
|
|
|
|
|
/* According to the specification, HT _should_ cancel LCF. But
|
|
|
|
|
* XTerm, and nearly all other emulators, don't. So we follow
|
2020-08-16 14:37:27 +01:00
|
|
|
* suit */
|
2020-07-14 10:47:17 +02:00
|
|
|
bool lcf = term->grid->cursor.lcf;
|
vt: emit a tab character if all cells between cursor and tab stop are empty
TAB (\t) move the cursor to the next tab stop. That’s it, according to
the specification.
However, many terminal emulators try to keep tabs in the grid, to be
able to e.g. copy them. That is, copying a text chunk containing tabs
should result in tabs being pasted, not spaces.
In order to do that, we need to print a tab character to the grid. To
improve text reflow of tabs, we also print spaces to the subsequent
cells, up until (but not including) the next tab stop.
However, we can only do this if all the cells between the cursor and
the next tab stop are empty, since (obviously), we cannot overwrite
pre-existing characters.
Finally, while some fonts render tabs as spaces (i.e. an empty glyph),
some use a glyph representing “unprintable” characters, or
similar. Thus, we need to exclude cells with tab characters when
rendering.
2021-06-05 22:48:20 +02:00
|
|
|
term_cursor_right(term, new_col - start_col);
|
2020-07-14 10:47:17 +02:00
|
|
|
term->grid->cursor.lcf = lcf;
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-14 09:11:17 +02:00
|
|
|
case '\n':
|
|
|
|
|
case '\v':
|
2020-07-14 09:18:52 +02:00
|
|
|
case '\f':
|
2020-07-14 09:15:15 +02:00
|
|
|
/* LF - \n - line feed */
|
|
|
|
|
/* VT - \v - vertical tab */
|
2020-07-14 09:18:52 +02:00
|
|
|
/* FF - \f - form feed */
|
2020-07-14 09:15:15 +02:00
|
|
|
term_linefeed(term);
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-07-14 09:11:17 +02:00
|
|
|
case '\r':
|
|
|
|
|
/* CR - carriage ret */
|
2020-07-14 09:29:10 +02:00
|
|
|
term_carriage_return(term);
|
2020-07-14 09:11:17 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
case '\x0e':
|
|
|
|
|
/* SO - shift out */
|
2021-06-09 09:51:48 +01:00
|
|
|
term->charsets.selected = G1;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-03-14 19:19:10 +01:00
|
|
|
term_update_ascii_printer(term);
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\x0f':
|
|
|
|
|
/* SI - shift in */
|
2021-06-09 09:51:48 +01:00
|
|
|
term->charsets.selected = G0;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-03-14 19:19:10 +01:00
|
|
|
term_update_ascii_printer(term);
|
2019-12-20 23:27:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 8-bit C1 control characters
|
|
|
|
|
*
|
|
|
|
|
* We ignore these, but keep them here for reference, along
|
|
|
|
|
* with their corresponding 7-bit variants.
|
|
|
|
|
*
|
|
|
|
|
* As far as I can tell, XTerm also ignores these _when in
|
|
|
|
|
* UTF-8 mode_. Which would be the normal mode of operation
|
|
|
|
|
* these days. And since we _only_ support UTF-8...
|
|
|
|
|
*/
|
2020-07-14 09:29:10 +02:00
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
#if 0
|
|
|
|
|
case '\x84': /* IND -> ESC D */
|
|
|
|
|
case '\x85': /* NEL -> ESC E */
|
|
|
|
|
case '\x88': /* Tab Set -> ESC H */
|
|
|
|
|
case '\x8d': /* RI -> ESC M */
|
|
|
|
|
case '\x8e': /* SS2 -> ESC N */
|
|
|
|
|
case '\x8f': /* SS3 -> ESC O */
|
|
|
|
|
case '\x90': /* DCS -> ESC P */
|
|
|
|
|
case '\x96': /* SPA -> ESC V */
|
|
|
|
|
case '\x97': /* EPA -> ESC W */
|
|
|
|
|
case '\x98': /* SOS -> ESC X */
|
|
|
|
|
case '\x9a': /* DECID -> ESC Z (obsolete form of CSI c) */
|
|
|
|
|
case '\x9b': /* CSI -> ESC [ */
|
|
|
|
|
case '\x9c': /* ST -> ESC \ */
|
|
|
|
|
case '\x9d': /* OSC -> ESC ] */
|
|
|
|
|
case '\x9e': /* PM -> ESC ^ */
|
|
|
|
|
case '\x9f': /* APC -> ESC _ */
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_print(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
2020-08-20 19:25:35 +02:00
|
|
|
term_reset_grapheme_state(term);
|
2021-03-14 19:19:10 +01:00
|
|
|
term->ascii_printer(term, c);
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2023-06-16 16:26:13 +02:00
|
|
|
action_param_lazy_init(struct terminal *term)
|
2019-12-20 23:27:15 +01:00
|
|
|
{
|
|
|
|
|
if (term->vt.params.idx == 0) {
|
|
|
|
|
struct vt_param *param = &term->vt.params.v[0];
|
2023-06-16 16:26:13 +02:00
|
|
|
|
|
|
|
|
term->vt.params.cur = param;
|
2019-12-20 23:27:15 +01:00
|
|
|
param->value = 0;
|
|
|
|
|
param->sub.idx = 0;
|
2023-06-16 16:26:13 +02:00
|
|
|
param->sub.cur = NULL;
|
2019-12-20 23:27:15 +01:00
|
|
|
term->vt.params.idx = 1;
|
|
|
|
|
}
|
2023-06-16 16:26:13 +02:00
|
|
|
}
|
2019-12-20 23:27:15 +01:00
|
|
|
|
2023-06-16 16:26:13 +02:00
|
|
|
static void
|
|
|
|
|
action_param_new(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
xassert(c == ';');
|
|
|
|
|
action_param_lazy_init(term);
|
2020-02-01 19:44:56 +01:00
|
|
|
|
|
|
|
|
const size_t max_params
|
|
|
|
|
= sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]);
|
|
|
|
|
|
2023-06-16 16:26:13 +02:00
|
|
|
struct vt_param *param;
|
2020-02-01 19:44:56 +01:00
|
|
|
|
2023-06-16 16:26:13 +02:00
|
|
|
if (unlikely(term->vt.params.idx >= max_params)) {
|
2020-02-01 19:44:56 +01:00
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
"unsupported: escape with more than %zu parameters "
|
|
|
|
|
"(will not warn again)",
|
|
|
|
|
sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]));
|
|
|
|
|
}
|
2023-06-16 16:26:13 +02:00
|
|
|
param = &term->vt.params.dummy;
|
|
|
|
|
} else
|
|
|
|
|
param = &term->vt.params.v[term->vt.params.idx++];
|
2020-02-01 19:44:56 +01:00
|
|
|
|
2023-06-16 16:26:13 +02:00
|
|
|
term->vt.params.cur = param;
|
|
|
|
|
param->value = 0;
|
|
|
|
|
param->sub.idx = 0;
|
|
|
|
|
param->sub.cur = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_param_new_subparam(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
xassert(c == ':');
|
|
|
|
|
action_param_lazy_init(term);
|
|
|
|
|
|
|
|
|
|
const size_t max_sub_params
|
|
|
|
|
= sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]);
|
|
|
|
|
|
|
|
|
|
struct vt_param *param = term->vt.params.cur;
|
|
|
|
|
unsigned *sub_param_value;
|
|
|
|
|
|
|
|
|
|
if (unlikely(param->sub.idx >= max_sub_params)) {
|
2020-02-01 19:44:56 +01:00
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
"unsupported: escape with more than %zu sub-parameters "
|
|
|
|
|
"(will not warn again)",
|
|
|
|
|
sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]));
|
|
|
|
|
}
|
2023-06-16 16:26:13 +02:00
|
|
|
|
|
|
|
|
sub_param_value = ¶m->sub.dummy;
|
|
|
|
|
} else
|
|
|
|
|
sub_param_value = ¶m->sub.value[param->sub.idx++];
|
|
|
|
|
|
|
|
|
|
param->sub.cur = sub_param_value;
|
|
|
|
|
*sub_param_value = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_param(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
action_param_lazy_init(term);
|
|
|
|
|
xassert(term->vt.params.cur != NULL);
|
|
|
|
|
|
|
|
|
|
struct vt_param *param = term->vt.params.cur;
|
|
|
|
|
unsigned *value;
|
|
|
|
|
|
|
|
|
|
if (unlikely(param->sub.cur != NULL))
|
|
|
|
|
value = param->sub.cur;
|
|
|
|
|
else
|
|
|
|
|
value = ¶m->value;
|
|
|
|
|
|
|
|
|
|
unsigned v = *value;
|
|
|
|
|
v *= 10;
|
|
|
|
|
v += c - '0';
|
|
|
|
|
*value = v;
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_collect(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
2020-02-01 19:29:14 +01:00
|
|
|
LOG_DBG("collect: %c", c);
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
|
|
|
|
|
/*
|
2020-12-16 15:06:34 +01:00
|
|
|
* Having more than one private is *very* rare. Foot only supports
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
* a *single* escape with two privates, and none with three or
|
|
|
|
|
* more.
|
|
|
|
|
*
|
|
|
|
|
* As such, we optimize *reading* the private(s), and *resetting*
|
2024-02-06 12:36:45 +01:00
|
|
|
* them (in action_clear()). Writing is ok if it's a bit slow.
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if ((term->vt.private & 0xff) == 0)
|
|
|
|
|
term->vt.private = c;
|
|
|
|
|
else if (((term->vt.private >> 8) & 0xff) == 0)
|
|
|
|
|
term->vt.private |= c << 8;
|
|
|
|
|
else if (((term->vt.private >> 16) & 0xff) == 0)
|
|
|
|
|
term->vt.private |= c << 16;
|
|
|
|
|
else if (((term->vt.private >> 24) & 0xff) == 0)
|
|
|
|
|
term->vt.private |= c << 24;
|
2019-12-20 23:27:15 +01:00
|
|
|
else
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
LOG_WARN("only four private/intermediate characters supported");
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-07-02 08:36:39 +01:00
|
|
|
UNITTEST
|
|
|
|
|
{
|
|
|
|
|
struct terminal term = {.vt = {.private = 0}};
|
|
|
|
|
uint32_t expected = ' ';
|
|
|
|
|
action_collect(&term, ' ');
|
|
|
|
|
xassert(term.vt.private == expected);
|
|
|
|
|
|
|
|
|
|
expected |= '/' << 8;
|
|
|
|
|
action_collect(&term, '/');
|
|
|
|
|
xassert(term.vt.private == expected);
|
|
|
|
|
|
|
|
|
|
expected |= '<' << 16;
|
|
|
|
|
action_collect(&term, '<');
|
|
|
|
|
xassert(term.vt.private == expected);
|
|
|
|
|
|
|
|
|
|
expected |= '?' << 24;
|
|
|
|
|
action_collect(&term, '?');
|
|
|
|
|
xassert(term.vt.private == expected);
|
|
|
|
|
|
|
|
|
|
action_collect(&term, '?');
|
|
|
|
|
xassert(term.vt.private == expected);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 07:00:14 +08:00
|
|
|
static void
|
|
|
|
|
tab_set(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
int col = term->grid->cursor.point.col;
|
|
|
|
|
|
|
|
|
|
if (tll_length(term->tab_stops) == 0 || tll_back(term->tab_stops) < col) {
|
|
|
|
|
tll_push_back(term->tab_stops, col);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->tab_stops, it) {
|
|
|
|
|
if (it->item < col) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (it->item > col) {
|
|
|
|
|
tll_insert_before(term->tab_stops, it, col);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
|
|
|
|
action_esc_dispatch(struct terminal *term, uint8_t final)
|
2019-07-04 19:35:01 +02:00
|
|
|
{
|
|
|
|
|
LOG_DBG("ESC: %s", esc_as_string(term, final));
|
2019-06-23 13:36:20 +02:00
|
|
|
|
vt: don’t ignore extra private/intermediate characters
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
2020-12-16 14:30:49 +01:00
|
|
|
switch (term->vt.private) {
|
2019-11-05 11:32:56 +01:00
|
|
|
case 0:
|
|
|
|
|
switch (final) {
|
|
|
|
|
case '7':
|
2021-01-15 17:08:30 +01:00
|
|
|
term_save_cursor(term);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
2019-07-17 10:39:38 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case '8':
|
2020-04-16 18:51:14 +02:00
|
|
|
term_restore_cursor(term, &term->grid->saved_cursor);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
2019-06-29 20:49:00 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'c':
|
|
|
|
|
term_reset(term, true);
|
|
|
|
|
break;
|
2019-06-29 20:49:00 +02:00
|
|
|
|
2021-06-08 21:06:18 +01:00
|
|
|
case 'n':
|
|
|
|
|
/* LS2 - Locking Shift 2 */
|
2021-06-09 09:51:48 +01:00
|
|
|
term->charsets.selected = G2;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-06-08 21:06:18 +01:00
|
|
|
term_update_ascii_printer(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
|
/* LS3 - Locking Shift 3 */
|
2021-06-09 09:51:48 +01:00
|
|
|
term->charsets.selected = G3;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-06-08 21:06:18 +01:00
|
|
|
term_update_ascii_printer(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'D':
|
|
|
|
|
term_linefeed(term);
|
|
|
|
|
break;
|
2019-07-10 16:05:19 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'E':
|
2020-07-14 09:29:10 +02:00
|
|
|
term_carriage_return(term);
|
2019-11-05 11:32:56 +01:00
|
|
|
term_linefeed(term);
|
|
|
|
|
break;
|
2019-07-18 19:25:53 +02:00
|
|
|
|
2019-11-16 10:57:11 +01:00
|
|
|
case 'H':
|
2023-07-20 07:00:14 +08:00
|
|
|
tab_set(term);
|
2019-11-16 10:57:11 +01:00
|
|
|
break;
|
|
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'M':
|
|
|
|
|
term_reverse_index(term);
|
|
|
|
|
break;
|
2019-07-10 16:05:01 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'N':
|
|
|
|
|
/* SS2 - Single Shift 2 */
|
2021-06-09 09:51:48 +01:00
|
|
|
term_single_shift(term, G2);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
2019-07-18 19:25:53 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'O':
|
|
|
|
|
/* SS3 - Single Shift 3 */
|
2021-06-09 09:51:48 +01:00
|
|
|
term_single_shift(term, G3);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
2019-07-18 19:25:53 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case '\\':
|
|
|
|
|
/* ST - String Terminator */
|
|
|
|
|
break;
|
2019-07-10 16:05:19 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case '=':
|
|
|
|
|
term->keypad_keys_mode = KEYPAD_APPLICATION;
|
|
|
|
|
break;
|
2019-07-03 16:21:26 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case '>':
|
|
|
|
|
term->keypad_keys_mode = KEYPAD_NUMERICAL;
|
|
|
|
|
break;
|
2019-07-03 16:21:26 +02:00
|
|
|
|
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
break;
|
2019-07-03 16:21:26 +02:00
|
|
|
}
|
2019-11-05 11:32:56 +01:00
|
|
|
break; /* private[0] == 0 */
|
|
|
|
|
|
2021-06-08 16:52:00 +01:00
|
|
|
// Designate character set
|
|
|
|
|
case '(': // G0
|
|
|
|
|
case ')': // G1
|
|
|
|
|
case '*': // G2
|
|
|
|
|
case '+': // G3
|
2019-11-05 11:32:56 +01:00
|
|
|
switch (final) {
|
|
|
|
|
case '0': {
|
2021-06-08 16:52:00 +01:00
|
|
|
size_t idx = term->vt.private - '(';
|
2021-06-09 09:51:48 +01:00
|
|
|
xassert(idx <= G3);
|
2019-11-17 09:59:12 +01:00
|
|
|
term->charsets.set[idx] = CHARSET_GRAPHIC;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-03-14 19:19:10 +01:00
|
|
|
term_update_ascii_printer(term);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2019-06-29 20:49:00 +02:00
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case 'B': {
|
2021-06-08 16:52:00 +01:00
|
|
|
size_t idx = term->vt.private - '(';
|
2021-06-09 09:51:48 +01:00
|
|
|
xassert(idx <= G3);
|
2019-11-17 09:59:12 +01:00
|
|
|
term->charsets.set[idx] = CHARSET_ASCII;
|
2024-06-24 01:26:57 +02:00
|
|
|
term->bits_affecting_ascii_printer.charset =
|
|
|
|
|
term->charsets.set[term->charsets.selected] != CHARSET_ASCII;
|
2021-03-14 19:19:10 +01:00
|
|
|
term_update_ascii_printer(term);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-23 13:36:20 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-11-05 11:32:56 +01:00
|
|
|
case '#':
|
|
|
|
|
switch (final) {
|
2021-12-26 16:07:51 +01:00
|
|
|
case '8': /* DECALN */
|
2021-12-26 16:42:53 +01:00
|
|
|
sixel_overwrite_by_rectangle(term, 0, 0, term->rows, term->cols);
|
|
|
|
|
|
|
|
|
|
term->scroll_region.start = 0;
|
|
|
|
|
term->scroll_region.end = term->rows;
|
|
|
|
|
|
2021-12-26 16:07:51 +01:00
|
|
|
for (int r = 0; r < term->rows; r++)
|
|
|
|
|
term_fill(term, r, 0, 'E', term->cols, false);
|
2021-12-26 16:42:53 +01:00
|
|
|
|
|
|
|
|
term_cursor_home(term);
|
2019-11-05 11:32:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break; /* private[0] == '#' */
|
|
|
|
|
|
2019-07-07 16:32:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
|
|
|
|
action_csi_dispatch(struct terminal *term, uint8_t c)
|
2019-11-17 17:22:16 +01:00
|
|
|
{
|
2019-12-20 23:27:15 +01:00
|
|
|
csi_dispatch(term, c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_osc_start(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
term->vt.osc.idx = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_osc_end(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
2022-03-20 16:31:44 +01:00
|
|
|
struct vt *vt = &term->vt;
|
|
|
|
|
|
|
|
|
|
if (!osc_ensure_size(term, vt->osc.idx + 1))
|
2019-11-30 00:32:34 +01:00
|
|
|
return;
|
2022-03-20 16:31:44 +01:00
|
|
|
|
|
|
|
|
vt->osc.data[vt->osc.idx] = '\0';
|
|
|
|
|
vt->osc.bel = c == '\a';
|
2019-12-20 23:27:15 +01:00
|
|
|
osc_dispatch(term);
|
2022-03-20 16:31:44 +01:00
|
|
|
|
|
|
|
|
if (unlikely(vt->osc.idx >= 4096)) {
|
|
|
|
|
free(vt->osc.data);
|
|
|
|
|
vt->osc.data = NULL;
|
|
|
|
|
vt->osc.size = 0;
|
|
|
|
|
}
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_osc_put(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
if (!osc_ensure_size(term, term->vt.osc.idx + 1))
|
2019-11-30 00:32:34 +01:00
|
|
|
return;
|
2019-12-20 23:27:15 +01:00
|
|
|
term->vt.osc.data[term->vt.osc.idx++] = c;
|
|
|
|
|
}
|
2019-11-30 00:32:34 +01:00
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
|
|
|
|
action_hook(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
2020-01-12 11:55:22 +01:00
|
|
|
dcs_hook(term, c);
|
2019-11-17 17:22:16 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
|
|
|
|
action_unhook(struct terminal *term, uint8_t c)
|
2019-07-07 16:32:18 +02:00
|
|
|
{
|
2020-01-12 11:55:22 +01:00
|
|
|
dcs_unhook(term);
|
2019-07-07 16:32:18 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
|
|
|
|
action_put(struct terminal *term, uint8_t c)
|
2019-07-07 16:32:18 +02:00
|
|
|
{
|
2020-01-12 11:55:22 +01:00
|
|
|
dcs_put(term, c);
|
2019-12-20 23:27:15 +01:00
|
|
|
}
|
2019-11-30 00:15:05 +01:00
|
|
|
|
2019-12-20 23:27:15 +01:00
|
|
|
static void
|
fcft: adapt to API changes in fcft-3.x
Fcft no longer uses wchar_t, but plain uint32_t to represent
codepoints.
Since we do a fair amount of string operations in foot, it still makes
sense to use something that actually _is_ a string (or character),
rather than an array of uint32_t.
For this reason, we switch out all wchar_t usage in foot to
char32_t. We also verify, at compile-time, that char32_t used
UTF-32 (which is what fcft expects).
Unfortunately, there are no string functions for char32_t. To avoid
having to re-implement all wcs*() functions, we add a small wrapper
layer of c32*() functions.
These wrapper functions take char32_t arguments, but then simply call
the corresponding wcs*() function.
For this to work, wcs*() must _also_ be UTF-32 compatible. We can
check for the presence of the __STDC_ISO_10646__ macro. If set,
wchar_t is at least 4 bytes and its internal representation is UTF-32.
FreeBSD does *not* define this macro, because its internal wchar_t
representation depends on the current locale. It _does_ use UTF-32
_if_ the current locale is UTF-8.
Since foot enforces UTF-8, we simply need to check if __FreeBSD__ is
defined.
Other fcft API changes:
* fcft_glyph_rasterize() -> fcft_codepoint_rasterize()
* font.space_advance has been removed
* ‘tags’ have been removed from fcft_grapheme_rasterize()
* ‘fcft_log_init()’ removed
* ‘fcft_init()’ and ‘fcft_fini()’ must be explicitly called
2021-08-21 14:50:42 +02:00
|
|
|
action_utf8_print(struct terminal *term, char32_t wc)
|
2019-12-20 23:27:15 +01:00
|
|
|
{
|
2025-01-26 07:33:53 +01:00
|
|
|
term_process_and_print_non_ascii(term, wc);
|
2019-06-23 13:36:20 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
static void
|
|
|
|
|
action_utf8_21(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)
|
|
|
|
|
term->vt.utf8 = (c & 0x1f) << 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_22(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)
|
|
|
|
|
term->vt.utf8 |= c & 0x3f;
|
|
|
|
|
action_utf8_print(term, term->vt.utf8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_31(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 0xf) << 12) | ((utf8[1] & 0x3f) << 6) | (utf8[2] & 0x3f)
|
|
|
|
|
term->vt.utf8 = (c & 0x0f) << 12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_32(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 0xf) << 12) | ((utf8[1] & 0x3f) << 6) | (utf8[2] & 0x3f)
|
|
|
|
|
term->vt.utf8 |= (c & 0x3f) << 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_33(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 0xf) << 12) | ((utf8[1] & 0x3f) << 6) | (utf8[2] & 0x3f)
|
|
|
|
|
term->vt.utf8 |= c & 0x3f;
|
2023-07-22 11:21:41 +02:00
|
|
|
|
|
|
|
|
const char32_t utf32 = term->vt.utf8;
|
|
|
|
|
if (unlikely(utf32 >= 0xd800 && utf32 <= 0xdfff)) {
|
|
|
|
|
/* Invalid sequence - invalid UTF-16 surrogate halves */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 12:36:45 +01:00
|
|
|
/* Note: the E0 range contains overlong encodings. We don't try to
|
|
|
|
|
detect, as they'll still decode to valid UTF-32. */
|
2023-07-22 11:21:41 +02:00
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
action_utf8_print(term, term->vt.utf8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_41(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 7) << 18) | ((utf8[1] & 0x3f) << 12) | ((utf8[2] & 0x3f) << 6) | (utf8[3] & 0x3f);
|
|
|
|
|
term->vt.utf8 = (c & 0x07) << 18;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_42(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 7) << 18) | ((utf8[1] & 0x3f) << 12) | ((utf8[2] & 0x3f) << 6) | (utf8[3] & 0x3f);
|
|
|
|
|
term->vt.utf8 |= (c & 0x3f) << 12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_43(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 7) << 18) | ((utf8[1] & 0x3f) << 12) | ((utf8[2] & 0x3f) << 6) | (utf8[3] & 0x3f);
|
|
|
|
|
term->vt.utf8 |= (c & 0x3f) << 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
action_utf8_44(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
// wc = ((utf8[0] & 7) << 18) | ((utf8[1] & 0x3f) << 12) | ((utf8[2] & 0x3f) << 6) | (utf8[3] & 0x3f);
|
|
|
|
|
term->vt.utf8 |= c & 0x3f;
|
2023-07-22 11:21:41 +02:00
|
|
|
|
|
|
|
|
const char32_t utf32 = term->vt.utf8;
|
|
|
|
|
|
|
|
|
|
if (unlikely(utf32 > 0x10FFFF)) {
|
|
|
|
|
/* Invalid UTF-8 */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 12:36:45 +01:00
|
|
|
/* Note: the F0 range contains overlong encodings. We don't try to
|
|
|
|
|
detect, as they'll still decode to valid UTF-32. */
|
2023-07-22 11:21:41 +02:00
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
action_utf8_print(term, term->vt.utf8);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-03 08:56:47 +00:00
|
|
|
IGNORE_WARNING("-Wpedantic")
|
2020-08-23 09:38:37 +02:00
|
|
|
|
2019-12-20 19:16:52 +01:00
|
|
|
static enum state
|
2021-05-10 10:23:06 +01:00
|
|
|
anywhere(struct terminal *term, uint8_t data)
|
2019-12-20 19:16:52 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x18: action_execute(term, data); return STATE_GROUND;
|
|
|
|
|
case 0x1a: action_execute(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1b: action_clear(term); return STATE_ESCAPE;
|
2021-05-25 19:14:06 +01:00
|
|
|
|
|
|
|
|
/* 8-bit C1 control characters (not supported) */
|
|
|
|
|
case 0x80 ... 0x9f: return STATE_GROUND;
|
2021-05-10 09:54:07 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return term->vt.state;
|
2021-05-10 09:54:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
|
|
|
|
state_ground_switch(struct terminal *term, uint8_t data)
|
|
|
|
|
{
|
|
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
|
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_GROUND;
|
|
|
|
|
|
|
|
|
|
/* modified from 0x20..0x7f to 0x20..0x7e, since 0x7f is DEL, which is a zero-width character */
|
|
|
|
|
case 0x20 ... 0x7e: action_print(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
|
2021-05-10 09:54:07 +01:00
|
|
|
case 0xc2 ... 0xdf: action_utf8_21(term, data); return STATE_UTF8_21;
|
|
|
|
|
case 0xe0 ... 0xef: action_utf8_31(term, data); return STATE_UTF8_31;
|
|
|
|
|
case 0xf0 ... 0xf4: action_utf8_41(term, data); return STATE_UTF8_41;
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_escape_switch(struct terminal *term, uint8_t data)
|
2019-12-20 19:16:52 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 19:16:52 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_ESCAPE;
|
2019-12-20 23:38:16 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_ESCAPE_INTERMEDIATE;
|
|
|
|
|
case 0x30 ... 0x4f: action_esc_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x50: action_clear(term); return STATE_DCS_ENTRY;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x51 ... 0x57: action_esc_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x58: return STATE_SOS_PM_APC_STRING;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x59: action_esc_dispatch(term, data); return STATE_GROUND;
|
|
|
|
|
case 0x5a: action_esc_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x5b: action_clear(term); return STATE_CSI_ENTRY;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x5c: action_esc_dispatch(term, data); return STATE_GROUND;
|
|
|
|
|
case 0x5d: action_osc_start(term, data); return STATE_OSC_STRING;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x5e ... 0x5f: return STATE_SOS_PM_APC_STRING;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x60 ... 0x7e: action_esc_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_ESCAPE;
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_escape_intermediate_switch(struct terminal *term, uint8_t data)
|
2019-12-20 19:16:52 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 19:16:52 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_ESCAPE_INTERMEDIATE;
|
2019-12-20 19:16:52 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_ESCAPE_INTERMEDIATE;
|
|
|
|
|
case 0x30 ... 0x7e: action_esc_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_ESCAPE_INTERMEDIATE;
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 19:16:52 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 20:57:38 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_csi_entry_switch(struct terminal *term, uint8_t data)
|
2019-12-20 20:57:38 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 20:57:38 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_CSI_ENTRY;
|
2019-12-20 20:57:38 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
|
|
|
|
case 0x30 ... 0x39: action_param(term, data); return STATE_CSI_PARAM;
|
2023-06-16 16:26:13 +02:00
|
|
|
case 0x3a: action_param_new_subparam(term, data); return STATE_CSI_PARAM;
|
|
|
|
|
case 0x3b: action_param_new(term, data); return STATE_CSI_PARAM;
|
|
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x3c ... 0x3f: action_collect(term, data); return STATE_CSI_PARAM;
|
|
|
|
|
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_CSI_ENTRY;
|
2019-12-20 20:57:38 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 20:57:38 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 21:04:47 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_csi_param_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:04:47 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:04:47 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_CSI_PARAM;
|
2019-12-20 21:04:47 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
2019-12-20 21:04:47 +01:00
|
|
|
|
2023-06-16 16:26:13 +02:00
|
|
|
case 0x30 ... 0x39: action_param(term, data); return STATE_CSI_PARAM;
|
|
|
|
|
case 0x3a: action_param_new_subparam(term, data); return STATE_CSI_PARAM;
|
|
|
|
|
case 0x3b: action_param_new(term, data); return STATE_CSI_PARAM;
|
2019-12-20 21:04:47 +01:00
|
|
|
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x3c ... 0x3f: return STATE_CSI_IGNORE;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_CSI_PARAM;
|
2019-12-20 21:04:47 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:04:47 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 21:09:00 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_csi_intermediate_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:09:00 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:09:00 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_CSI_INTERMEDIATE;
|
2019-12-20 21:09:00 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x30 ... 0x3f: return STATE_CSI_IGNORE;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_CSI_INTERMEDIATE;
|
2019-12-20 21:09:00 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:09:00 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 21:13:06 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_csi_ignore_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:13:06 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:13:06 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x1f: action_execute(term, data); return STATE_CSI_IGNORE;
|
2019-12-20 21:13:06 +01:00
|
|
|
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x20 ... 0x3f: action_ignore(term); return STATE_CSI_IGNORE;
|
|
|
|
|
case 0x40 ... 0x7e: return STATE_GROUND;
|
|
|
|
|
case 0x7f: action_ignore(term); return STATE_CSI_IGNORE;
|
2019-12-20 21:13:06 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:13:06 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 21:48:04 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_osc_string_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
|
|
|
|
|
/* Note: original was 20-7f, but I changed to 20-ff to include utf-8. Don't forget to add EXECUTE to 8-bit C1 if we implement that. */
|
2019-12-20 23:59:23 +01:00
|
|
|
default: action_osc_put(term, data); return STATE_OSC_STRING;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x07: action_osc_end(term, data); return STATE_GROUND;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
|
|
|
|
case 0x00 ... 0x06:
|
|
|
|
|
case 0x08 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1c ... 0x1f: action_ignore(term); return STATE_OSC_STRING;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
case 0x18:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1a: action_osc_end(term, data); action_execute(term, data); return STATE_GROUND;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1b: action_osc_end(term, data); action_clear(term); return STATE_ESCAPE;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_dcs_entry_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1c ... 0x1f: action_ignore(term); return STATE_DCS_ENTRY;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
|
|
|
|
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x3a: return STATE_DCS_IGNORE;
|
2023-06-16 16:26:13 +02:00
|
|
|
case 0x3b: action_param_new(term, data); return STATE_DCS_PARAM;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x3c ... 0x3f: action_collect(term, data); return STATE_DCS_PARAM;
|
|
|
|
|
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_DCS_ENTRY;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_dcs_param_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1c ... 0x1f: action_ignore(term); return STATE_DCS_PARAM;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
|
|
|
|
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x3a: return STATE_DCS_IGNORE;
|
2023-06-16 16:26:13 +02:00
|
|
|
case 0x3b: action_param_new(term, data); return STATE_DCS_PARAM;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x3c ... 0x3f: return STATE_DCS_IGNORE;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_DCS_PARAM;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_dcs_intermediate_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1c ... 0x1f: action_ignore(term); return STATE_DCS_INTERMEDIATE;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x30 ... 0x3f: return STATE_DCS_IGNORE;
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_DCS_INTERMEDIATE;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_dcs_ignore_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
|
|
|
|
case 0x1c ... 0x1f:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x20 ... 0x7f: action_ignore(term); return STATE_DCS_IGNORE;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_dcs_passthrough_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:48:04 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:48:04 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x1c ... 0x7e: action_put(term, data); return STATE_DCS_PASSTHROUGH;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x7f: action_ignore(term); return STATE_DCS_PASSTHROUGH;
|
2019-12-20 21:48:04 +01:00
|
|
|
|
|
|
|
|
/* Anywhere */
|
2019-12-20 23:59:23 +01:00
|
|
|
case 0x18: action_unhook(term, data); action_execute(term, data); return STATE_GROUND;
|
|
|
|
|
case 0x1a: action_unhook(term, data); action_execute(term, data); return STATE_GROUND;
|
|
|
|
|
case 0x1b: action_unhook(term, data); action_clear(term); return STATE_ESCAPE;
|
2021-05-25 19:14:06 +01:00
|
|
|
|
|
|
|
|
/* 8-bit C1 control characters (not supported) */
|
|
|
|
|
case 0x80 ... 0x9f: action_unhook(term, data); return STATE_GROUND;
|
2019-12-20 23:38:16 +01:00
|
|
|
|
|
|
|
|
default: return STATE_DCS_PASSTHROUGH;
|
2019-12-20 21:48:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 21:50:54 +01:00
|
|
|
static enum state
|
2019-12-20 23:59:23 +01:00
|
|
|
state_sos_pm_apc_string_switch(struct terminal *term, uint8_t data)
|
2019-12-20 21:50:54 +01:00
|
|
|
{
|
2019-12-20 23:59:23 +01:00
|
|
|
switch (data) {
|
2019-12-20 23:38:16 +01:00
|
|
|
/* exit current enter new state */
|
2019-12-20 21:50:54 +01:00
|
|
|
case 0x00 ... 0x17:
|
|
|
|
|
case 0x19:
|
2019-12-20 23:38:16 +01:00
|
|
|
case 0x1c ... 0x7f: action_ignore(term); return STATE_SOS_PM_APC_STRING;
|
2019-12-20 21:50:54 +01:00
|
|
|
}
|
2021-05-10 09:54:07 +01:00
|
|
|
|
2021-05-10 10:23:06 +01:00
|
|
|
return anywhere(term, data);
|
2019-12-20 21:50:54 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-20 22:10:27 +01:00
|
|
|
static enum state
|
2020-06-07 16:16:50 +02:00
|
|
|
state_utf8_21_switch(struct terminal *term, uint8_t data)
|
|
|
|
|
{
|
|
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_22(term, data); return STATE_GROUND;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
|
|
|
|
state_utf8_31_switch(struct terminal *term, uint8_t data)
|
2019-12-20 22:10:27 +01:00
|
|
|
{
|
2020-06-07 16:16:50 +02:00
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_32(term, data); return STATE_UTF8_32;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
2019-12-20 22:10:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2020-06-07 16:16:50 +02:00
|
|
|
state_utf8_32_switch(struct terminal *term, uint8_t data)
|
2019-12-20 22:10:27 +01:00
|
|
|
{
|
2020-06-07 16:16:50 +02:00
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_33(term, data); return STATE_GROUND;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
2019-12-20 22:10:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
2020-06-07 16:16:50 +02:00
|
|
|
state_utf8_41_switch(struct terminal *term, uint8_t data)
|
2019-12-20 22:10:27 +01:00
|
|
|
{
|
2020-06-07 16:16:50 +02:00
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_42(term, data); return STATE_UTF8_42;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
|
|
|
|
state_utf8_42_switch(struct terminal *term, uint8_t data)
|
|
|
|
|
{
|
|
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_43(term, data); return STATE_UTF8_43;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum state
|
|
|
|
|
state_utf8_43_switch(struct terminal *term, uint8_t data)
|
|
|
|
|
{
|
|
|
|
|
switch (data) {
|
|
|
|
|
/* exit current enter new state */
|
|
|
|
|
case 0x80 ... 0xbf: action_utf8_44(term, data); return STATE_GROUND;
|
2025-03-18 18:28:09 +01:00
|
|
|
default: action_utf8_print(term, 0xfffd); return state_ground_switch(term, data);
|
2020-06-07 16:16:50 +02:00
|
|
|
}
|
2019-12-20 22:10:27 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-03 08:56:47 +00:00
|
|
|
UNIGNORE_WARNINGS
|
2020-08-23 09:38:37 +02:00
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
void
|
|
|
|
|
vt_from_slave(struct terminal *term, const uint8_t *data, size_t len)
|
|
|
|
|
{
|
2019-12-20 23:00:07 +01:00
|
|
|
enum state current_state = term->vt.state;
|
|
|
|
|
|
|
|
|
|
const uint8_t *p = data;
|
2019-12-20 23:59:23 +01:00
|
|
|
for (size_t i = 0; i < len; i++, p++) {
|
|
|
|
|
switch (current_state) {
|
|
|
|
|
case STATE_GROUND: current_state = state_ground_switch(term, *p); break;
|
|
|
|
|
case STATE_ESCAPE: current_state = state_escape_switch(term, *p); break;
|
|
|
|
|
case STATE_ESCAPE_INTERMEDIATE: current_state = state_escape_intermediate_switch(term, *p); break;
|
|
|
|
|
case STATE_CSI_ENTRY: current_state = state_csi_entry_switch(term, *p); break;
|
|
|
|
|
case STATE_CSI_PARAM: current_state = state_csi_param_switch(term, *p); break;
|
|
|
|
|
case STATE_CSI_INTERMEDIATE: current_state = state_csi_intermediate_switch(term, *p); break;
|
|
|
|
|
case STATE_CSI_IGNORE: current_state = state_csi_ignore_switch(term, *p); break;
|
|
|
|
|
case STATE_OSC_STRING: current_state = state_osc_string_switch(term, *p); break;
|
|
|
|
|
case STATE_DCS_ENTRY: current_state = state_dcs_entry_switch(term, *p); break;
|
|
|
|
|
case STATE_DCS_PARAM: current_state = state_dcs_param_switch(term, *p); break;
|
|
|
|
|
case STATE_DCS_INTERMEDIATE: current_state = state_dcs_intermediate_switch(term, *p); break;
|
|
|
|
|
case STATE_DCS_IGNORE: current_state = state_dcs_ignore_switch(term, *p); break;
|
|
|
|
|
case STATE_DCS_PASSTHROUGH: current_state = state_dcs_passthrough_switch(term, *p); break;
|
|
|
|
|
case STATE_SOS_PM_APC_STRING: current_state = state_sos_pm_apc_string_switch(term, *p); break;
|
|
|
|
|
|
2020-06-07 16:16:50 +02:00
|
|
|
case STATE_UTF8_21: current_state = state_utf8_21_switch(term, *p); break;
|
|
|
|
|
case STATE_UTF8_31: current_state = state_utf8_31_switch(term, *p); break;
|
|
|
|
|
case STATE_UTF8_32: current_state = state_utf8_32_switch(term, *p); break;
|
|
|
|
|
case STATE_UTF8_41: current_state = state_utf8_41_switch(term, *p); break;
|
|
|
|
|
case STATE_UTF8_42: current_state = state_utf8_42_switch(term, *p); break;
|
|
|
|
|
case STATE_UTF8_43: current_state = state_utf8_43_switch(term, *p); break;
|
2019-12-20 23:59:23 +01:00
|
|
|
}
|
2019-12-20 22:12:35 +01:00
|
|
|
|
2021-05-15 19:20:36 +01:00
|
|
|
term->vt.state = current_state;
|
|
|
|
|
}
|
2019-06-15 22:22:44 +02:00
|
|
|
}
|