Support colon-separated sub-params in CSI and DCS

This adds supports for sub-parameters in control sequences. For now, it
supports the colon-separated variants of SGR 38 and SGR 48 for RGB and
indexed colours:

CSI 38 : 2 : <id> : <red> : <green> : <blue> [ : ... ] m
CSI 38 : 2 : <red> : <green> : <blue> m
CSI 38 : 5 : <index8> m

and likewise for SGR 48. <id> is ignored (it's supposed to be a colour
space id, to support different kinds of RGB).
wilder
Luis Javier Merino Morán 4 years ago committed by Tomaz Canabrava
parent 497f9afbd5
commit 316a386d92
  1. 109
      src/Vt102Emulation.cpp
  2. 17
      src/Vt102Emulation.h

@ -270,28 +270,44 @@ const int MAX_ARGUMENT = 40960;
/* The tokenizer's state
The state is represented by the buffer (tokenBuffer, tokenBufferPos),
and accompanied by decoded arguments kept in (argv,argc).
and accompanied by decoded arguments kept in params.
Note that they are kept internal in the tokenizer.
*/
void Vt102Emulation::resetTokenizer()
{
tokenBufferPos = 0;
argc = 0;
argv[0] = 0;
argv[1] = 0;
params.count = 0;
params.value[0] = 0;
params.value[1] = 0;
params.sub[0].value[0] = 0;
params.sub[0].count = 0;
tokenState = -1;
}
void Vt102Emulation::addDigit(int digit)
{
argv[argc] = qMin(10 * argv[argc] + digit, MAX_ARGUMENT);
if (params.sub[params.count].count == 0) {
params.value[params.count] = qMin(10 * params.value[params.count] + digit, MAX_ARGUMENT);
} else {
struct subParam *sub = &params.sub[params.count];
sub->value[sub->count] = qMin(10 * sub->value[sub->count] + digit, MAX_ARGUMENT);
}
}
void Vt102Emulation::addArgument()
{
argc = qMin(argc + 1, MAXARGS - 1);
argv[argc] = 0;
params.count = qMin(params.count + 1, MAXARGS - 1);
params.value[params.count] = 0;
params.sub[params.count].value[0] = 0;
params.sub[params.count].count = 0;
}
void Vt102Emulation::addSub()
{
struct subParam *sub = &params.sub[params.count];
sub->count = qMin(sub->count + 1, MAXARGS - 1);
sub->value[sub->count] = 0;
}
void Vt102Emulation::addToCurrentToken(uint cc)
@ -439,11 +455,7 @@ void Vt102Emulation::clear()
{
_nIntermediate = 0;
_ignore = false;
tokenBufferPos = 0;
tokenState = -1;
argc = 0;
argv[0] = 0;
argv[1] = 0;
resetTokenizer();
}
#define MAX_INTERMEDIATES 1
@ -466,7 +478,7 @@ void Vt102Emulation::param(const uint cc)
} else if (cc == ';') {
addArgument();
} else if (cc == ':') {
_ignore = true;
addSub();
}
}
@ -476,39 +488,54 @@ void Vt102Emulation::csi_dispatch(const uint cc)
return;
if ((tokenBufferPos == 0 || (tokenBuffer[0] != '?' && tokenBuffer[0] != '!' && tokenBuffer[0] != '=' && tokenBuffer[0] != '>')) && cc < 256
&& (charClass[cc] & CPN) == CPN && _nIntermediate == 0) {
processToken(token_csi_pn(cc), argv[0], argv[1]);
processToken(token_csi_pn(cc), params.value[0], params.value[1]);
} else if ((tokenBufferPos == 0 || (tokenBuffer[0] != '?' && tokenBuffer[0] != '!' && tokenBuffer[0] != '=' && tokenBuffer[0] != '>')) && cc < 256
&& (charClass[cc] & CPS) == CPS && _nIntermediate == 0) {
processToken(token_csi_ps(cc, argv[0]), argv[1], argv[2]);
processToken(token_csi_ps(cc, params.value[0]), params.value[1], params.value[2]);
} else if (tokenBufferPos != 0 && tokenBuffer[0] == '!') {
processToken(token_csi_pe(cc), 0, 0);
} else if (_nIntermediate == 1 && _intermediate[0] == ' ') {
if (tokenBufferPos == 1) {
processToken(token_csi_sp(cc), 0, 0);
} else {
processToken(token_csi_psp(cc, argv[0]), 0, 0);
processToken(token_csi_psp(cc, params.value[0]), 0, 0);
}
} else if (cc == 'y' && _nIntermediate == 1 && _intermediate[0] == '*') {
processChecksumRequest(argc, argv);
processChecksumRequest(params.count, params.value);
} else {
for (int i = 0; i <= argc; i++) {
for (int i = 0; i <= params.count; i++) {
if (tokenBufferPos != 0 && tokenBuffer[0] == '?') {
processToken(token_csi_pr(cc, argv[i]), i, 0);
processToken(token_csi_pr(cc, params.value[i]), i, 0);
} else if (tokenBufferPos != 0 && tokenBuffer[0] == '=') {
processToken(token_csi_pq(cc), 0, 0);
} else if (tokenBufferPos != 0 && tokenBuffer[0] == '>') {
processToken(token_csi_pg(cc), 0, 0);
} else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i + 1] == 2) {
} else if (cc == 'm' && params.count - i >= 4 && (params.value[i] == 38 || params.value[i] == 48) && params.value[i + 1] == 2) {
// ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m
i += 2;
processToken(token_csi_ps(cc, argv[i - 2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i + 1] << 8) | argv[i + 2]);
processToken(token_csi_ps(cc, params.value[i - 2]),
COLOR_SPACE_RGB,
(params.value[i] << 16) | (params.value[i + 1] << 8) | params.value[i + 2]);
i += 2;
} else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i + 1] == 5) {
} else if (cc == 'm' && params.sub[i].count >= 5 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2) {
// ESC[ ... 48:2:<id>:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<id>:<red>:<green>:<blue> ... m
processToken(token_csi_ps(cc, params.value[i]),
COLOR_SPACE_RGB,
(params.sub[i].value[3] << 16) | (params.sub[i].value[4] << 8) | params.sub[i].value[5]);
} else if (cc == 'm' && params.sub[i].count == 4 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2) {
// ESC[ ... 48:2:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<red>:<green>:<blue> ... m
processToken(token_csi_ps(cc, params.value[i]),
COLOR_SPACE_RGB,
(params.sub[i].value[2] << 16) | (params.sub[i].value[3] << 8) | params.sub[i].value[4]);
} else if (cc == 'm' && params.count - i >= 2 && (params.value[i] == 38 || params.value[i] == 48) && params.value[i + 1] == 5) {
// ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m
i += 2;
processToken(token_csi_ps(cc, argv[i - 2]), COLOR_SPACE_256, argv[i]);
processToken(token_csi_ps(cc, params.value[i - 2]), COLOR_SPACE_256, params.value[i]);
} else if (cc == 'm' && params.sub[i].count >= 2 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 5) {
// ESC[ ... 48:5:<index> ... m -or- ESC[ ... 38:5:<index> ... m
processToken(token_csi_ps(cc, params.value[i]), COLOR_SPACE_256, params.sub[i].value[2]);
} else if (_nIntermediate == 0) {
processToken(token_csi_ps(cc, argv[i]), 0, 0);
processToken(token_csi_ps(cc, params.value[i]), 0, 0);
}
}
}
@ -2640,13 +2667,13 @@ void Vt102Emulation::sixelQuery(int q)
{
char tmp[30];
if (q == 1) {
if (argv[1] == 1 || argv[1] == 4) {
if (params.value[1] == 1 || params.value[1] == 4) {
snprintf(tmp, sizeof(tmp), "\033[?1;0;%dS", MAX_SIXEL_COLORS);
sendString(tmp);
}
}
if (q == 2) {
if (argv[1] == 1 || argv[1] == 4) {
if (params.value[1] == 1 || params.value[1] == 4) {
snprintf(tmp, sizeof(tmp), "\033[?2;0;%d;%dS", MAX_IMAGE_DIM, MAX_IMAGE_DIM);
sendString(tmp);
}
@ -2937,19 +2964,19 @@ bool Vt102Emulation::processSixel(uint cc)
}
addArgument();
if (argc == 4) {
if (params.count == 4) {
// We just ignore the pixel aspect ratio, it's dumb
// const int pixelWidth = argv[0];
// const int pixelHeight = argv[1];
// const int pixelWidth = params.value[0];
// const int pixelHeight = params.value[1];
if (!m_SixelStarted) {
if (argv[1] == 0 || argv[0] == 0) {
if (params.value[1] == 0 || params.value[0] == 0) {
m_aspect = qMakePair(1, 1);
} else {
m_aspect = qMakePair(argv[0], argv[1]);
m_aspect = qMakePair(params.value[0], params.value[1]);
}
const int width = argv[2];
const int height = argv[3];
const int width = params.value[2];
const int height = params.value[3];
SixelModeEnable(width, height);
}
resetTokenizer();
@ -2969,7 +2996,7 @@ bool Vt102Emulation::processSixel(uint cc)
return true;
}
SixelCharacterAdd(cc, argv[0]);
SixelCharacterAdd(cc, params.value[0]);
resetTokenizer();
return true;
}
@ -2979,24 +3006,24 @@ bool Vt102Emulation::processSixel(uint cc)
return true;
}
addArgument();
if (argc < 1) {
if (params.count < 1) {
return false;
}
const int index = argv[0];
if (argc == 5) {
const int colorspace = argv[1];
const int index = params.value[0];
if (params.count == 5) {
const int colorspace = params.value[1];
switch (colorspace) {
case 1:
// Confusingly it is in HLS order...
SixelColorChangeHSL(index, argv[2], argv[4], argv[3]);
SixelColorChangeHSL(index, params.value[2], params.value[4], params.value[3]);
break;
case 2:
SixelColorChangeRGB(index, argv[2], argv[3], argv[4]);
SixelColorChangeRGB(index, params.value[2], params.value[3], params.value[4]);
break;
default:
return false;
}
} else if (argc == 1 && index >= 0) { // Negative index is an error. Too large index is ignored
} else if (params.count == 1 && index >= 0) { // Negative index is an error. Too large index is ignored
if (index < MAX_SIXEL_COLORS) {
m_currentColor = index;
}

@ -129,11 +129,22 @@ protected:
uint tokenBuffer[MAX_TOKEN_LENGTH]; // FIXME: overflow?
private:
#define MAXARGS 15
#define MAXARGS 16
void addDigit(int dig);
void addArgument();
int argv[MAXARGS] = {};
int argc;
void addSub();
struct subParam {
int value[MAXARGS];
int count;
};
struct {
int value[MAXARGS];
struct subParam sub[MAXARGS];
int count;
} params = {};
void initTokenizer();
enum ParserStates {

Loading…
Cancel
Save