2019-06-15 22:22:44 +02:00
|
|
|
|
#include "csi.h"
|
|
|
|
|
|
|
2019-06-17 18:57:12 +02:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <unistd.h>
|
2020-08-18 07:00:26 +02:00
|
|
|
|
#include <errno.h>
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-06-18 21:55:39 +02:00
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-08-18 07:00:26 +02:00
|
|
|
|
#include <sys/timerfd.h>
|
|
|
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
|
#define LOG_MODULE "csi"
|
2019-07-03 20:21:03 +02:00
|
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-15 22:22:44 +02:00
|
|
|
|
#include "log.h"
|
2020-11-26 18:08:28 +01:00
|
|
|
|
#include "config.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
|
#include "debug.h"
|
2019-06-17 19:33:10 +02:00
|
|
|
|
#include "grid.h"
|
2019-07-11 10:00:59 +02:00
|
|
|
|
#include "selection.h"
|
2020-02-22 14:02:00 +01:00
|
|
|
|
#include "sixel.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
|
#include "util.h"
|
2020-07-24 17:47:47 +02:00
|
|
|
|
#include "version.h"
|
|
|
|
|
|
#include "vt.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
|
#include "xmalloc.h"
|
2021-01-14 21:30:06 +00:00
|
|
|
|
#include "xsnprintf.h"
|
2019-06-22 21:31:28 +02:00
|
|
|
|
|
2020-02-01 19:42:31 +01:00
|
|
|
|
#define UNHANDLED() LOG_DBG("unhandled: %s", csi_as_string(term, final, -1))
|
|
|
|
|
|
#define UNHANDLED_SGR(idx) LOG_DBG("unhandled: %s", csi_as_string(term, 'm', idx))
|
2019-07-30 21:42:46 +02:00
|
|
|
|
|
2019-06-22 21:38:47 +02:00
|
|
|
|
static void
|
|
|
|
|
|
sgr_reset(struct terminal *term)
|
|
|
|
|
|
{
|
2019-06-26 19:44:31 +02:00
|
|
|
|
memset(&term->vt.attrs, 0, sizeof(term->vt.attrs));
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.fg = term->colors.fg;
|
|
|
|
|
|
term->vt.attrs.bg = term->colors.bg;
|
2019-06-22 21:38:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-10 18:45:12 +02:00
|
|
|
|
static const char *
|
2020-02-01 19:42:31 +01:00
|
|
|
|
csi_as_string(struct terminal *term, uint8_t final, int idx)
|
2019-07-10 18:45:12 +02:00
|
|
|
|
{
|
|
|
|
|
|
static char msg[1024];
|
|
|
|
|
|
int c = snprintf(msg, sizeof(msg), "CSI: ");
|
|
|
|
|
|
|
2020-02-01 19:42:31 +01:00
|
|
|
|
for (size_t i = idx >= 0 ? idx : 0;
|
|
|
|
|
|
i < (idx >= 0 ? idx + 1 : term->vt.params.idx);
|
|
|
|
|
|
i++)
|
|
|
|
|
|
{
|
2020-02-01 19:24:46 +01:00
|
|
|
|
c += snprintf(&msg[c], sizeof(msg) - c, "%u",
|
2019-07-10 18:45:12 +02:00
|
|
|
|
term->vt.params.v[i].value);
|
|
|
|
|
|
|
2019-07-18 14:13:35 +02:00
|
|
|
|
for (size_t j = 0; j < term->vt.params.v[i].sub.idx; j++) {
|
2020-02-01 19:24:46 +01:00
|
|
|
|
c += snprintf(&msg[c], sizeof(msg) - c, ":%u",
|
2019-07-10 18:45:12 +02:00
|
|
|
|
term->vt.params.v[i].sub.value[j]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c += snprintf(&msg[c], sizeof(msg) - c, "%s",
|
|
|
|
|
|
i == term->vt.params.idx - 1 ? "" : ";");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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)
|
2020-06-30 17:42:57 +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);
|
2020-06-30 17:42:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-12 18:20:52 +02:00
|
|
|
|
snprintf(&msg[c], sizeof(msg) - c, "%c (%u parameters)",
|
2020-02-01 19:42:31 +01:00
|
|
|
|
final, idx >= 0 ? 1 : term->vt.params.idx);
|
2019-07-10 18:45:12 +02:00
|
|
|
|
return msg;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-07 16:32:18 +02:00
|
|
|
|
static void
|
2019-06-15 22:22:44 +02:00
|
|
|
|
csi_sgr(struct terminal *term)
|
|
|
|
|
|
{
|
2019-06-22 21:38:47 +02:00
|
|
|
|
if (term->vt.params.idx == 0) {
|
|
|
|
|
|
sgr_reset(term);
|
2019-07-07 16:32:18 +02:00
|
|
|
|
return;
|
2019-06-22 21:38:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
|
for (size_t i = 0; i < term->vt.params.idx; i++) {
|
2019-06-23 15:00:27 +02:00
|
|
|
|
const int param = term->vt.params.v[i].value;
|
|
|
|
|
|
|
|
|
|
|
|
switch (param) {
|
2019-06-15 22:22:44 +02:00
|
|
|
|
case 0:
|
2019-06-22 21:38:47 +02:00
|
|
|
|
sgr_reset(term);
|
2019-06-15 22:22:44 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-06-16 16:44:42 +02:00
|
|
|
|
case 1: term->vt.attrs.bold = true; break;
|
2019-07-16 13:25:45 +02:00
|
|
|
|
case 2: term->vt.attrs.dim = true; break;
|
2019-06-16 16:44:42 +02:00
|
|
|
|
case 3: term->vt.attrs.italic = true; break;
|
|
|
|
|
|
case 4: term->vt.attrs.underline = true; break;
|
2019-07-16 13:19:17 +02:00
|
|
|
|
case 5: term->vt.attrs.blink = true; break;
|
2019-08-04 18:11:59 +02:00
|
|
|
|
case 6: LOG_WARN("ignored: rapid blink"); break;
|
2019-06-16 16:44:42 +02:00
|
|
|
|
case 7: term->vt.attrs.reverse = true; break;
|
|
|
|
|
|
case 8: term->vt.attrs.conceal = true; break;
|
|
|
|
|
|
case 9: term->vt.attrs.strikethrough = true; break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
|
2020-12-14 19:10:30 +01:00
|
|
|
|
case 21: break; /* double-underline, not implemented */
|
2019-07-16 13:25:45 +02:00
|
|
|
|
case 22: term->vt.attrs.bold = term->vt.attrs.dim = false; break;
|
2019-06-16 16:44:42 +02:00
|
|
|
|
case 23: term->vt.attrs.italic = false; break;
|
|
|
|
|
|
case 24: term->vt.attrs.underline = false; break;
|
2019-07-16 13:19:17 +02:00
|
|
|
|
case 25: term->vt.attrs.blink = false; break;
|
2019-07-10 18:45:12 +02:00
|
|
|
|
case 26: break; /* rapid blink, ignored */
|
2019-06-16 16:44:42 +02:00
|
|
|
|
case 27: term->vt.attrs.reverse = false; break;
|
|
|
|
|
|
case 28: term->vt.attrs.conceal = false; break;
|
|
|
|
|
|
case 29: term->vt.attrs.strikethrough = false; break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
|
2019-06-19 11:12:45 +02:00
|
|
|
|
/* Regular foreground colors */
|
2019-06-23 15:00:27 +02:00
|
|
|
|
case 30:
|
|
|
|
|
|
case 31:
|
|
|
|
|
|
case 32:
|
|
|
|
|
|
case 33:
|
|
|
|
|
|
case 34:
|
|
|
|
|
|
case 35:
|
|
|
|
|
|
case 36:
|
|
|
|
|
|
case 37:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_fg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.fg = term->colors.table[param - 30];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 38: {
|
2019-09-04 20:28:35 +02:00
|
|
|
|
/* Indexed: 38;5;<idx> */
|
2019-07-04 09:57:02 +02:00
|
|
|
|
if (term->vt.params.idx - i - 1 >= 2 &&
|
2019-06-23 15:00:27 +02:00
|
|
|
|
term->vt.params.v[i + 1].value == 5)
|
|
|
|
|
|
{
|
2019-07-18 14:25:15 +02:00
|
|
|
|
uint8_t idx = term->vt.params.v[i + 2].value;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_fg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.fg = term->colors.table[idx];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
i += 2;
|
2019-07-15 12:35:43 +02:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-04 20:28:35 +02:00
|
|
|
|
/* RGB: 38;2;<r>;<g>;<b> */
|
2019-07-15 12:35:43 +02:00
|
|
|
|
else if (term->vt.params.idx - i - 1 >= 4 &&
|
|
|
|
|
|
term->vt.params.v[i + 1].value == 2)
|
2019-06-23 15:00:27 +02:00
|
|
|
|
{
|
2019-06-26 20:33:32 +02:00
|
|
|
|
uint8_t r = term->vt.params.v[i + 2].value;
|
|
|
|
|
|
uint8_t g = term->vt.params.v[i + 3].value;
|
|
|
|
|
|
uint8_t b = term->vt.params.v[i + 4].value;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_fg = 1;
|
|
|
|
|
|
term->vt.attrs.fg = r << 16 | g << 8 | b;
|
2019-06-23 15:00:27 +02:00
|
|
|
|
i += 4;
|
2019-07-15 12:35:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 20:50:43 +01:00
|
|
|
|
/* Indexed: 38:5:<idx> */
|
2019-09-04 20:28:35 +02:00
|
|
|
|
else if (term->vt.params.v[i].sub.idx >= 2 &&
|
2020-12-12 20:50:43 +01:00
|
|
|
|
term->vt.params.v[i].sub.value[0] == 5)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct vt_param *param = &term->vt.params.v[i];
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t idx = param->sub.value[1];
|
|
|
|
|
|
term->vt.attrs.have_fg = 1;
|
|
|
|
|
|
term->vt.attrs.fg = term->colors.table[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* RGB: 38:2:<color-space>:r:g:b[:ignored:tolerance:tolerance-color-space]
|
|
|
|
|
|
* RGB: 38:2:r:g:b
|
|
|
|
|
|
*
|
|
|
|
|
|
* The second version is a "bastard" version - many
|
|
|
|
|
|
* programs "forget" the color space ID
|
|
|
|
|
|
* parameter... *sigh*
|
|
|
|
|
|
*/
|
|
|
|
|
|
else if (term->vt.params.v[i].sub.idx >= 4 &&
|
2019-09-04 20:28:35 +02:00
|
|
|
|
term->vt.params.v[i].sub.value[0] == 2)
|
|
|
|
|
|
{
|
2019-07-15 12:35:43 +02:00
|
|
|
|
const struct vt_param *param = &term->vt.params.v[i];
|
2020-12-12 20:50:43 +01:00
|
|
|
|
bool have_color_space_id = param->sub.idx >= 5;
|
|
|
|
|
|
|
|
|
|
|
|
/* 0 - color space (ignored) */
|
|
|
|
|
|
int r_idx = 2 - !have_color_space_id;
|
|
|
|
|
|
int g_idx = 3 - !have_color_space_id;
|
|
|
|
|
|
int b_idx = 4 - !have_color_space_id;
|
|
|
|
|
|
/* 5 - unused */
|
|
|
|
|
|
/* 6 - CS tolerance */
|
|
|
|
|
|
/* 7 - color space associated with tolerance */
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t r = param->sub.value[r_idx];
|
|
|
|
|
|
uint8_t g = param->sub.value[g_idx];
|
|
|
|
|
|
uint8_t b = param->sub.value[b_idx];
|
|
|
|
|
|
|
|
|
|
|
|
term->vt.attrs.have_fg = 1;
|
|
|
|
|
|
term->vt.attrs.fg = r << 16 | g << 8 | b;
|
2019-07-15 12:35:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 20:50:43 +01:00
|
|
|
|
/* Transparent: 38:1 */
|
|
|
|
|
|
/* CMY: 38:3:<color-space>:c:m:y[:tolerance:tolerance-color-space] */
|
|
|
|
|
|
/* CMYK: 38:4:<color-space>:c:m:y:k[:tolerance:tolerance-color-space] */
|
|
|
|
|
|
|
2019-09-04 20:28:35 +02:00
|
|
|
|
/* Unrecognized */
|
2019-07-30 21:42:46 +02:00
|
|
|
|
else
|
2020-02-01 19:42:31 +01:00
|
|
|
|
UNHANDLED_SGR(i);
|
2019-07-30 21:42:46 +02:00
|
|
|
|
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-07-15 12:35:43 +02:00
|
|
|
|
|
2019-06-26 19:44:31 +02:00
|
|
|
|
case 39:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_fg = 0;
|
2019-06-26 19:44:31 +02:00
|
|
|
|
break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
|
2019-06-19 11:12:45 +02:00
|
|
|
|
/* Regular background colors */
|
2019-06-23 15:00:27 +02:00
|
|
|
|
case 40:
|
|
|
|
|
|
case 41:
|
|
|
|
|
|
case 42:
|
|
|
|
|
|
case 43:
|
|
|
|
|
|
case 44:
|
|
|
|
|
|
case 45:
|
|
|
|
|
|
case 46:
|
|
|
|
|
|
case 47:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_bg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.bg = term->colors.table[param - 40];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 48: {
|
2019-09-04 20:28:35 +02:00
|
|
|
|
/* Indexed: 48;5;<idx> */
|
2019-07-04 09:57:02 +02:00
|
|
|
|
if (term->vt.params.idx - i - 1 >= 2 &&
|
2019-06-23 15:00:27 +02:00
|
|
|
|
term->vt.params.v[i + 1].value == 5)
|
|
|
|
|
|
{
|
2019-07-18 14:25:15 +02:00
|
|
|
|
uint8_t idx = term->vt.params.v[i + 2].value;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_bg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.bg = term->colors.table[idx];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
i += 2;
|
2019-09-04 20:28:35 +02:00
|
|
|
|
|
2019-07-15 12:35:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-04 20:28:35 +02:00
|
|
|
|
/* RGB: 48;2;<r>;<g>;<b> */
|
2019-07-15 12:35:43 +02:00
|
|
|
|
else if (term->vt.params.idx - i - 1 >= 4 &&
|
2019-09-04 20:28:35 +02:00
|
|
|
|
term->vt.params.v[i + 1].value == 2)
|
2019-06-23 15:00:27 +02:00
|
|
|
|
{
|
2019-06-26 20:33:32 +02:00
|
|
|
|
uint8_t r = term->vt.params.v[i + 2].value;
|
|
|
|
|
|
uint8_t g = term->vt.params.v[i + 3].value;
|
|
|
|
|
|
uint8_t b = term->vt.params.v[i + 4].value;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_bg = 1;
|
|
|
|
|
|
term->vt.attrs.bg = r << 16 | g << 8 | b;
|
2019-06-23 15:00:27 +02:00
|
|
|
|
i += 4;
|
2019-07-15 12:35:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 20:50:43 +01:00
|
|
|
|
/* Indexed: 48:5:<idx> */
|
2019-09-04 20:28:35 +02:00
|
|
|
|
else if (term->vt.params.v[i].sub.idx >= 2 &&
|
2020-12-12 20:50:43 +01:00
|
|
|
|
term->vt.params.v[i].sub.value[0] == 5)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct vt_param *param = &term->vt.params.v[i];
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t idx = param->sub.value[1];
|
|
|
|
|
|
term->vt.attrs.have_bg = 1;
|
|
|
|
|
|
term->vt.attrs.bg = term->colors.table[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* RGB: 48:2:<color-space>:r:g:b[:ignored:tolerance:tolerance-color-space]
|
|
|
|
|
|
* RGB: 48:2:r:g:b
|
|
|
|
|
|
*
|
|
|
|
|
|
* The second version is a "bastard" version - many
|
|
|
|
|
|
* programs "forget" the color space ID
|
|
|
|
|
|
* parameter... *sigh*
|
|
|
|
|
|
*/
|
|
|
|
|
|
else if (term->vt.params.v[i].sub.idx >= 4 &&
|
2019-09-04 20:28:35 +02:00
|
|
|
|
term->vt.params.v[i].sub.value[0] == 2)
|
|
|
|
|
|
{
|
2019-07-15 12:35:43 +02:00
|
|
|
|
const struct vt_param *param = &term->vt.params.v[i];
|
2020-12-12 20:50:43 +01:00
|
|
|
|
bool have_color_space_id = param->sub.idx >= 5;
|
|
|
|
|
|
|
|
|
|
|
|
/* 0 - color space (ignored) */
|
|
|
|
|
|
int r_idx = 2 - !have_color_space_id;
|
|
|
|
|
|
int g_idx = 3 - !have_color_space_id;
|
|
|
|
|
|
int b_idx = 4 - !have_color_space_id;
|
|
|
|
|
|
/* 5 - unused */
|
|
|
|
|
|
/* 6 - CS tolerance */
|
|
|
|
|
|
/* 7 - color space associated with tolerance */
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t r = param->sub.value[r_idx];
|
|
|
|
|
|
uint8_t g = param->sub.value[g_idx];
|
|
|
|
|
|
uint8_t b = param->sub.value[b_idx];
|
|
|
|
|
|
|
|
|
|
|
|
term->vt.attrs.have_bg = 1;
|
|
|
|
|
|
term->vt.attrs.bg = r << 16 | g << 8 | b;
|
2019-07-15 12:35:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 20:50:43 +01:00
|
|
|
|
/* Transparent: 48:1 */
|
|
|
|
|
|
/* CMY: 48:3:<color-space>:c:m:y[:tolerance:tolerance-color-space] */
|
|
|
|
|
|
/* CMYK: 48:4:<color-space>:c:m:y:k[:tolerance:tolerance-color-space] */
|
|
|
|
|
|
|
2019-07-30 21:42:46 +02:00
|
|
|
|
else
|
2020-02-01 19:42:31 +01:00
|
|
|
|
UNHANDLED_SGR(i);
|
2019-07-30 21:42:46 +02:00
|
|
|
|
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-06-26 19:44:31 +02:00
|
|
|
|
case 49:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_bg = 0;
|
2019-06-26 19:44:31 +02:00
|
|
|
|
break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
|
2019-06-19 11:12:45 +02:00
|
|
|
|
/* Bright foreground colors */
|
2019-06-23 15:00:27 +02:00
|
|
|
|
case 90:
|
|
|
|
|
|
case 91:
|
|
|
|
|
|
case 92:
|
|
|
|
|
|
case 93:
|
|
|
|
|
|
case 94:
|
|
|
|
|
|
case 95:
|
|
|
|
|
|
case 96:
|
|
|
|
|
|
case 97:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_fg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.fg = term->colors.table[param - 90 + 8];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
2019-06-19 11:12:45 +02:00
|
|
|
|
|
2019-08-21 18:47:48 +02:00
|
|
|
|
/* Bright background colors */
|
2019-06-23 15:00:27 +02:00
|
|
|
|
case 100:
|
|
|
|
|
|
case 101:
|
|
|
|
|
|
case 102:
|
|
|
|
|
|
case 103:
|
|
|
|
|
|
case 104:
|
|
|
|
|
|
case 105:
|
|
|
|
|
|
case 106:
|
|
|
|
|
|
case 107:
|
2019-08-02 18:19:07 +02:00
|
|
|
|
term->vt.attrs.have_bg = 1;
|
2019-08-21 18:50:24 +02:00
|
|
|
|
term->vt.attrs.bg = term->colors.table[param - 100 + 8];
|
2019-06-23 15:00:27 +02:00
|
|
|
|
break;
|
2019-06-19 11:12:45 +02:00
|
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
|
default:
|
2020-02-01 19:42:31 +01:00
|
|
|
|
UNHANDLED_SGR(i);
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
static void
|
|
|
|
|
|
decset_decrst(struct terminal *term, unsigned param, bool enable)
|
|
|
|
|
|
{
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
/* For UNHANDLED() */
|
|
|
|
|
|
int UNUSED final = enable ? 'h' : 'l';
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-08-16 16:38:57 +02:00
|
|
|
|
/* Note: update XTSAVE/XTRESTORE if adding/removing things here */
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
switch (param) {
|
|
|
|
|
|
case 1:
|
2020-08-16 16:25:52 +02:00
|
|
|
|
/* DECCKM */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->cursor_keys_mode =
|
|
|
|
|
|
enable ? CURSOR_KEYS_APPLICATION : CURSOR_KEYS_NORMAL;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 3:
|
2020-08-16 16:25:52 +02:00
|
|
|
|
/* DECCOLM */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
if (enable)
|
|
|
|
|
|
LOG_WARN("unimplemented: 132 column mode (DECCOLM)");
|
|
|
|
|
|
|
|
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
|
|
|
|
|
&(struct coord){0, 0},
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->rows - 1});
|
|
|
|
|
|
term_cursor_home(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
/* DECSCLM - Smooth scroll */
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
LOG_WARN("unimplemented: Smooth (Slow) Scroll (DECSCLM)");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 5:
|
2020-08-16 16:25:52 +02:00
|
|
|
|
/* DECSCNM */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->reverse = enable;
|
|
|
|
|
|
term_damage_all(term);
|
2020-10-08 19:55:32 +02:00
|
|
|
|
term_damage_margins(term);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-16 16:25:52 +02:00
|
|
|
|
case 6: {
|
|
|
|
|
|
/* DECOM */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->origin = enable ? ORIGIN_RELATIVE : ORIGIN_ABSOLUTE;
|
|
|
|
|
|
term_cursor_home(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
|
|
/* DECAWM */
|
|
|
|
|
|
term->auto_margin = enable;
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 9:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
LOG_WARN("unimplemented: X10 mouse tracking mode");
|
2020-08-18 06:53:47 +02:00
|
|
|
|
#if 0
|
|
|
|
|
|
else if (term->mouse_tracking == MOUSE_X10)
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->mouse_tracking = MOUSE_NONE;
|
2020-08-18 06:53:47 +02:00
|
|
|
|
#endif
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 12:
|
term: split cursor blink state into two
There are two different escape sequences that can be used to set the
cursor blink state: ‘CSI ? 12 h/l’ and ‘CSI Ps SP q’.
Up until now, they both modified the same internal state in foot. This
meant you could enable a blinking cursor with e.g. ‘CSI ? 12 h’ and
then disable it with ‘CSI 2 SP q’.
Since the ‘CSI ? 12’ escapes are used in the civis/cnorm/cvvis
terminfo entries, applications often ended up disabling the blink
state on exit (typically be emitting ‘cnorm’), requiring users to
manually re-enable blinking.
By splitting the internal state into two separate states, we can
improve the situation.
The cursor will blink if at least one of the two have been enabled.
The setting in foot.ini sets the default state of the ‘CSI Ps SP q’
escape.
This means if the user has enabled blinking in the configuration, the
cursor will blink regardless of civis/cnorm/cvvis. Which probably is
what the user wants.
If the user has NOT enabled blinking, civis/cnorm/cvvis act as
intended: cvvis blink, civis and cnorm do not.
If an application overrides the cursor blink/style with ‘CSI Ps SP q’,
that will override the user’s setting in foot.ini. But most likely
that too is intended (for example, the user may have configured the
application to use a different cursor style). And, a well written
application will emit the ‘Se’ terminfo sequence on exit, which in
foot is defined to ‘CSI SP q’, which will reset both the style and
blink state to the user configured style/state.
Closes #218
2020-11-26 18:09:32 +01:00
|
|
|
|
term->cursor_blink.decset = enable;
|
|
|
|
|
|
term_cursor_blink_update(term);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 25:
|
2020-08-16 16:25:52 +02:00
|
|
|
|
/* DECTCEM */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->hide_cursor = !enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-10-01 20:15:32 +02:00
|
|
|
|
case 45:
|
|
|
|
|
|
term->reverse_wrap = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
case 80:
|
2021-07-14 19:17:44 +02:00
|
|
|
|
term->sixel.scrolling = !enable;
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 1000:
|
2020-08-18 06:53:47 +02:00
|
|
|
|
if (enable)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_CLICK;
|
|
|
|
|
|
else if (term->mouse_tracking == MOUSE_CLICK)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_NONE;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_xcursor_update(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1001:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
LOG_WARN("unimplemented: highlight mouse tracking");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1002:
|
2020-08-18 06:53:47 +02:00
|
|
|
|
if (enable)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_DRAG;
|
|
|
|
|
|
else if (term->mouse_tracking == MOUSE_DRAG)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_NONE;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_xcursor_update(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1003:
|
2020-08-18 06:53:47 +02:00
|
|
|
|
if (enable)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_MOTION;
|
|
|
|
|
|
else if (term->mouse_tracking == MOUSE_MOTION)
|
|
|
|
|
|
term->mouse_tracking = MOUSE_NONE;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_xcursor_update(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1004:
|
|
|
|
|
|
term->focus_events = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1005:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
LOG_WARN("unimplemented: mouse reporting mode: UTF-8");
|
2020-08-18 06:53:47 +02:00
|
|
|
|
#if 0
|
|
|
|
|
|
else if (term->mouse_reporting == MOUSE_UTF8)
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->mouse_reporting = MOUSE_NONE;
|
2020-08-18 06:53:47 +02:00
|
|
|
|
#endif
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1006:
|
|
|
|
|
|
if (enable)
|
2020-08-18 06:53:47 +02:00
|
|
|
|
term->mouse_reporting = MOUSE_SGR;
|
|
|
|
|
|
else if (term->mouse_reporting == MOUSE_SGR)
|
|
|
|
|
|
term->mouse_reporting = MOUSE_NORMAL;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1007:
|
|
|
|
|
|
term->alt_scrolling = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1015:
|
|
|
|
|
|
if (enable)
|
2020-08-18 06:53:47 +02:00
|
|
|
|
term->mouse_reporting = MOUSE_URXVT;
|
|
|
|
|
|
else if (term->mouse_reporting == MOUSE_URXVT)
|
|
|
|
|
|
term->mouse_reporting = MOUSE_NORMAL;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1034:
|
|
|
|
|
|
/* smm */
|
|
|
|
|
|
LOG_DBG("%s 8-bit meta mode", enable ? "enabling" : "disabling");
|
|
|
|
|
|
term->meta.eight_bit = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-11-11 18:26:47 +01:00
|
|
|
|
case 1035:
|
|
|
|
|
|
/* numLock */
|
|
|
|
|
|
LOG_DBG("%s Num Lock modifier", enable ? "enabling" : "disabling");
|
|
|
|
|
|
term->num_lock_modifier = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 1036:
|
|
|
|
|
|
/* metaSendsEscape */
|
|
|
|
|
|
LOG_DBG("%s meta-sends-escape", enable ? "enabling" : "disabling");
|
|
|
|
|
|
term->meta.esc_prefix = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1042:
|
2020-12-10 18:22:48 +01:00
|
|
|
|
term->bell_action_enabled = enable;
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-10-11 17:44:29 +02:00
|
|
|
|
#if 0
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 1043:
|
|
|
|
|
|
LOG_WARN("unimplemented: raise window on ctrl-g");
|
|
|
|
|
|
break;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
case 1048:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
term_save_cursor(term);
|
|
|
|
|
|
else
|
|
|
|
|
|
term_restore_cursor(term, &term->grid->saved_cursor);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2021-01-16 13:34:40 +01:00
|
|
|
|
case 47:
|
2021-01-15 17:09:15 +01:00
|
|
|
|
case 1047:
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 1049:
|
|
|
|
|
|
if (enable && term->grid != &term->alt) {
|
|
|
|
|
|
selection_cancel(term);
|
|
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
if (param == 1049)
|
|
|
|
|
|
term_save_cursor(term);
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term->grid = &term->alt;
|
|
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
/* Cursor retains its position from the normal grid */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_cursor_to(
|
|
|
|
|
|
term,
|
2021-01-15 17:09:15 +01:00
|
|
|
|
min(term->normal.cursor.point.row, term->rows - 1),
|
|
|
|
|
|
min(term->normal.cursor.point.col, term->cols - 1));
|
2020-08-16 16:20:47 +02:00
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
tll_free(term->normal.scroll_damage);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
|
|
|
|
|
&(struct coord){0, 0},
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->rows - 1});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (!enable && term->grid == &term->alt) {
|
|
|
|
|
|
selection_cancel(term);
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = &term->normal;
|
|
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
/* Cursor retains its position from the alt grid */
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_cursor_to(
|
2021-01-15 17:09:15 +01:00
|
|
|
|
term, min(term->alt.cursor.point.row, term->rows - 1),
|
|
|
|
|
|
min(term->alt.cursor.point.col, term->cols - 1));
|
2020-08-16 16:20:47 +02:00
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
if (param == 1049)
|
|
|
|
|
|
term_restore_cursor(term, &term->grid->saved_cursor);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
|
|
|
|
|
|
/* Delete all sixel images on the alt screen */
|
|
|
|
|
|
tll_foreach(term->alt.sixel_images, it) {
|
|
|
|
|
|
sixel_destroy(&it->item);
|
|
|
|
|
|
tll_remove(term->alt.sixel_images, it);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-15 17:09:15 +01:00
|
|
|
|
tll_free(term->alt.scroll_damage);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
term_damage_all(term);
|
|
|
|
|
|
}
|
2021-03-16 13:07:09 +01:00
|
|
|
|
term_update_ascii_printer(term);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2021-02-16 19:37:49 +01:00
|
|
|
|
case 1070:
|
|
|
|
|
|
term->sixel.use_private_palette = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 2004:
|
|
|
|
|
|
term->bracketed_paste = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2021-04-25 18:35:46 +01:00
|
|
|
|
case 2026:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
term_enable_app_sync_updates(term);
|
|
|
|
|
|
else
|
|
|
|
|
|
term_disable_app_sync_updates(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2021-02-16 19:11:38 +01:00
|
|
|
|
case 8452:
|
|
|
|
|
|
term->sixel.cursor_right_of_graphics = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-11-29 04:04:57 +00:00
|
|
|
|
case 27127:
|
|
|
|
|
|
term->modify_escape_key = enable;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-12-04 21:26:38 +01:00
|
|
|
|
case 737769:
|
|
|
|
|
|
if (enable)
|
|
|
|
|
|
term_ime_enable(term);
|
|
|
|
|
|
else
|
|
|
|
|
|
term_ime_disable(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
decset(struct terminal *term, unsigned param)
|
|
|
|
|
|
{
|
|
|
|
|
|
decset_decrst(term, param, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
decrst(struct terminal *term, unsigned param)
|
|
|
|
|
|
{
|
2020-08-16 16:38:30 +02:00
|
|
|
|
decset_decrst(term, param, false);
|
2020-08-16 16:20:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-16 13:57:40 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
decrqm(const struct terminal *term, unsigned param, bool *enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (param) {
|
|
|
|
|
|
case 1: *enabled = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; return true;
|
|
|
|
|
|
case 3: *enabled = false; return true;
|
|
|
|
|
|
case 4: *enabled = false; return true;
|
|
|
|
|
|
case 5: *enabled = term->reverse; return true;
|
|
|
|
|
|
case 6: *enabled = term->origin; return true;
|
|
|
|
|
|
case 7: *enabled = term->auto_margin; return true;
|
|
|
|
|
|
case 9: *enabled = false; /* term->mouse_tracking == MOUSE_X10; */ return true;
|
|
|
|
|
|
case 12: *enabled = term->cursor_blink.decset; return true;
|
|
|
|
|
|
case 25: *enabled = !term->hide_cursor; return true;
|
|
|
|
|
|
case 45: *enabled = term->reverse_wrap; return true;
|
2021-07-14 19:17:44 +02:00
|
|
|
|
case 80: *enabled = !term->sixel.scrolling; return true;
|
2020-12-16 13:57:40 +01:00
|
|
|
|
case 1000: *enabled = term->mouse_tracking == MOUSE_CLICK; return true;
|
|
|
|
|
|
case 1001: *enabled = false; return true;
|
|
|
|
|
|
case 1002: *enabled = term->mouse_tracking == MOUSE_DRAG; return true;
|
|
|
|
|
|
case 1003: *enabled = term->mouse_tracking == MOUSE_MOTION; return true;
|
|
|
|
|
|
case 1004: *enabled = term->focus_events; return true;
|
|
|
|
|
|
case 1005: *enabled = false; /* term->mouse_reporting == MOUSE_UTF8; */ return true;
|
|
|
|
|
|
case 1006: *enabled = term->mouse_reporting == MOUSE_SGR; return true;
|
|
|
|
|
|
case 1007: *enabled = term->alt_scrolling; return true;
|
|
|
|
|
|
case 1015: *enabled = term->mouse_reporting == MOUSE_URXVT; return true;
|
|
|
|
|
|
case 1034: *enabled = term->meta.eight_bit; return true;
|
|
|
|
|
|
case 1035: *enabled = term->num_lock_modifier; return true;
|
|
|
|
|
|
case 1036: *enabled = term->meta.esc_prefix; return true;
|
|
|
|
|
|
case 1042: *enabled = term->bell_action_enabled; return true;
|
2021-02-09 20:02:29 +01:00
|
|
|
|
case 47: /* FALLTHROUGH */
|
|
|
|
|
|
case 1047: /* FALLTHROUGH */
|
2020-12-16 13:57:40 +01:00
|
|
|
|
case 1049: *enabled = term->grid == &term->alt; return true;
|
2021-02-16 19:37:49 +01:00
|
|
|
|
case 1079: *enabled = term->sixel.use_private_palette; return true;
|
2020-12-16 13:57:40 +01:00
|
|
|
|
case 2004: *enabled = term->bracketed_paste; return true;
|
2021-04-25 18:35:46 +01:00
|
|
|
|
case 2026: *enabled = term->render.app_sync_updates.enabled; return true;
|
2021-02-16 19:11:38 +01:00
|
|
|
|
case 8452: *enabled = term->sixel.cursor_right_of_graphics; return true;
|
2020-12-16 13:57:40 +01:00
|
|
|
|
case 27127: *enabled = term->modify_escape_key; return true;
|
|
|
|
|
|
case 737769: *enabled = term_ime_is_enabled(term); return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-16 16:38:57 +02:00
|
|
|
|
static void
|
|
|
|
|
|
xtsave(struct terminal *term, unsigned param)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (param) {
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1: term->xtsave.application_cursor_keys = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 3: break;
|
|
|
|
|
|
case 4: break;
|
|
|
|
|
|
case 5: term->xtsave.reverse = term->reverse; break;
|
|
|
|
|
|
case 6: term->xtsave.origin = term->origin; break;
|
|
|
|
|
|
case 7: term->xtsave.auto_margin = term->auto_margin; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 9: /* term->xtsave.mouse_x10 = term->mouse_tracking == MOUSE_X10; */ break;
|
term: split cursor blink state into two
There are two different escape sequences that can be used to set the
cursor blink state: ‘CSI ? 12 h/l’ and ‘CSI Ps SP q’.
Up until now, they both modified the same internal state in foot. This
meant you could enable a blinking cursor with e.g. ‘CSI ? 12 h’ and
then disable it with ‘CSI 2 SP q’.
Since the ‘CSI ? 12’ escapes are used in the civis/cnorm/cvvis
terminfo entries, applications often ended up disabling the blink
state on exit (typically be emitting ‘cnorm’), requiring users to
manually re-enable blinking.
By splitting the internal state into two separate states, we can
improve the situation.
The cursor will blink if at least one of the two have been enabled.
The setting in foot.ini sets the default state of the ‘CSI Ps SP q’
escape.
This means if the user has enabled blinking in the configuration, the
cursor will blink regardless of civis/cnorm/cvvis. Which probably is
what the user wants.
If the user has NOT enabled blinking, civis/cnorm/cvvis act as
intended: cvvis blink, civis and cnorm do not.
If an application overrides the cursor blink/style with ‘CSI Ps SP q’,
that will override the user’s setting in foot.ini. But most likely
that too is intended (for example, the user may have configured the
application to use a different cursor style). And, a well written
application will emit the ‘Se’ terminfo sequence on exit, which in
foot is defined to ‘CSI SP q’, which will reset both the style and
blink state to the user configured style/state.
Closes #218
2020-11-26 18:09:32 +01:00
|
|
|
|
case 12: term->xtsave.cursor_blink = term->cursor_blink.decset; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 25: term->xtsave.show_cursor = !term->hide_cursor; break;
|
2020-10-01 20:15:32 +02:00
|
|
|
|
case 45: term->xtsave.reverse_wrap = term->reverse_wrap; break;
|
2021-01-16 13:34:40 +01:00
|
|
|
|
case 47: term->xtsave.alt_screen = term->grid == &term->alt; break;
|
2021-07-14 19:17:44 +02:00
|
|
|
|
case 80: term->xtsave.sixel_display_mode = !term->sixel.scrolling; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1000: term->xtsave.mouse_click = term->mouse_tracking == MOUSE_CLICK; break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 1001: break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1002: term->xtsave.mouse_drag = term->mouse_tracking == MOUSE_DRAG; break;
|
|
|
|
|
|
case 1003: term->xtsave.mouse_motion = term->mouse_tracking == MOUSE_MOTION; break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 1004: term->xtsave.focus_events = term->focus_events; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1005: /* term->xtsave.mouse_utf8 = term->mouse_reporting == MOUSE_UTF8; */ break;
|
|
|
|
|
|
case 1006: term->xtsave.mouse_sgr = term->mouse_reporting == MOUSE_SGR; break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 1007: term->xtsave.alt_scrolling = term->alt_scrolling; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1015: term->xtsave.mouse_urxvt = term->mouse_reporting == MOUSE_URXVT; break;
|
|
|
|
|
|
case 1034: term->xtsave.meta_eight_bit = term->meta.eight_bit; break;
|
2020-11-11 18:26:47 +01:00
|
|
|
|
case 1035: term->xtsave.num_lock_modifier = term->num_lock_modifier; break;
|
2020-08-16 16:57:39 +02:00
|
|
|
|
case 1036: term->xtsave.meta_esc_prefix = term->meta.esc_prefix; break;
|
2020-12-10 18:22:48 +01:00
|
|
|
|
case 1042: term->xtsave.bell_action_enabled = term->bell_action_enabled; break;
|
2021-01-15 17:09:15 +01:00
|
|
|
|
case 1047: term->xtsave.alt_screen = term->grid == &term->alt; break;
|
|
|
|
|
|
case 1048: term_save_cursor(term); break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 1049: term->xtsave.alt_screen = term->grid == &term->alt; break;
|
2021-02-16 19:37:49 +01:00
|
|
|
|
case 1070: term->xtsave.sixel_private_palette = term->sixel.use_private_palette; break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
case 2004: term->xtsave.bracketed_paste = term->bracketed_paste; break;
|
2021-04-25 18:35:46 +01:00
|
|
|
|
case 2026: term->xtsave.app_sync_updates = term->render.app_sync_updates.enabled; break;
|
2021-02-16 19:11:38 +01:00
|
|
|
|
case 8452: term->xtsave.sixel_cursor_right_of_graphics = term->sixel.cursor_right_of_graphics; break;
|
2020-11-29 04:04:57 +00:00
|
|
|
|
case 27127: term->xtsave.modify_escape_key = term->modify_escape_key; break;
|
2020-12-04 21:26:38 +01:00
|
|
|
|
case 737769: term->xtsave.ime = term_ime_is_enabled(term); break;
|
2020-08-16 16:38:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-16 17:02:52 +02:00
|
|
|
|
static void
|
|
|
|
|
|
xtrestore(struct terminal *term, unsigned param)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool enable;
|
|
|
|
|
|
switch (param) {
|
2020-08-18 21:13:50 +02:00
|
|
|
|
case 1: enable = term->xtsave.application_cursor_keys; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 3: return;
|
|
|
|
|
|
case 4: return;
|
|
|
|
|
|
case 5: enable = term->xtsave.reverse; break;
|
|
|
|
|
|
case 6: enable = term->xtsave.origin; break;
|
|
|
|
|
|
case 7: enable = term->xtsave.auto_margin; break;
|
|
|
|
|
|
case 9: /* enable = term->xtsave.mouse_x10; break; */ return;
|
2020-08-18 07:00:26 +02:00
|
|
|
|
case 12: enable = term->xtsave.cursor_blink; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 25: enable = term->xtsave.show_cursor; break;
|
2020-10-03 11:53:03 +02:00
|
|
|
|
case 45: enable = term->xtsave.reverse_wrap; break;
|
2021-01-16 13:34:40 +01:00
|
|
|
|
case 47: enable = term->xtsave.alt_screen; break;
|
2021-07-14 19:17:44 +02:00
|
|
|
|
case 80: enable = term->xtsave.sixel_display_mode; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 1000: enable = term->xtsave.mouse_click; break;
|
|
|
|
|
|
case 1001: return;
|
|
|
|
|
|
case 1002: enable = term->xtsave.mouse_drag; break;
|
|
|
|
|
|
case 1003: enable = term->xtsave.mouse_motion; break;
|
|
|
|
|
|
case 1004: enable = term->xtsave.focus_events; break;
|
|
|
|
|
|
case 1005: /* enable = term->xtsave.mouse_utf8; break; */ return;
|
|
|
|
|
|
case 1006: enable = term->xtsave.mouse_sgr; break;
|
|
|
|
|
|
case 1007: enable = term->xtsave.alt_scrolling; break;
|
|
|
|
|
|
case 1015: enable = term->xtsave.mouse_urxvt; break;
|
|
|
|
|
|
case 1034: enable = term->xtsave.meta_eight_bit; break;
|
2020-11-11 18:26:47 +01:00
|
|
|
|
case 1035: enable = term->xtsave.num_lock_modifier; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 1036: enable = term->xtsave.meta_esc_prefix; break;
|
2020-12-10 18:22:48 +01:00
|
|
|
|
case 1042: enable = term->xtsave.bell_action_enabled; break;
|
2021-01-15 17:09:15 +01:00
|
|
|
|
case 1047: enable = term->xtsave.alt_screen; break;
|
|
|
|
|
|
case 1048: enable = true; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 1049: enable = term->xtsave.alt_screen; break;
|
2021-02-16 19:37:49 +01:00
|
|
|
|
case 1070: enable = term->xtsave.sixel_private_palette; break;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
case 2004: enable = term->xtsave.bracketed_paste; break;
|
2021-04-25 18:35:46 +01:00
|
|
|
|
case 2026: enable = term->xtsave.app_sync_updates; break;
|
2021-02-16 19:11:38 +01:00
|
|
|
|
case 8452: enable = term->xtsave.sixel_cursor_right_of_graphics; break;
|
2020-11-29 04:04:57 +00:00
|
|
|
|
case 27127: enable = term->xtsave.modify_escape_key; break;
|
2020-12-04 21:26:38 +01:00
|
|
|
|
case 737769: enable = term->xtsave.ime; break;
|
2020-08-18 21:14:05 +02:00
|
|
|
|
|
|
|
|
|
|
default: return;
|
2020-08-16 17:02:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
decset_decrst(term, param, enable);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-07 16:32:18 +02:00
|
|
|
|
void
|
2019-07-04 19:35:01 +02:00
|
|
|
|
csi_dispatch(struct terminal *term, uint8_t final)
|
|
|
|
|
|
{
|
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_DBG("%s (%08x)", csi_as_string(term, final, -1), term->vt.private);
|
2019-06-18 21:55:39 +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-07-10 15:03:16 +02:00
|
|
|
|
case 0: {
|
2019-06-17 18:57:12 +02:00
|
|
|
|
switch (final) {
|
2020-01-20 18:37:20 +01:00
|
|
|
|
case 'b':
|
|
|
|
|
|
if (term->vt.last_printed != 0) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Note: we never reset 'last-printed'. According to
|
|
|
|
|
|
* ECMA-48, the behaviour is undefined if REP was
|
2020-08-15 19:39:00 +01:00
|
|
|
|
* _not_ preceded by a graphical character.
|
2020-01-20 18:37:20 +01:00
|
|
|
|
*/
|
|
|
|
|
|
int count = vt_param_get(term, 0, 1);
|
2020-08-06 23:20:46 +02:00
|
|
|
|
LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count);
|
2020-01-20 18:37:20 +01:00
|
|
|
|
|
|
|
|
|
|
const int width = wcwidth(term->vt.last_printed);
|
2020-07-16 08:06:37 +02:00
|
|
|
|
if (width > 0) {
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
term_print(term, term->vt.last_printed, width);
|
|
|
|
|
|
}
|
2020-01-20 18:37:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2019-11-18 12:07:17 +01:00
|
|
|
|
case 'c': {
|
2020-10-09 18:53:00 +02:00
|
|
|
|
if (vt_param_get(term, 0, 0) != 0) {
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-18 12:07:17 +01:00
|
|
|
|
/* Send Device Attributes (Primary DA) */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Responses:
|
|
|
|
|
|
* - CSI?1;2c vt100 with advanced video option
|
|
|
|
|
|
* - CSI?1;0c vt101 with no options
|
|
|
|
|
|
* - CSI?6c vt102
|
|
|
|
|
|
* - CSI?62;<Ps>c vt220
|
|
|
|
|
|
* - CSI?63;<Ps>c vt320
|
|
|
|
|
|
* - CSI?64;<Ps>c vt420
|
|
|
|
|
|
*
|
|
|
|
|
|
* Ps (response may contain multiple):
|
|
|
|
|
|
* - 1 132 columns
|
|
|
|
|
|
* - 2 Printer.
|
|
|
|
|
|
* - 3 ReGIS graphics.
|
|
|
|
|
|
* - 4 Sixel graphics.
|
|
|
|
|
|
* - 6 Selective erase.
|
|
|
|
|
|
* - 8 User-defined keys.
|
|
|
|
|
|
* - 9 National Replacement Character sets.
|
|
|
|
|
|
* - 15 Technical characters.
|
|
|
|
|
|
* - 16 Locator port.
|
|
|
|
|
|
* - 17 Terminal state interrogation.
|
|
|
|
|
|
* - 18 User windows.
|
|
|
|
|
|
* - 21 Horizontal scrolling.
|
|
|
|
|
|
* - 22 ANSI color, e.g., VT525.
|
|
|
|
|
|
* - 28 Rectangular editing.
|
|
|
|
|
|
* - 29 ANSI text locator (i.e., DEC Locator mode).
|
2020-07-24 17:46:18 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Note: we report ourselves as a VT220, mainly to be able
|
2020-08-15 19:39:00 +01:00
|
|
|
|
* to pass parameters, to indicate we support sixel, and
|
2020-07-24 17:46:18 +02:00
|
|
|
|
* ANSI colors.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The VT level must be synchronized with the secondary DA
|
|
|
|
|
|
* response.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Note: tertiary DA responds with "FOOT".
|
2019-11-18 12:07:17 +01:00
|
|
|
|
*/
|
2021-08-30 07:40:03 +01:00
|
|
|
|
static const char reply[] = "\033[?62;4;22c";
|
|
|
|
|
|
term_to_slave(term, reply, sizeof(reply) - 1);
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-11-18 12:07:17 +01:00
|
|
|
|
}
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-06-24 19:59:54 +02:00
|
|
|
|
case 'd': {
|
2019-06-24 20:05:24 +02:00
|
|
|
|
/* VPA - vertical line position absolute */
|
2019-11-17 18:52:27 +01:00
|
|
|
|
int rel_row = vt_param_get(term, 0, 1) - 1;
|
|
|
|
|
|
int row = term_row_rel_to_abs(term, rel_row);
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_to(term, row, term->grid->cursor.point.col);
|
2019-06-24 19:59:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-17 18:57:12 +02:00
|
|
|
|
case 'm':
|
2019-07-07 16:32:18 +02:00
|
|
|
|
csi_sgr(term);
|
|
|
|
|
|
break;
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-06-24 20:17:02 +02:00
|
|
|
|
case 'A':
|
2019-07-10 16:04:16 +02:00
|
|
|
|
term_cursor_up(term, vt_param_get(term, 0, 1));
|
2019-06-22 21:31:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-06-24 20:05:31 +02:00
|
|
|
|
case 'e':
|
2019-06-24 20:17:02 +02:00
|
|
|
|
case 'B':
|
2019-07-10 16:04:16 +02:00
|
|
|
|
term_cursor_down(term, vt_param_get(term, 0, 1));
|
2019-06-22 21:31:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-11-17 11:31:35 +01:00
|
|
|
|
case 'a':
|
2019-06-24 20:17:02 +02:00
|
|
|
|
case 'C':
|
2019-07-10 16:04:16 +02:00
|
|
|
|
term_cursor_right(term, vt_param_get(term, 0, 1));
|
2019-06-22 21:31:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-06-24 20:17:02 +02:00
|
|
|
|
case 'D':
|
2019-07-10 16:04:16 +02:00
|
|
|
|
term_cursor_left(term, vt_param_get(term, 0, 1));
|
2019-06-22 21:31:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-11-17 11:34:53 +01:00
|
|
|
|
case 'E':
|
|
|
|
|
|
/* CNL - Cursor Next Line */
|
|
|
|
|
|
term_cursor_down(term, vt_param_get(term, 0, 1));
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_left(term, term->grid->cursor.point.col);
|
2019-11-17 11:34:53 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-11-17 11:36:24 +01:00
|
|
|
|
case 'F':
|
|
|
|
|
|
/* CPL - Cursor Previous Line */
|
|
|
|
|
|
term_cursor_up(term, vt_param_get(term, 0, 1));
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_left(term, term->grid->cursor.point.col);
|
2019-11-17 11:36:24 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-11-16 10:55:28 +01:00
|
|
|
|
case 'g': {
|
|
|
|
|
|
int param = vt_param_get(term, 0, 0);
|
|
|
|
|
|
switch (param) {
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
/* Clear tab stop at *current* column */
|
|
|
|
|
|
tll_foreach(term->tab_stops, it) {
|
2020-04-16 18:51:14 +02:00
|
|
|
|
if (it->item == term->grid->cursor.point.col)
|
2019-11-16 10:55:28 +01:00
|
|
|
|
tll_remove(term->tab_stops, it);
|
2020-04-16 18:51:14 +02:00
|
|
|
|
else if (it->item > term->grid->cursor.point.col)
|
2019-11-16 10:55:28 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
/* Clear *all* tabs */
|
|
|
|
|
|
tll_free(term->tab_stops);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-17 11:09:16 +01:00
|
|
|
|
case '`':
|
2019-06-24 20:08:44 +02:00
|
|
|
|
case 'G': {
|
|
|
|
|
|
/* Cursor horizontal absolute */
|
2019-11-17 18:52:27 +01:00
|
|
|
|
int col = min(vt_param_get(term, 0, 1), term->cols) - 1;
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_to(term, term->grid->cursor.point.row, col);
|
2019-06-24 20:08:44 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-05 11:10:19 +01:00
|
|
|
|
case 'f':
|
2019-06-23 14:11:35 +02:00
|
|
|
|
case 'H': {
|
|
|
|
|
|
/* Move cursor */
|
2019-11-17 18:52:27 +01:00
|
|
|
|
int rel_row = vt_param_get(term, 0, 1) - 1;
|
|
|
|
|
|
int row = term_row_rel_to_abs(term, rel_row);
|
|
|
|
|
|
int col = min(vt_param_get(term, 1, 1), term->cols) - 1;
|
|
|
|
|
|
term_cursor_to(term, row, col);
|
2019-06-23 14:11:35 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-17 18:57:12 +02:00
|
|
|
|
case 'J': {
|
2019-06-19 10:04:47 +02:00
|
|
|
|
/* Erase screen */
|
|
|
|
|
|
|
2019-07-10 16:04:16 +02:00
|
|
|
|
int param = vt_param_get(term, 0, 0);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
switch (param) {
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
/* From cursor to end of screen */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&term->grid->cursor.point,
|
2019-07-08 13:57:31 +02:00
|
|
|
|
&(struct coord){term->cols - 1, term->rows - 1});
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
/* From start of screen to cursor */
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_erase(term, &(struct coord){0, 0}, &term->grid->cursor.point);
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
/* Erase entire screen */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
|
|
|
|
|
&(struct coord){0, 0},
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->rows - 1});
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-22 19:05:22 +02:00
|
|
|
|
case 3: {
|
|
|
|
|
|
/* Erase scrollback */
|
2021-07-16 16:45:36 +02:00
|
|
|
|
term_erase_scrollback(term);
|
2019-07-22 19:05:22 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
2019-06-17 18:57:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'K': {
|
2019-06-19 10:04:47 +02:00
|
|
|
|
/* Erase line */
|
|
|
|
|
|
|
2019-07-10 16:04:16 +02:00
|
|
|
|
int param = vt_param_get(term, 0, 0);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
switch (param) {
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
/* From cursor to end of line */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&term->grid->cursor.point,
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->grid->cursor.point.row});
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
/* From start of line to cursor */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term, &(struct coord){0, term->grid->cursor.point.row}, &term->grid->cursor.point);
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
/* Entire line */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&(struct coord){0, term->grid->cursor.point.row},
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->grid->cursor.point.row});
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-23 21:12:32 +02:00
|
|
|
|
case 'L': {
|
2020-04-16 18:51:14 +02:00
|
|
|
|
if (term->grid->cursor.point.row < term->scroll_region.start ||
|
|
|
|
|
|
term->grid->cursor.point.row >= term->scroll_region.end)
|
2019-06-23 21:12:32 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
int count = min(
|
2019-07-10 16:04:16 +02:00
|
|
|
|
vt_param_get(term, 0, 1),
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->scroll_region.end - term->grid->cursor.point.row);
|
2019-06-23 21:12:32 +02:00
|
|
|
|
|
2019-06-29 21:03:28 +02:00
|
|
|
|
term_scroll_reverse_partial(
|
|
|
|
|
|
term,
|
2019-06-25 20:11:08 +02:00
|
|
|
|
(struct scroll_region){
|
2020-04-16 18:51:14 +02:00
|
|
|
|
.start = term->grid->cursor.point.row,
|
2019-06-29 21:08:08 +02:00
|
|
|
|
.end = term->scroll_region.end},
|
2019-06-23 21:12:32 +02:00
|
|
|
|
count);
|
2020-07-14 10:52:15 +02:00
|
|
|
|
term->grid->cursor.lcf = false;
|
2021-06-07 21:57:11 +02:00
|
|
|
|
term->grid->cursor.point.col = 0;
|
2019-06-23 21:12:32 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'M': {
|
2020-04-16 18:51:14 +02:00
|
|
|
|
if (term->grid->cursor.point.row < term->scroll_region.start ||
|
|
|
|
|
|
term->grid->cursor.point.row >= term->scroll_region.end)
|
2019-06-23 21:12:32 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
int count = min(
|
2019-07-10 16:04:16 +02:00
|
|
|
|
vt_param_get(term, 0, 1),
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->scroll_region.end - term->grid->cursor.point.row);
|
2019-06-23 21:12:32 +02:00
|
|
|
|
|
2019-06-29 21:03:28 +02:00
|
|
|
|
term_scroll_partial(
|
|
|
|
|
|
term,
|
2019-06-25 20:11:08 +02:00
|
|
|
|
(struct scroll_region){
|
2020-04-16 18:51:14 +02:00
|
|
|
|
.start = term->grid->cursor.point.row,
|
2019-06-29 21:08:08 +02:00
|
|
|
|
.end = term->scroll_region.end},
|
2019-06-23 21:12:32 +02:00
|
|
|
|
count);
|
2020-07-14 10:52:15 +02:00
|
|
|
|
term->grid->cursor.lcf = false;
|
2021-06-07 21:57:11 +02:00
|
|
|
|
term->grid->cursor.point.col = 0;
|
2019-06-23 21:12:32 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-22 21:31:28 +02:00
|
|
|
|
case 'P': {
|
2019-07-17 11:19:28 +02:00
|
|
|
|
/* DCH: Delete character(s) */
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2019-07-02 22:19:43 +02:00
|
|
|
|
/* Number of characters to delete */
|
|
|
|
|
|
int count = min(
|
2020-04-16 18:51:14 +02:00
|
|
|
|
vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-07-02 22:19:43 +02:00
|
|
|
|
/* Number of characters left after deletion (on current line) */
|
2020-04-16 18:51:14 +02:00
|
|
|
|
int remaining = term->cols - (term->grid->cursor.point.col + count);
|
2019-06-22 21:31:28 +02:00
|
|
|
|
|
2019-07-02 22:19:43 +02:00
|
|
|
|
/* 'Delete' characters by moving the remaining ones */
|
2020-04-16 18:51:14 +02:00
|
|
|
|
memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col],
|
|
|
|
|
|
&term->grid->cur_row->cells[term->grid->cursor.point.col + count],
|
2019-07-08 13:57:31 +02:00
|
|
|
|
remaining * sizeof(term->grid->cur_row->cells[0]));
|
2019-07-30 18:03:03 +02:00
|
|
|
|
|
|
|
|
|
|
for (size_t c = 0; c < remaining; c++)
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->grid->cur_row->cells[term->grid->cursor.point.col + c].attrs.clean = 0;
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term->grid->cur_row->dirty = true;
|
2019-07-02 22:19:43 +02:00
|
|
|
|
|
|
|
|
|
|
/* Erase the remainder of the line */
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&(struct coord){term->grid->cursor.point.col + remaining, term->grid->cursor.point.row},
|
|
|
|
|
|
&(struct coord){term->cols - 1, term->grid->cursor.point.row});
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-07-17 11:19:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case '@': {
|
|
|
|
|
|
/* ICH: insert character(s) */
|
|
|
|
|
|
|
|
|
|
|
|
/* Number of characters to insert */
|
|
|
|
|
|
int count = min(
|
2020-04-16 18:51:14 +02:00
|
|
|
|
vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
|
2019-07-17 11:19:28 +02:00
|
|
|
|
|
|
|
|
|
|
/* Characters to move */
|
2020-04-16 18:51:14 +02:00
|
|
|
|
int remaining = term->cols - (term->grid->cursor.point.col + count);
|
2019-07-17 11:19:28 +02:00
|
|
|
|
|
|
|
|
|
|
/* Push existing characters */
|
2020-04-16 18:51:14 +02:00
|
|
|
|
memmove(&term->grid->cur_row->cells[term->grid->cursor.point.col + count],
|
|
|
|
|
|
&term->grid->cur_row->cells[term->grid->cursor.point.col],
|
2019-07-17 11:19:28 +02:00
|
|
|
|
remaining * sizeof(term->grid->cur_row->cells[0]));
|
2019-07-30 18:03:03 +02:00
|
|
|
|
for (size_t c = 0; c < remaining; c++)
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term->grid->cur_row->cells[term->grid->cursor.point.col + count + c].attrs.clean = 0;
|
2019-07-17 11:19:28 +02:00
|
|
|
|
term->grid->cur_row->dirty = true;
|
|
|
|
|
|
|
|
|
|
|
|
/* Erase (insert space characters) */
|
2019-07-02 22:19:43 +02:00
|
|
|
|
term_erase(
|
2019-07-17 11:19:28 +02:00
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&term->grid->cursor.point,
|
|
|
|
|
|
&(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
break;
|
2019-07-03 14:47:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-19 18:47:38 +02:00
|
|
|
|
case 'S': {
|
2021-08-30 07:40:03 +01:00
|
|
|
|
const struct scroll_region *r = &term->scroll_region;
|
|
|
|
|
|
int amount = min(vt_param_get(term, 0, 1), r->end - r->start);
|
2020-05-19 18:47:38 +02:00
|
|
|
|
term_scroll(term, amount);
|
2019-07-04 19:39:23 +02:00
|
|
|
|
break;
|
2020-05-19 18:47:38 +02:00
|
|
|
|
}
|
2019-07-04 19:39:23 +02:00
|
|
|
|
|
2020-05-19 18:47:38 +02:00
|
|
|
|
case 'T': {
|
2021-08-30 07:40:03 +01:00
|
|
|
|
const struct scroll_region *r = &term->scroll_region;
|
|
|
|
|
|
int amount = min(vt_param_get(term, 0, 1), r->end - r->start);
|
2020-05-19 18:47:38 +02:00
|
|
|
|
term_scroll_reverse(term, amount);
|
2019-07-04 19:39:23 +02:00
|
|
|
|
break;
|
2020-05-19 18:47:38 +02:00
|
|
|
|
}
|
2019-07-04 19:39:23 +02:00
|
|
|
|
|
2019-07-03 15:58:49 +02:00
|
|
|
|
case 'X': {
|
|
|
|
|
|
/* Erase chars */
|
|
|
|
|
|
int count = min(
|
2020-04-16 18:51:14 +02:00
|
|
|
|
vt_param_get(term, 0, 1), term->cols - term->grid->cursor.point.col);
|
2019-07-03 15:58:49 +02:00
|
|
|
|
|
2019-07-08 13:57:31 +02:00
|
|
|
|
term_erase(
|
|
|
|
|
|
term,
|
2020-04-16 18:51:14 +02:00
|
|
|
|
&term->grid->cursor.point,
|
|
|
|
|
|
&(struct coord){term->grid->cursor.point.col + count - 1, term->grid->cursor.point.row});
|
|
|
|
|
|
term->grid->cursor.lcf = false;
|
2019-07-03 15:58:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-17 12:29:52 +01:00
|
|
|
|
case 'I': {
|
|
|
|
|
|
/* CHT - Tab Forward (param is number of tab stops to move through) */
|
|
|
|
|
|
for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
|
|
|
|
|
|
int new_col = term->cols - 1;
|
|
|
|
|
|
tll_foreach(term->tab_stops, it) {
|
2020-04-16 18:51:14 +02:00
|
|
|
|
if (it->item > term->grid->cursor.point.col) {
|
2019-11-17 12:29:52 +01:00
|
|
|
|
new_col = it->item;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_col >= term->grid->cursor.point.col);
|
2021-06-07 21:18:15 +02:00
|
|
|
|
|
|
|
|
|
|
bool lcf = term->grid->cursor.lcf;
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_right(term, new_col - term->grid->cursor.point.col);
|
2021-06-07 21:18:15 +02:00
|
|
|
|
term->grid->cursor.lcf = lcf;
|
2019-11-17 12:29:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-17 11:20:59 +01:00
|
|
|
|
case 'Z':
|
|
|
|
|
|
/* CBT - Back tab (param is number of tab stops to move back through) */
|
|
|
|
|
|
for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
|
|
|
|
|
|
int new_col = 0;
|
|
|
|
|
|
tll_rforeach(term->tab_stops, it) {
|
2020-04-16 18:51:14 +02:00
|
|
|
|
if (it->item < term->grid->cursor.point.col) {
|
2019-11-17 11:20:59 +01:00
|
|
|
|
new_col = it->item;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term->grid->cursor.point.col >= new_col);
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_cursor_left(term, term->grid->cursor.point.col - new_col);
|
2019-11-17 11:20:59 +01:00
|
|
|
|
}
|
2019-07-22 20:33:50 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-03 21:16:41 +02:00
|
|
|
|
case 'h':
|
2019-08-30 22:01:05 +02:00
|
|
|
|
/* Set mode */
|
|
|
|
|
|
switch (vt_param_get(term, 0, 0)) {
|
|
|
|
|
|
case 2: /* Keyboard Action Mode - AM */
|
|
|
|
|
|
LOG_WARN("unimplemented: keyboard action mode (AM)");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 4: /* Insert Mode - IRM */
|
|
|
|
|
|
term->insert_mode = true;
|
2021-03-14 19:19:10 +01:00
|
|
|
|
term_update_ascii_printer(term);
|
2019-08-30 22:01:05 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 12: /* Send/receive Mode - SRM */
|
|
|
|
|
|
LOG_WARN("unimplemented: send/receive mode (SRM)");
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 20: /* Automatic Newline Mode - LNM */
|
2020-07-14 09:29:10 +02:00
|
|
|
|
/* TODO: would be easy to implemented; when active
|
|
|
|
|
|
* term_linefeed() would _also_ do a
|
|
|
|
|
|
* term_carriage_return() */
|
2019-08-30 22:01:05 +02:00
|
|
|
|
LOG_WARN("unimplemented: automatic newline mode (LNM)");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-07-03 21:16:41 +02:00
|
|
|
|
break;
|
2019-07-03 14:47:01 +02:00
|
|
|
|
|
2019-07-03 21:16:41 +02:00
|
|
|
|
case 'l':
|
2019-08-30 22:01:05 +02:00
|
|
|
|
/* Reset mode */
|
|
|
|
|
|
switch (vt_param_get(term, 0, 0)) {
|
|
|
|
|
|
case 4: /* Insert Mode - IRM */
|
|
|
|
|
|
term->insert_mode = false;
|
2021-03-14 19:19:10 +01:00
|
|
|
|
term_update_ascii_printer(term);
|
2019-08-30 22:01:05 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2: /* Keyboard Action Mode - AM */
|
|
|
|
|
|
case 12: /* Send/receive Mode - SRM */
|
|
|
|
|
|
case 20: /* Automatic Newline Mode - LNM */
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-07-03 14:47:01 +02:00
|
|
|
|
break;
|
2019-06-17 18:57:12 +02:00
|
|
|
|
|
2019-06-23 18:02:49 +02:00
|
|
|
|
case 'r': {
|
2019-07-10 16:04:16 +02:00
|
|
|
|
int start = vt_param_get(term, 0, 1);
|
|
|
|
|
|
int end = min(vt_param_get(term, 1, term->rows), term->rows);
|
2019-06-23 21:12:32 +02:00
|
|
|
|
|
2019-09-29 14:58:02 +02:00
|
|
|
|
if (end > start) {
|
2019-06-23 21:12:32 +02:00
|
|
|
|
|
2019-09-29 14:58:02 +02:00
|
|
|
|
/* 1-based */
|
|
|
|
|
|
term->scroll_region.start = start - 1;
|
|
|
|
|
|
term->scroll_region.end = end;
|
2019-11-05 13:27:37 +01:00
|
|
|
|
term_cursor_home(term);
|
2019-09-29 14:58:02 +02:00
|
|
|
|
|
|
|
|
|
|
LOG_DBG("scroll region: %d-%d",
|
|
|
|
|
|
term->scroll_region.start,
|
|
|
|
|
|
term->scroll_region.end);
|
|
|
|
|
|
}
|
2019-06-23 18:02:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-23 17:57:07 +02:00
|
|
|
|
case 's':
|
2021-01-15 17:08:30 +01:00
|
|
|
|
term_save_cursor(term);
|
2019-07-23 17:57:07 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'u':
|
2020-04-16 18:51:14 +02:00
|
|
|
|
term_restore_cursor(term, &term->grid->saved_cursor);
|
2019-07-23 17:57:07 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-21 17:48:06 +02:00
|
|
|
|
case 't': {
|
2020-02-01 19:27:52 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Window operations
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
const unsigned param = vt_param_get(term, 0, 0);
|
2019-07-21 17:48:06 +02:00
|
|
|
|
|
|
|
|
|
|
switch (param) {
|
2020-02-01 19:27:52 +01:00
|
|
|
|
case 1: LOG_WARN("unimplemented: de-iconify"); break;
|
|
|
|
|
|
case 2: LOG_WARN("unimplemented: iconify"); break;
|
|
|
|
|
|
case 3: LOG_WARN("unimplemented: move window to pixel position"); break;
|
|
|
|
|
|
case 4: LOG_WARN("unimplemented: resize window in pixels"); break;
|
|
|
|
|
|
case 5: LOG_WARN("unimplemented: raise window to front of stack"); break;
|
|
|
|
|
|
case 6: LOG_WARN("unimplemented: raise window to back of stack"); break;
|
|
|
|
|
|
case 7: LOG_WARN("unimplemented: refresh window"); break;
|
|
|
|
|
|
case 8: LOG_WARN("unimplemented: resize window in chars"); break;
|
|
|
|
|
|
case 9: LOG_WARN("unimplemented: maximize/unmaximize window"); break;
|
|
|
|
|
|
case 10: LOG_WARN("unimplemented: to/from full screen"); break;
|
|
|
|
|
|
case 20: LOG_WARN("unimplemented: report icon label"); break;
|
|
|
|
|
|
case 21: LOG_WARN("unimplemented: report window title"); break;
|
|
|
|
|
|
case 24: LOG_WARN("unimplemented: resize window (DECSLPP)"); break;
|
|
|
|
|
|
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
case 11: /* report if window is iconified */
|
|
|
|
|
|
/* We don't know - always report *not* iconified */
|
|
|
|
|
|
/* 1=not iconified, 2=iconified */
|
|
|
|
|
|
term_to_slave(term, "\033[1t", 4);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 13: { /* report window position */
|
2021-02-16 14:31:46 +01:00
|
|
|
|
/* We don't know our position - always report (0,0) */
|
2021-08-30 07:40:03 +01:00
|
|
|
|
static const char reply[] = "\033[3;0;0t";
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
switch (vt_param_get(term, 1, 0)) {
|
2021-08-30 07:40:03 +01:00
|
|
|
|
case 0: /* window position */
|
|
|
|
|
|
case 2: /* text area position */
|
|
|
|
|
|
term_to_slave(term, reply, sizeof(reply) - 1);
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-01 19:27:52 +01:00
|
|
|
|
case 14: { /* report window size in pixels */
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
int width = -1;
|
|
|
|
|
|
int height = -1;
|
|
|
|
|
|
|
|
|
|
|
|
switch (vt_param_get(term, 1, 0)) {
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
/* text area size */
|
|
|
|
|
|
width = term->width - term->margins.left - term->margins.right;
|
|
|
|
|
|
height = term->height - term->margins.top - term->margins.bottom;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
/* window size */
|
|
|
|
|
|
width = term->width;
|
|
|
|
|
|
height = term->height;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (width >= 0 && height >= 0) {
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt",
|
2020-04-18 23:20:09 +02:00
|
|
|
|
height / term->scale, width / term->scale);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
}
|
2020-02-01 19:27:52 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
case 15: /* report screen size in pixels */
|
|
|
|
|
|
tll_foreach(term->window->on_outputs, it) {
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[5;%d;%dt",
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
it->item->dim.px_scaled.height,
|
|
|
|
|
|
it->item->dim.px_scaled.width);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tll_length(term->window->on_outputs) == 0)
|
|
|
|
|
|
term_to_slave(term, "\033[5;0;0t", 8);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-01 19:27:52 +01:00
|
|
|
|
case 16: { /* report cell size in pixels */
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt",
|
2020-04-18 23:20:09 +02:00
|
|
|
|
term->cell_height / term->scale,
|
|
|
|
|
|
term->cell_width / term->scale);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-01 19:27:52 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
case 18: { /* text area size in chars */
|
2020-02-01 19:27:52 +01:00
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[8;%d;%dt",
|
2020-02-01 19:27:52 +01:00
|
|
|
|
term->rows, term->cols);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-01 19:27:52 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
case 19: { /* report screen size in chars */
|
|
|
|
|
|
tll_foreach(term->window->on_outputs, it) {
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt",
|
2020-04-18 23:20:09 +02:00
|
|
|
|
it->item->dim.px_real.height / term->cell_height / term->scale,
|
2020-04-19 14:52:14 +02:00
|
|
|
|
it->item->dim.px_real.width / term->cell_width / term->scale);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
csi: implement CSI Ps ; Ps ; Ps t reporting escape sequences
A lot of the escape sequences on the "CSI Ps ; Ps ; Ps t" form are
expected to return a reply. Thus, not having these implemented means
we will hang if the client sends these escapes.
Not all of these escapes can be meaningfully implemented on Wayland,
and thus this implementation is best effort.
We now support the following escapes:
* 11t - report if window is iconified (always replies "no")
* 13t - report window position (always replies 0,0)
* 13;2t - report text area position (replies with margins, since we
cannot get the window's position)
* 14t - report text area size, in pixels
* 14;2t - report window size, in pixels
* 15t - report screen size, in pixels
* 16t - report cell size, in pixels
* 18t - report text area size, in cells
* 19t - report screen size, in cells
2020-04-17 23:03:20 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tll_length(term->window->on_outputs) == 0)
|
|
|
|
|
|
term_to_slave(term, "\033[9;0;0t", 8);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-21 17:48:06 +02:00
|
|
|
|
case 22: { /* push window title */
|
|
|
|
|
|
/* 0 - icon + title, 1 - icon, 2 - title */
|
|
|
|
|
|
unsigned what = vt_param_get(term, 1, 0);
|
|
|
|
|
|
if (what == 0 || what == 2) {
|
|
|
|
|
|
tll_push_back(
|
2020-08-08 20:34:30 +01:00
|
|
|
|
term->window_title_stack, xstrdup(term->window_title));
|
2019-07-21 17:48:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 23: { /* pop window title */
|
|
|
|
|
|
/* 0 - icon + title, 1 - icon, 2 - title */
|
|
|
|
|
|
unsigned what = vt_param_get(term, 1, 0);
|
|
|
|
|
|
if (what == 0 || what == 2) {
|
|
|
|
|
|
if (tll_length(term->window_title_stack) > 0) {
|
|
|
|
|
|
char *title = tll_pop_back(term->window_title_stack);
|
|
|
|
|
|
term_set_window_title(term, title);
|
|
|
|
|
|
free(title);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-21 19:14:19 +02:00
|
|
|
|
case 1001: {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-21 17:48:06 +02:00
|
|
|
|
default:
|
2020-02-01 19:42:31 +01:00
|
|
|
|
LOG_DBG("ignoring %s", csi_as_string(term, final, -1));
|
2019-07-21 17:48:06 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-06-22 22:25:50 +02:00
|
|
|
|
break;
|
2019-07-21 17:48:06 +02:00
|
|
|
|
}
|
2019-06-22 22:25:50 +02:00
|
|
|
|
|
2019-06-23 18:17:36 +02:00
|
|
|
|
case 'n': {
|
|
|
|
|
|
if (term->vt.params.idx > 0) {
|
2019-07-10 16:04:16 +02:00
|
|
|
|
int param = vt_param_get(term, 0, 0);
|
2019-06-23 18:17:36 +02:00
|
|
|
|
switch (param) {
|
2019-11-18 11:44:03 +01:00
|
|
|
|
case 5:
|
|
|
|
|
|
/* Query device status */
|
|
|
|
|
|
term_to_slave(term, "\x1b[0n", 4); /* "Device OK" */
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2019-06-23 18:17:36 +02:00
|
|
|
|
case 6: {
|
|
|
|
|
|
/* u7 - cursor position query */
|
2019-11-05 13:27:37 +01:00
|
|
|
|
|
|
|
|
|
|
int row = term->origin == ORIGIN_ABSOLUTE
|
2020-04-16 18:51:14 +02:00
|
|
|
|
? term->grid->cursor.point.row
|
|
|
|
|
|
: term->grid->cursor.point.row - term->scroll_region.start;
|
2019-11-05 13:27:37 +01:00
|
|
|
|
|
2019-06-23 18:17:36 +02:00
|
|
|
|
/* TODO: we use 0-based position, while the xterm
|
|
|
|
|
|
* terminfo says the receiver of the reply should
|
2019-06-24 19:03:19 +02:00
|
|
|
|
* decrement, hence we must add 1 */
|
2019-06-23 18:17:36 +02:00
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\x1b[%d;%dR",
|
2020-04-16 18:51:14 +02:00
|
|
|
|
row + 1, term->grid->cursor.point.col + 1);
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
2019-06-23 18:17:36 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-23 18:17:36 +02:00
|
|
|
|
}
|
2019-07-30 21:42:46 +02:00
|
|
|
|
} else
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
|
2019-06-23 18:17:36 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-17 18:57:12 +02:00
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-10 15:03:16 +02:00
|
|
|
|
break;
|
2019-06-17 18:57:12 +02:00
|
|
|
|
}
|
2019-06-22 22:25:50 +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
|
|
|
|
break; /* private[0] == 0 */
|
2019-07-10 15:03:16 +02:00
|
|
|
|
}
|
2019-06-22 22:25:50 +02:00
|
|
|
|
|
2019-07-10 15:03:16 +02:00
|
|
|
|
case '?': {
|
2019-06-22 22:25:50 +02:00
|
|
|
|
switch (final) {
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 'h':
|
|
|
|
|
|
/* DECSET - DEC private mode set */
|
|
|
|
|
|
for (size_t i = 0; i < term->vt.params.idx; i++)
|
|
|
|
|
|
decset(term, term->vt.params.v[i].value);
|
2019-06-22 22:25:50 +02:00
|
|
|
|
break;
|
2019-11-04 12:00:50 +01:00
|
|
|
|
|
2020-08-16 16:20:47 +02:00
|
|
|
|
case 'l':
|
|
|
|
|
|
/* DECRST - DEC private mode reset */
|
|
|
|
|
|
for (size_t i = 0; i < term->vt.params.idx; i++)
|
|
|
|
|
|
decrst(term, term->vt.params.v[i].value);
|
2019-06-22 22:25:50 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-03 16:00:27 +02:00
|
|
|
|
case 's':
|
2020-08-16 16:47:46 +02:00
|
|
|
|
for (size_t i = 0; i < term->vt.params.idx; i++)
|
|
|
|
|
|
xtsave(term, term->vt.params.v[i].value);
|
2019-07-03 21:33:23 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-03 16:00:27 +02:00
|
|
|
|
case 'r':
|
2020-08-16 17:02:52 +02:00
|
|
|
|
for (size_t i = 0; i < term->vt.params.idx; i++)
|
|
|
|
|
|
xtrestore(term, term->vt.params.v[i].value);
|
2019-07-03 16:00:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-22 14:02:00 +01:00
|
|
|
|
case 'S': {
|
|
|
|
|
|
unsigned target = vt_param_get(term, 0, 0);
|
|
|
|
|
|
unsigned operation = vt_param_get(term, 1, 0);
|
|
|
|
|
|
|
|
|
|
|
|
switch (target) {
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
switch (operation) {
|
|
|
|
|
|
case 1: sixel_colors_report_current(term); break;
|
|
|
|
|
|
case 2: sixel_colors_reset(term); break;
|
|
|
|
|
|
case 3: sixel_colors_set(term, vt_param_get(term, 2, 0)); break;
|
2020-08-18 20:49:44 +01:00
|
|
|
|
case 4: sixel_colors_report_max(term); break;
|
2020-04-25 11:39:31 +02:00
|
|
|
|
default: UNHANDLED(); break;
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
2020-02-22 21:03:24 +01:00
|
|
|
|
switch (operation) {
|
|
|
|
|
|
case 1: sixel_geometry_report_current(term); break;
|
|
|
|
|
|
case 2: sixel_geometry_reset(term); break;
|
|
|
|
|
|
case 3: sixel_geometry_set(term, vt_param_get(term, 2, 0), vt_param_get(term, 3, 0)); break;
|
2020-08-18 20:49:44 +01:00
|
|
|
|
case 4: sixel_geometry_report_max(term); break;
|
2020-04-25 11:39:31 +02:00
|
|
|
|
default: UNHANDLED(); break;
|
2020-02-22 21:03:24 +01:00
|
|
|
|
}
|
2020-02-22 14:02:00 +01:00
|
|
|
|
break;
|
2020-04-25 11:39:31 +02:00
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|
2020-04-25 11:39:31 +02:00
|
|
|
|
|
2020-02-22 14:02:00 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-22 22:25:50 +02:00
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
2019-06-22 22:25:50 +02:00
|
|
|
|
}
|
2019-07-10 15:03:16 +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
|
|
|
|
break; /* private[0] == '?' */
|
2019-06-22 22:25:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-10 15:03:16 +02:00
|
|
|
|
case '>': {
|
2019-06-24 20:01:41 +02:00
|
|
|
|
switch (final) {
|
2019-12-14 14:38:03 +01:00
|
|
|
|
case 'c':
|
2019-11-18 12:06:29 +01:00
|
|
|
|
/* Send Device Attributes (Secondary DA) */
|
2019-12-14 14:38:03 +01:00
|
|
|
|
if (vt_param_get(term, 0, 0) != 0) {
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-24 20:01:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-18 12:06:29 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Param 1 - terminal type:
|
|
|
|
|
|
* 0 - vt100
|
|
|
|
|
|
* 1 - vt220
|
|
|
|
|
|
* 2 - vt240
|
|
|
|
|
|
* 18 - vt330
|
|
|
|
|
|
* 19 - vt340
|
|
|
|
|
|
* 24 - vt320
|
|
|
|
|
|
* 41 - vt420
|
|
|
|
|
|
* 61 - vt510
|
|
|
|
|
|
* 64 - vt520
|
|
|
|
|
|
* 65 - vt525
|
|
|
|
|
|
*
|
|
|
|
|
|
* Param 2 - firmware version
|
|
|
|
|
|
* xterm uses its version number. We use an xterm
|
|
|
|
|
|
* version number too, since e.g. Emacs uses this to
|
|
|
|
|
|
* determine level of support.
|
2020-07-24 17:47:47 +02:00
|
|
|
|
*
|
|
|
|
|
|
* We report ourselves as a VT220. This must be
|
2020-08-15 19:39:00 +01:00
|
|
|
|
* synchronized with the primary DA response.
|
2020-07-24 17:47:47 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Note: tertiary DA replies with "FOOT".
|
2019-11-18 12:06:29 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2020-07-24 17:47:47 +02:00
|
|
|
|
static_assert(FOOT_MAJOR < 100, "Major version must not exceed 99");
|
|
|
|
|
|
static_assert(FOOT_MINOR < 100, "Minor version must not exceed 99");
|
|
|
|
|
|
static_assert(FOOT_PATCH < 100, "Patch version must not exceed 99");
|
|
|
|
|
|
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[>1;%02u%02u%02u;0c",
|
2020-07-24 17:47:47 +02:00
|
|
|
|
FOOT_MAJOR, FOOT_MINOR, FOOT_PATCH);
|
|
|
|
|
|
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
2019-07-07 16:32:18 +02:00
|
|
|
|
break;
|
2019-06-24 20:01:41 +02:00
|
|
|
|
|
2019-07-18 19:48:37 +02:00
|
|
|
|
case 'm':
|
|
|
|
|
|
if (term->vt.params.idx == 0) {
|
|
|
|
|
|
/* Reset all */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
int resource = vt_param_get(term, 0, 0);
|
2019-08-04 18:11:59 +02:00
|
|
|
|
int value = vt_param_get(term, 1, -1);
|
2019-07-18 19:48:37 +02:00
|
|
|
|
|
|
|
|
|
|
switch (resource) {
|
|
|
|
|
|
case 0: /* modifyKeyboard */
|
2019-08-04 18:11:59 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-18 19:48:37 +02:00
|
|
|
|
case 1: /* modifyCursorKeys */
|
|
|
|
|
|
case 2: /* modifyFunctionKeys */
|
|
|
|
|
|
/* Ignored, we always report modifiers */
|
2021-11-11 17:43:39 +01:00
|
|
|
|
if (value != 2 && value != -1) {
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"unimplemented: %s = %d",
|
|
|
|
|
|
resource == 1 ? "modifyCursorKeys" :
|
|
|
|
|
|
resource == 2 ? "modifyFunctionKeys" : "<invalid>",
|
|
|
|
|
|
value);
|
2019-08-04 18:11:59 +02:00
|
|
|
|
}
|
2019-07-18 19:48:37 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2021-11-11 17:43:39 +01:00
|
|
|
|
case 4: /* modifyOtherKeys */
|
|
|
|
|
|
term->modify_other_keys_2 = value == 2;
|
|
|
|
|
|
LOG_DBG("modifyOtherKeys=%d", value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-18 19:48:37 +02:00
|
|
|
|
default:
|
|
|
|
|
|
LOG_WARN("invalid resource %d in %s",
|
2020-02-01 19:42:31 +01:00
|
|
|
|
resource, csi_as_string(term, final, -1));
|
2019-07-18 19:48:37 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break; /* final == 'm' */
|
|
|
|
|
|
|
2021-02-16 12:00:58 +01:00
|
|
|
|
case 'q': {
|
|
|
|
|
|
/* XTVERSION */
|
|
|
|
|
|
if (vt_param_get(term, 0, 0) != 0) {
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char reply[64];
|
|
|
|
|
|
size_t n = xsnprintf(
|
|
|
|
|
|
reply, sizeof(reply), "\033P>|foot(%u.%u.%u%s%s)\033\\",
|
|
|
|
|
|
FOOT_MAJOR, FOOT_MINOR, FOOT_PATCH,
|
|
|
|
|
|
FOOT_EXTRA[0] != '\0' ? "-" : "", FOOT_EXTRA);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-24 20:01:41 +02:00
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-16 10:20:47 +02:00
|
|
|
|
break;
|
2019-06-24 20:01:41 +02:00
|
|
|
|
}
|
2019-07-10 15:03:16 +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
|
|
|
|
break; /* private[0] == '>' */
|
2019-06-24 20:01:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-16 10:20:54 +02:00
|
|
|
|
case ' ': {
|
|
|
|
|
|
switch (final) {
|
|
|
|
|
|
case 'q': {
|
|
|
|
|
|
int param = vt_param_get(term, 0, 0);
|
|
|
|
|
|
switch (param) {
|
2020-06-30 17:43:43 +02:00
|
|
|
|
case 0: /* blinking block, but we use it to reset to configured default */
|
2020-11-26 18:08:28 +01:00
|
|
|
|
term->cursor_style = term->conf->cursor.style;
|
term: split cursor blink state into two
There are two different escape sequences that can be used to set the
cursor blink state: ‘CSI ? 12 h/l’ and ‘CSI Ps SP q’.
Up until now, they both modified the same internal state in foot. This
meant you could enable a blinking cursor with e.g. ‘CSI ? 12 h’ and
then disable it with ‘CSI 2 SP q’.
Since the ‘CSI ? 12’ escapes are used in the civis/cnorm/cvvis
terminfo entries, applications often ended up disabling the blink
state on exit (typically be emitting ‘cnorm’), requiring users to
manually re-enable blinking.
By splitting the internal state into two separate states, we can
improve the situation.
The cursor will blink if at least one of the two have been enabled.
The setting in foot.ini sets the default state of the ‘CSI Ps SP q’
escape.
This means if the user has enabled blinking in the configuration, the
cursor will blink regardless of civis/cnorm/cvvis. Which probably is
what the user wants.
If the user has NOT enabled blinking, civis/cnorm/cvvis act as
intended: cvvis blink, civis and cnorm do not.
If an application overrides the cursor blink/style with ‘CSI Ps SP q’,
that will override the user’s setting in foot.ini. But most likely
that too is intended (for example, the user may have configured the
application to use a different cursor style). And, a well written
application will emit the ‘Se’ terminfo sequence on exit, which in
foot is defined to ‘CSI SP q’, which will reset both the style and
blink state to the user configured style/state.
Closes #218
2020-11-26 18:09:32 +01:00
|
|
|
|
term->cursor_blink.deccsusr = term->conf->cursor.blink;
|
|
|
|
|
|
term_cursor_blink_update(term);
|
2019-07-22 20:19:27 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-06-30 17:43:43 +02:00
|
|
|
|
case 1: /* blinking block */
|
|
|
|
|
|
case 2: /* steady block */
|
|
|
|
|
|
term->cursor_style = CURSOR_BLOCK;
|
2019-07-16 10:20:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
2019-07-22 19:44:21 +02:00
|
|
|
|
case 3: /* blinking underline */
|
|
|
|
|
|
case 4: /* steady underline */
|
|
|
|
|
|
term->cursor_style = CURSOR_UNDERLINE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 5: /* blinking bar */
|
|
|
|
|
|
case 6: /* steady bar */
|
2021-04-30 20:31:47 +02:00
|
|
|
|
term->cursor_style = CURSOR_BEAM;
|
2019-07-16 10:20:54 +02:00
|
|
|
|
break;
|
2020-05-04 20:19:24 +02:00
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
2019-07-16 10:20:54 +02:00
|
|
|
|
}
|
2019-07-22 19:44:21 +02:00
|
|
|
|
|
2020-06-30 17:43:43 +02:00
|
|
|
|
if (param > 0 && param <= 6) {
|
term: split cursor blink state into two
There are two different escape sequences that can be used to set the
cursor blink state: ‘CSI ? 12 h/l’ and ‘CSI Ps SP q’.
Up until now, they both modified the same internal state in foot. This
meant you could enable a blinking cursor with e.g. ‘CSI ? 12 h’ and
then disable it with ‘CSI 2 SP q’.
Since the ‘CSI ? 12’ escapes are used in the civis/cnorm/cvvis
terminfo entries, applications often ended up disabling the blink
state on exit (typically be emitting ‘cnorm’), requiring users to
manually re-enable blinking.
By splitting the internal state into two separate states, we can
improve the situation.
The cursor will blink if at least one of the two have been enabled.
The setting in foot.ini sets the default state of the ‘CSI Ps SP q’
escape.
This means if the user has enabled blinking in the configuration, the
cursor will blink regardless of civis/cnorm/cvvis. Which probably is
what the user wants.
If the user has NOT enabled blinking, civis/cnorm/cvvis act as
intended: cvvis blink, civis and cnorm do not.
If an application overrides the cursor blink/style with ‘CSI Ps SP q’,
that will override the user’s setting in foot.ini. But most likely
that too is intended (for example, the user may have configured the
application to use a different cursor style). And, a well written
application will emit the ‘Se’ terminfo sequence on exit, which in
foot is defined to ‘CSI SP q’, which will reset both the style and
blink state to the user configured style/state.
Closes #218
2020-11-26 18:09:32 +01:00
|
|
|
|
term->cursor_blink.deccsusr = param & 1;
|
|
|
|
|
|
term_cursor_blink_update(term);
|
2020-05-04 20:19:24 +02:00
|
|
|
|
}
|
2019-07-16 10:20:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-16 10:20:54 +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
|
|
|
|
break; /* private[0] == ' ' */
|
2019-07-16 10:20:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-17 10:39:38 +02:00
|
|
|
|
case '!': {
|
2021-08-30 07:40:03 +01:00
|
|
|
|
if (final == 'p') {
|
2019-08-01 20:51:11 +02:00
|
|
|
|
term_reset(term, false);
|
2019-07-17 10:39:38 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2021-08-30 07:40:03 +01:00
|
|
|
|
|
|
|
|
|
|
UNHANDLED();
|
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
|
|
|
|
break; /* private[0] == '!' */
|
2019-07-17 10:39:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-18 12:46:52 +01:00
|
|
|
|
case '=': {
|
|
|
|
|
|
switch (final) {
|
|
|
|
|
|
case 'c':
|
2019-12-14 14:38:03 +01:00
|
|
|
|
if (vt_param_get(term, 0, 0) != 0) {
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-14 19:10:59 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Send Device Attributes (Tertiary DA)
|
|
|
|
|
|
*
|
|
|
|
|
|
* Reply format is "DCS ! | DDDDDDDD ST"
|
|
|
|
|
|
*
|
|
|
|
|
|
* D..D is the unit ID of the terminal, consisting of four
|
|
|
|
|
|
* hexadecimal pairs. The first pair represents the
|
|
|
|
|
|
* manufacturing site code. This code can be any
|
|
|
|
|
|
* hexadecimal value from 00 through FF.
|
|
|
|
|
|
*/
|
2019-11-18 12:46:52 +01:00
|
|
|
|
|
2019-12-21 20:35:55 +01:00
|
|
|
|
term_to_slave(term, "\033P!|464f4f54\033\\", 14); /* FOOT */
|
2019-11-18 12:46:52 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
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
|
|
|
|
break; /* private[0] == '=' */
|
2019-11-18 12:46:52 +01: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
|
|
|
|
case 0x243f: /* ?$ */
|
|
|
|
|
|
switch (final) {
|
|
|
|
|
|
case 'p': {
|
|
|
|
|
|
unsigned param = vt_param_get(term, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Request DEC private mode (DECRQM)
|
|
|
|
|
|
* Reply:
|
|
|
|
|
|
* 0 - not recognized
|
|
|
|
|
|
* 1 - set
|
|
|
|
|
|
* 2 - reset
|
|
|
|
|
|
* 3 - permanently set
|
|
|
|
|
|
* 4 - permantently reset
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool enabled;
|
|
|
|
|
|
unsigned value;
|
|
|
|
|
|
if (decrqm(term, param, &enabled))
|
|
|
|
|
|
value = enabled ? 1 : 2;
|
|
|
|
|
|
else
|
|
|
|
|
|
value = 0;
|
|
|
|
|
|
|
|
|
|
|
|
char reply[32];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?%u;%u$y", param, value);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
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
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break; /* private[0] == ‘?’ && private[1] == ‘$’ */
|
|
|
|
|
|
|
2019-07-10 15:03:16 +02:00
|
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
|
UNHANDLED();
|
2019-07-10 15:03:16 +02:00
|
|
|
|
break;
|
2019-06-15 22:22:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|