Terminal emulator with all the bells and whistles https://www.enlightenment.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4975 lines
133 KiB

#include "private.h"
#include <Elementary.h>
#include <stdint.h>
#include <assert.h>
#include "colors.h"
#include "termio.h"
#include "termpty.h"
#include "termptydbl.h"
#include "termptyesc.h"
#include "termptyops.h"
#include "termptyext.h"
#include "theme.h"
#if defined(BINARY_TYTEST)
#include "tytest.h"
#endif
#undef CRITICAL
#undef ERR
#undef WRN
#undef INF
#undef DBG
#define CRITICAL(...) EINA_LOG_DOM_CRIT(_termpty_log_dom, __VA_ARGS__)
#define ERR(...) EINA_LOG_DOM_ERR(_termpty_log_dom, __VA_ARGS__)
#define WRN(...) EINA_LOG_DOM_WARN(_termpty_log_dom, __VA_ARGS__)
#define INF(...) EINA_LOG_DOM_INFO(_termpty_log_dom, __VA_ARGS__)
#define DBG(...) EINA_LOG_DOM_DBG(_termpty_log_dom, __VA_ARGS__)
#define ST 0x9c // String Terminator
#define BEL 0x07 // Bell
#define ESC 033 // Escape
#define CSI 0x9b
#define OSC 0x9d
#define DEL 0x7f
/* XXX: all handle_ functions return the number of bytes successfully read, 0
* if not enough bytes could be read
*/
static const char *const ASCII_CHARS_TABLE[] =
{
"NUL", // '\0'
"SOH", // '\001'
"STX", // '\002'
"ETX", // '\003'
"EOT", // '\004'
"ENQ", // '\005'
"ACK", // '\006'
"BEL", // '\007'
"BS", // '\010'
"HT", // '\011'
"LF", // '\012'
"VT", // '\013'
"FF", // '\014'
"CR" , // '\015'
"SO", // '\016'
"SI", // '\017'
"DLE", // '\020'
"DC1", // '\021'
"DC2", // '\022'
"DC3", // '\023'
"DC4", // '\024'
"NAK", // '\025'
"SYN", // '\026'
"ETB", // '\027'
"CAN", // '\030'
"EM", // '\031'
"SUB", // '\032'
"ESC", // '\033'
"FS", // '\034'
"GS", // '\035'
"RS", // '\036'
"US" // '\037'
};
const char * EINA_PURE
termptyesc_safechar(const unsigned int c)
{
static char _str[9];
// This should avoid 'BEL' and 'ESC' in particular, which would
// have side effects in the parent terminal (esp. ESC).
if (c < (sizeof(ASCII_CHARS_TABLE) / sizeof(ASCII_CHARS_TABLE[0])))
return ASCII_CHARS_TABLE[c];
if (c == DEL)
return "DEL";
// The rest should be safe (?)
snprintf(_str, 9, "%c", c);
_str[8] = '\0';
return _str;
}
static Eina_Bool
_cursor_is_within_margins(const Termpty *ty)
{
return !(
((ty->termstate.top_margin > 0)
&& (ty->cursor_state.cy < ty->termstate.top_margin))
|| ((ty->termstate.bottom_margin > 0)
&& (ty->cursor_state.cy >= ty->termstate.bottom_margin))
|| ((ty->termstate.left_margin > 0)
&& (ty->cursor_state.cx < ty->termstate.left_margin))
|| ((ty->termstate.right_margin > 0)
&& (ty->cursor_state.cx >= ty->termstate.right_margin))
);
}
enum esc_arg_error {
ESC_ARG_NO_VALUE = 1,
ESC_ARG_ERROR = 2
};
static int
_csi_arg_get(Termpty *ty, Eina_Unicode **ptr)
{
Eina_Unicode *b = *ptr;
int sum = 0;
if ((b == NULL) || (*b == '\0'))
{
*ptr = NULL;
return -ESC_ARG_NO_VALUE;
}
/* Skip potential '?', '>'.... */
while ((*b) && ( (*b) != ';' && ((*b) < '0' || (*b) > '9')))
b++;
if (*b == ';')
{
b++;
*ptr = b;
return -ESC_ARG_NO_VALUE;
}
if (*b == '\0')
{
*ptr = NULL;
return -ESC_ARG_NO_VALUE;
}
while ((*b >= '0') && (*b <= '9'))
{
if (sum > INT32_MAX/10 )
{
ERR("Invalid sequence: argument is too large");
ty->decoding_error = EINA_TRUE;
goto error;
}
sum *= 10;
sum += *b - '0';
b++;
}
if ((*b == ';') || (*b == ':'))
{
if (b[1])
b++;
*ptr = b;
}
else if (*b == '\0')
{
*ptr = NULL;
}
else
{
*ptr = b;
}
return sum;
error:
ERR("Invalid CSI argument");
ty->decoding_error = EINA_TRUE;
*ptr = NULL;
return -ESC_ARG_ERROR;
}
static void
_tab_forward(Termpty *ty, int n)
{
int cx = ty->cursor_state.cx;
for (; n > 0; n--)
{
do
{
cx++;
}
while ((cx < ty->w) && (!TAB_TEST(ty, cx)));
}
ty->cursor_state.cx = cx;
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cx, 0, ty->w);
}
static void
_cursor_to_start_of_line(Termpty *ty)
{
ty->cursor_state.cx = ty->termstate.left_margin;
}
static void
_handle_cursor_control(Termpty *ty, const Eina_Unicode *cc)
{
Termcell *cell;
switch (*cc)
{
case 0x07: // BEL '\a' (bell)
DBG("->BEL");
if (ty->cb.bell.func) ty->cb.bell.func(ty->cb.bell.data);
return;
case 0x08: // BS '\b' (backspace)
DBG("->BS");
ty->termstate.wrapnext = 0;
ty->cursor_state.cx--;
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cx, 0, ty->w);
return;
case 0x09: // HT '\t' (horizontal tab)
DBG("->HT");
cell = &(TERMPTY_SCREEN(ty, ty->cursor_state.cx, ty->cursor_state.cy));
cell->att.tab_inserted = 1;
_tab_forward(ty, 1);
cell = &(TERMPTY_SCREEN(ty, ty->cursor_state.cx -1, ty->cursor_state.cy));
cell->att.tab_last = 1;
return;
case 0x0a: // LF '\n' (new line)
case 0x0b: // VT '\v' (vertical tab)
case 0x0c: // FF '\f' (form feed)
DBG("->LF");
ty->termstate.wrapnext = 0;
if (ty->termstate.crlf)
_cursor_to_start_of_line(ty);
ty->cursor_state.cy++;
termpty_text_scroll_test(ty, EINA_TRUE);
return;
case 0x0d: // CR '\r' (carriage ret)
DBG("->CR");
if (ty->cursor_state.cx != 0)
{
ty->termstate.had_cr_x = ty->cursor_state.cx;
ty->termstate.had_cr_y = ty->cursor_state.cy;
ty->termstate.wrapnext = 0;
}
_cursor_to_start_of_line(ty);
return;
default:
return;
}
}
static void
_switch_to_alternative_screen(Termpty *ty, int mode)
{
// swap screen content now
if (mode != ty->altbuf)
termpty_screen_swap(ty);
}
static void
_move_cursor_to_origin(Termpty *ty)
{
if (ty->termstate.restrict_cursor)
{
ty->cursor_state.cx = ty->termstate.left_margin;
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cx, 0, ty->w);
ty->cursor_state.cy = ty->termstate.top_margin;
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cy, 0, ty->h);
}
else
{
ty->cursor_state.cx = 0;
ty->cursor_state.cy = 0;
}
}
static void
_handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
const Eina_Unicode * const end)
{
int mode = 0, priv = 0, arg;
if (cc == 'h')
mode = 1;
if (*b == '?')
{
priv = 1;
b++;
}
if (priv) /* DEC Private Mode Reset (DECRST) */
{
while (b && b <= end)
{
arg = _csi_arg_get(ty, &b);
// complete-ish list here:
// http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html
switch (arg)
{
case -ESC_ARG_ERROR:
return;
/* TODO: -ESC_ARG_NO_VALUE */
case 1:
ty->termstate.appcursor = mode;
break;
case 2:
DBG("DECANM - ANSI MODE - VT52");
ty->termstate.kbd_lock = mode;
break;
case 3: // 132 column modeā€¦ should we handle this?
#if defined(SUPPORT_80_132_COLUMNS)
if (ty->termstate.att.is_80_132_mode_allowed)
{
/* ONLY FOR TESTING PURPOSE FTM */
Evas_Object *wn;
int w, h;
wn = termio_win_get(ty->obj);
elm_win_size_step_get(wn, &w, &h);
evas_object_resize(wn,
4 +
(mode ? 132 : 80) * w,
4 + ty->h * h);
termpty_resize(ty, mode ? 132 : 80,
ty->h);
termpty_reset_state(ty);
termpty_clear_screen(ty,
TERMPTY_CLR_ALL);
}
#endif
break;
case 4:
DBG("scrolling mode (DECSCLM): %i (always fast mode)", mode);
break;
case 5:
ty->termstate.reverse = mode;
break;
case 6:
/* DECOM */
if (mode)
{
/* set, within margins */
ty->termstate.restrict_cursor = 1;
}
else
{
ty->termstate.restrict_cursor = 0;
}
_move_cursor_to_origin(ty);
DBG("DECOM: mode (%d): cursor is at 0,0"
" cursor limited to screen/start point"
" for line #'s depends on top margin",
mode);
break;
case 7:
DBG("DECAWM: set/reset wrap mode to %i", mode);
ty->termstate.wrap = mode;
break;
case 8:
ty->termstate.no_autorepeat = !mode;
DBG("DECARM - auto repeat %i", mode);
break;
case 9:
DBG("set mouse (X10) %i", mode);
if (mode) ty->mouse_mode = MOUSE_X10;
else ty->mouse_mode = MOUSE_OFF;
break;
case 12: // ignore
WRN("TODO: set blinking cursor to (stop?) %i or local echo (ignored)", mode);
break;
case 19: // never seen this - what to do?
WRN("TODO: set print extent to full screen");
ty->decoding_error = EINA_TRUE;
break;
case 20: // crfl==1 -> cur moves to col 0 on LF, FF or VT, ==0 -> mode is cr+lf
ty->termstate.crlf = mode;
break;
case 25:
ty->termstate.hide_cursor = !mode;
DBG("hide cursor: %d", !mode);
break;
case 30: // ignore
WRN("TODO: set scrollbar mapping %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 33: // ignore
WRN("TODO: Stop cursor blink %i", mode);
break;
case 34: // ignore
WRN("TODO: Underline cursor mode %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 35: // ignore
WRN("TODO: set shift keys %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 38: // ignore
WRN("TODO: switch to tek window %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 40:
DBG("Allow 80 -> 132 Mode %i", mode);
#if defined(SUPPORT_80_132_COLUMNS)
ty->termstate.att.is_80_132_mode_allowed = mode;
#endif
break;
case 45: // ignore
WRN("TODO: Reverse-wraparound Mode");
ty->decoding_error = EINA_TRUE;
break;
case 47:
_switch_to_alternative_screen(ty, mode);
break;
case 59: // ignore
WRN("TODO: kanji terminal mode %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 66:
WRN("TODO: app keypad mode %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 67:
ty->termstate.send_bs = mode;
DBG("backspace send bs not del = %i", mode);
break;
case 69:
ty->termstate.lr_margins = mode;
if (!mode)
{
ty->termstate.left_margin = 0;
ty->termstate.right_margin = 0;
}
break;
case 98:
DBG("DECARSM Set/Reset Auto Resize Mode: %d", mode);
break;
case 100:
DBG("DECAAM Set/Reset Auto Answerback Mode: %d", mode);
break;
case 101:
DBG("DECCANSM Set/Reset Conceal Answerback Message Mode: %d", mode);
break;
case 109:
DBG("DECCAPSLK Set/Reset Caps Lock Mode: %d", mode);
break;
case 1000:
if (mode) ty->mouse_mode = MOUSE_NORMAL;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release only) to %i", mode);
break;
case 1001:
WRN("TODO: x11 mouse highlighting %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 1002:
if (mode) ty->mouse_mode = MOUSE_NORMAL_BTN_MOVE;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release+motion while pressed) %i", mode);
break;
case 1003:
if (mode) ty->mouse_mode = MOUSE_NORMAL_ALL_MOVE;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release+all motion) %i", mode);
break;
case 1004: // I don't know what focus reporting is?
WRN("TODO: enable focus reporting %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 1005:
if (mode) ty->mouse_ext = MOUSE_EXT_UTF8;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (xterm utf8 style) %i", mode);
break;
case 1006:
if (mode) ty->mouse_ext = MOUSE_EXT_SGR;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (xterm sgr style) %i", mode);
break;
case 1010: // ignore
WRN("TODO: set home on tty output %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 1012: // ignore
WRN("TODO: set home on tty input %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 1015:
if (mode) ty->mouse_ext = MOUSE_EXT_URXVT;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (rxvt-unicode style) %i", mode);
break;
case 1034: // ignore
/* libreadline6 emits it but it shouldn't.
See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=577012
*/
WRN("Ignored screen mode %i", arg);
break;
case 1047:
if (!mode && ty->altbuf)
/* clear screen before switching back to normal */
termpty_clear_screen(ty, TERMPTY_CLR_ALL);
_switch_to_alternative_screen(ty, mode);
break;
case 1048:
termpty_cursor_copy(ty, mode);
break;
case 1049:
if (mode)
{
// switch to altbuf
termpty_cursor_copy(ty, mode);
_switch_to_alternative_screen(ty, mode);
if (ty->altbuf)
/* clear screen before switching back to normal */
termpty_clear_screen(ty, TERMPTY_CLR_ALL);
}
else
{
if (ty->altbuf)
/* clear screen before switching back to normal */
termpty_clear_screen(ty, TERMPTY_CLR_ALL);
_switch_to_alternative_screen(ty, mode);
termpty_cursor_copy(ty, mode);
}
break;
case 2004:
ty->bracketed_paste = mode;
break;
case 7727: // ignore
WRN("TODO: enable application escape mode %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 7786: // ignore
WRN("TODO: enable mouse wheel -> cursor key xlation %i", mode);
ty->decoding_error = EINA_TRUE;
break;
default:
WRN("Unhandled DEC Private Reset Mode arg %i", arg);
ty->decoding_error = EINA_TRUE;
break;
}
}
}
else /* Reset Mode (RM) */
{
while (b && b <= end)
{
arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
/* TODO: -ESC_ARG_NO_VALUE */
case 1:
ty->termstate.appcursor = mode;
break;
case 3:
WRN("CRM - Show Control Character Mode: %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 4:
DBG("set insert mode to %i", mode);
ty->termstate.insert = mode;
break;
case 34:
WRN("TODO: hebrew keyboard mapping: %i", mode);
ty->decoding_error = EINA_TRUE;
break;
case 36:
WRN("TODO: hebrew encoding mode: %i", mode);
ty->decoding_error = EINA_TRUE;
break;
default:
WRN("Unhandled ANSI Reset Mode arg %i", arg);
ty->decoding_error = EINA_TRUE;
}
}
}
}
static int
_csi_truecolor_arg_get(Termpty *ty, Eina_Unicode **ptr)
{
Eina_Unicode *b = *ptr;
int sum = 0;
char separator;
if ((b == NULL) || (*b == '\0'))
{
*ptr = NULL;
return -ESC_ARG_NO_VALUE;
}
/* by construction, shall be the same separator as the following ones */
separator = *(b-1);
if ((separator != ';') && (separator != ':'))
{
*ptr = NULL;
return -ESC_ARG_NO_VALUE;
}
if (*b == separator)
{
b++;
*ptr = b;
return -ESC_ARG_NO_VALUE;
}
if (*b == '\0')
{
*ptr = NULL;
return -ESC_ARG_NO_VALUE;
}
/* invalid values */
if ((*b < '0') || (*b > '9'))
{
*ptr = NULL;
return -ESC_ARG_ERROR;
}
while ((*b >= '0') && (*b <= '9'))
{
if (sum > INT32_MAX/10 )
{
*ptr = NULL;
ERR("Invalid sequence: argument is too large");
ty->decoding_error = EINA_TRUE;
return -ESC_ARG_ERROR;
}
sum *= 10;
sum += *b - '0';
b++;
}
if (*b == separator)
{
b++;
*ptr = b;
}
else if (*b == '\0')
{
*ptr = NULL;
}
else
{
*ptr = b;
}
return sum;
}
#if !defined(BINARY_TYFUZZ)
/*********************************
* cache true color approximations
*********************************
* Approximating true colors is costly since it needs to compare 256 colors.
* The following considers that a few colors are used a lot. For example, one
* can consider that a text editor with syntax highlighting would only use a
* small palette.
* Given that, using the cache needs to be efficient: it needs to speed up the
* approximation when the color is already in the cache but not knowing the
* color requested is not in the cache needs to be efficient too.
*
* The cache is an array of @TCC_LEN uint32_t.
* Each entry has on the MSB the color as 3 uint8_t (R,G,B) and the LSB is
* the approximated color.
* Searching a color is simply going through the array and testing the mask on
* the 3 most significant bytes.
* When a color is found, it is bubbled up towards the start of the array by
* swapping this entry with the one above. This way the array is ordered to
* get most used colors as fast as possible.
* When a color is not found, the slow process of comparing it with the 256
* colors is used. Then the result is inserted in the lower half of the
* array. This ensures that new entries can live a bit and not be removed by
* the next new color. Using a modulo with a prime number makes for a
* nice-enough random generator to figure out where to insert.
*/
#define TCC_LEN 32
#define TCC_PRIME 17 /* smallest prime number larger than @TCC_LEN/2 */
static struct {
uint32_t colors[TCC_LEN];
} _truecolor_cache;
static int _tcc_random_pos = 0;
static Eina_Bool
_tcc_find(const uint32_t color_msb, uint8_t *chosen_color)
{
int i;
for (i = 0; i < TCC_LEN; i++)
{
if ((_truecolor_cache.colors[i] & 0xffffff00) == color_msb)
{
*chosen_color = _truecolor_cache.colors[i] & 0xff;
/* bubble up this result */
if (i > 0)
{
uint32_t prev = _truecolor_cache.colors[i - 1];
_truecolor_cache.colors[i - 1] = _truecolor_cache.colors[i];
_truecolor_cache.colors[i] = prev;
}
return EINA_TRUE;
}
}
return EINA_FALSE;
}
static void
_tcc_insert(const uint32_t color_msb, const uint8_t approximated)
{
uint32_t c = color_msb | approximated;
int i;
_tcc_random_pos = ((_tcc_random_pos + TCC_PRIME) % (TCC_LEN / 2));
i = (TCC_LEN / 2) + _tcc_random_pos;
_truecolor_cache.colors[i] = c;
}
#endif
static uint8_t
_approximate_truecolor_rgb(Termpty *ty, uint8_t r0, uint8_t g0, uint8_t b0)
{
uint8_t chosen_color = COL_DEF;
#if defined(BINARY_TYFUZZ)
(void) ty;
(void) r0;
(void) g0;
(void) b0;
#else
int c;
int distance_min = INT_MAX;
Evas_Object *textgrid;
const uint32_t color_msb = 0
| (((uint32_t)r0) << 24)
| (((uint32_t)g0) << 16)
| (((uint32_t)b0) << 8);
if (_tcc_find(color_msb, &chosen_color))
return chosen_color;
textgrid = termio_textgrid_get(ty->obj);
for (c = 0; c < 256; c++)
{
int r1 = 0, g1 = 0, b1 = 0, a1 = 0;
int delta_red_sq, delta_green_sq, delta_blue_sq, red_mean;
int distance;
evas_object_textgrid_palette_get(textgrid,
EVAS_TEXTGRID_PALETTE_EXTENDED,
c, &r1, &g1, &b1, &a1);
/* Compute the color distance
* XXX: this is inacurate but should give good enough results.
* See https://en.wikipedia.org/wiki/Color_difference
*/
red_mean = (r0 + r1) / 2;
delta_red_sq = (r0 - r1) * (r0 - r1);
delta_green_sq = (g0 - g1) * (g0 - g1);
delta_blue_sq = (b0 - b1) * (b0 - b1);
#if 1
distance = 2 * delta_red_sq
+ 4 * delta_green_sq
+ 3 * delta_blue_sq
+ ((red_mean) * (delta_red_sq - delta_blue_sq) / 256);
#else
/* from https://www.compuphase.com/cmetric.htm */
distance = (((512 + red_mean) * delta_red_sq) >> 8)
+ 4 * delta_green_sq
+ (((767 - red_mean) * delta_blue_sq) >> 8);
/* euclidian distance */
distance = delta_red_sq + delta_green_sq + delta_blue_sq;
(void)red_mean;
#endif
if (distance < distance_min)
{
distance_min = distance;
chosen_color = c;
}
}
_tcc_insert(color_msb, chosen_color);
#endif
return chosen_color;
}
static uint8_t
_handle_esc_csi_truecolor_rgb(Termpty *ty, Eina_Unicode **ptr)
{
int r, g, b;
Eina_Unicode *u = *ptr;
char separator;
if ((u == NULL) || (*u == '\0'))
{
return COL_DEF;
}
separator = *(u-1);
r = _csi_truecolor_arg_get(ty, ptr);
g = _csi_truecolor_arg_get(ty, ptr);
b = _csi_truecolor_arg_get(ty, ptr);
if ((r == -ESC_ARG_ERROR) ||
(g == -ESC_ARG_ERROR) ||
(b == -ESC_ARG_ERROR))
return COL_DEF;
if (separator == ':' && *ptr)
{
if (**ptr != ';')
{
/* then the first parameter was a color-space-id (ignored) */
r = g;
g = b;
b = _csi_truecolor_arg_get(ty, ptr);
/* Skip other parameters */
while ((*ptr) && (**ptr != ';'))
{
int arg = _csi_truecolor_arg_get(ty, ptr);
if (arg == -ESC_ARG_ERROR)
break;
}
}
if ((*ptr) && (**ptr == ';'))
{
*ptr = (*ptr) + 1;
}
}
if (r == -ESC_ARG_NO_VALUE)
r = 0;
if (g == -ESC_ARG_NO_VALUE)
g = 0;
if (b == -ESC_ARG_NO_VALUE)
b = 0;
return _approximate_truecolor_rgb(ty, (uint8_t)r, (uint8_t)g, (uint8_t)b);
}
static uint8_t
_handle_esc_csi_truecolor_cmy(Termpty *ty, Eina_Unicode **ptr)
{
int r, g, b, c, m, y;
Eina_Unicode *u = *ptr;
char separator;
if ((u == NULL) || (*u == '\0'))
{
return COL_DEF;
}
separator = *(u-1);
/* Considering CMY stored as percents */
c = _csi_truecolor_arg_get(ty, ptr);
m = _csi_truecolor_arg_get(ty, ptr);
y = _csi_truecolor_arg_get(ty, ptr);
if ((c == -ESC_ARG_ERROR) ||
(m == -ESC_ARG_ERROR) ||
(y == -ESC_ARG_ERROR))
return COL_DEF;
if (separator == ':' && *ptr)
{
if (**ptr != ';')
{
/* then the first parameter was a color-space-id (ignored) */
c = m;
m = y;
y = _csi_truecolor_arg_get(ty, ptr);
/* Skip other parameters */
while ((*ptr) && (**ptr != ';'))
{
int arg = _csi_truecolor_arg_get(ty, ptr);
if (arg == -ESC_ARG_ERROR)
break;
}
}
if ((*ptr) && (**ptr == ';'))
{
*ptr = (*ptr) + 1;
}
}
if (c == -ESC_ARG_NO_VALUE)
c = 0;
if (m == -ESC_ARG_NO_VALUE)
m = 0;
if (y == -ESC_ARG_NO_VALUE)
y = 0;
r = 255 - ((c * 255) / 100);
g = 255 - ((m * 255) / 100);
b = 255 - ((y * 255) / 100);
return _approximate_truecolor_rgb(ty, (uint8_t)r, (uint8_t)g, (uint8_t)b);
}
static uint8_t
_handle_esc_csi_truecolor_cmyk(Termpty *ty, Eina_Unicode **ptr)
{
int r, g, b, c, m, y, k;
Eina_Unicode *u = *ptr;
char separator;
if ((u == NULL) || (*u == '\0'))
{
return COL_DEF;
}
separator = *(u-1);
/* Considering CMYK stored as percents */
c = _csi_truecolor_arg_get(ty, ptr);
m = _csi_truecolor_arg_get(ty, ptr);
y = _csi_truecolor_arg_get(ty, ptr);
k = _csi_truecolor_arg_get(ty, ptr);
if ((c == -ESC_ARG_ERROR) ||
(m == -ESC_ARG_ERROR) ||
(y == -ESC_ARG_ERROR) ||
(k == -ESC_ARG_ERROR))
return COL_DEF;
if (separator == ':' && *ptr)
{
if (**ptr != ';')
{
/* then the first parameter was a color-space-id (ignored) */
c = m;
m = y;
y = k;
k = _csi_truecolor_arg_get(ty, ptr);
/* Skip other parameters */
while ((*ptr) && (**ptr != ';'))
{
int arg = _csi_truecolor_arg_get(ty, ptr);
if (arg == -ESC_ARG_ERROR)
break;
}
}
if ((*ptr) && (**ptr == ';'))
{
*ptr = (*ptr) + 1;
}
}
if (c == -ESC_ARG_NO_VALUE)
c = 0;
if (m == -ESC_ARG_NO_VALUE)
m = 0;
if (y == -ESC_ARG_NO_VALUE)
y = 0;
if (k == -ESC_ARG_NO_VALUE)
k = 0;
r = (255 * (100 - c) * (100 - k)) / 100 / 100;
g = (255 * (100 - m) * (100 - k)) / 100 / 100;
b = (255 * (100 - y) * (100 - k)) / 100 / 100;
return _approximate_truecolor_rgb(ty, (uint8_t)r, (uint8_t)g, (uint8_t)b);
}
static void
_handle_esc_csi_color_set(Termpty *ty, Eina_Unicode **ptr,
const Eina_Unicode * const end)
{
Eina_Unicode *b = *ptr;
if (b && (*b == '>'))
{ // key resources used by xterm
WRN("TODO: set/reset key resources used by xterm");
ty->decoding_error = EINA_TRUE;
return;
}
DBG("color set");
while (b && b <= end)
{
int arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0: // reset to normal
termpty_reset_att(&(ty->termstate.att));
break;
case 1: // bold/bright
ty->termstate.att.bold = 1;
break;
case 2: // faint
ty->termstate.att.faint = 1;
break;
case 3: // italic
ty->termstate.att.italic = 1;
break;
case 4: // underline
ty->termstate.att.underline = 1;
break;
case 5: // blink
ty->termstate.att.blink = 1;
break;
case 6: // blink rapid
ty->termstate.att.blink2 = 1;
break;
case 7: // reverse
ty->termstate.att.inverse = 1;
break;
case 8: // invisible
ty->termstate.att.invisible = 1;
break;
case 9: // strikethrough
ty->termstate.att.strike = 1;
break;
case 20: // fraktur!
ty->termstate.att.fraktur = 1;
break;
case 21: // no bold/bright
ty->termstate.att.bold = 0;
break;
case 22: // no bold/bright, no faint
ty->termstate.att.bold = 0;
ty->termstate.att.faint = 0;
break;
case 23: // no italic, not fraktur
ty->termstate.att.italic = 0;
ty->termstate.att.fraktur = 0;
break;
case 24: // no underline
ty->termstate.att.underline = 0;
break;
case 25: // no blink
ty->termstate.att.blink = 0;
ty->termstate.att.blink2 = 0;
break;
case 27: // no reverse
ty->termstate.att.inverse = 0;
break;
case 28: // no invisible
ty->termstate.att.invisible = 0;
break;
case 29: // no strikethrough
ty->termstate.att.strike = 0;
break;
case 30: // fg
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
ty->termstate.att.fg256 = 0;
ty->termstate.att.fg = (arg - 30) + COL_BLACK;
ty->termstate.att.fgintense = 0;
break;
case 38: // xterm 256 fg color ???
arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
/* TODO: -ESC_ARG_NO_VALUE */
case 1:
ty->termstate.att.fg256 = 0;
ty->termstate.att.fg = COL_INVIS;
break;
case 2:
ty->termstate.att.fg256 = 1;
ty->termstate.att.fg =
_handle_esc_csi_truecolor_rgb(ty, &b);
DBG("truecolor RGB fg: approximation got color %d",
ty->termstate.att.fg);
break;
case 3:
ty->termstate.att.fg256 = 1;
ty->termstate.att.fg =
_handle_esc_csi_truecolor_cmy(ty, &b);
DBG("truecolor CMY fg: approximation got color %d",
ty->termstate.att.fg);
break;
case 4:
ty->termstate.att.fg256 = 1;
ty->termstate.att.fg =
_handle_esc_csi_truecolor_cmyk(ty, &b);
DBG("truecolor CMYK fg: approximation got color %d",
ty->termstate.att.fg);
break;
case 5:
// then get next arg - should be color index 0-255
arg = _csi_arg_get(ty, &b);
if (arg <= -ESC_ARG_ERROR || arg > 255)
{
ERR("Invalid fg color %d", arg);
ty->decoding_error = EINA_TRUE;
}
else
{
if (arg == -ESC_ARG_NO_VALUE)
arg = 0;
ty->termstate.att.fg256 = 1;
ty->termstate.att.fg = arg;
}
break;
default:
ERR("Failed xterm 256 color fg (got %d)", arg);
ty->decoding_error = EINA_TRUE;
}
ty->termstate.att.fgintense = 0;
break;
case 39: // default fg color
ty->termstate.att.fg256 = 0;
ty->termstate.att.fg = COL_DEF;
ty->termstate.att.fgintense = 0;
break;
case 40: // bg
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
ty->termstate.att.bg256 = 0;
ty->termstate.att.bg = (arg - 40) + COL_BLACK;
ty->termstate.att.bgintense = 0;
break;
case 48: // xterm 256 bg color ???
arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
/* TODO: -ESC_ARG_NO_VALUE */
case 1:
ty->termstate.att.bg256 = 0;
ty->termstate.att.bg = COL_INVIS;
break;
case 2:
ty->termstate.att.bg256 = 1;
ty->termstate.att.bg =
_handle_esc_csi_truecolor_rgb(ty, &b);
DBG("truecolor RGB bg: approximation got color %d",
ty->termstate.att.bg);
break;
case 3:
ty->termstate.att.bg256 = 1;
ty->termstate.att.bg =
_handle_esc_csi_truecolor_cmy(ty, &b);
DBG("truecolor CMY bg: approximation got color %d",
ty->termstate.att.bg);
break;
case 4:
ty->termstate.att.bg256 = 1;
ty->termstate.att.bg =
_handle_esc_csi_truecolor_cmyk(ty, &b);
DBG("truecolor CMYK bg: approximation got color %d",
ty->termstate.att.bg);
break;
case 5:
// then get next arg - should be color index 0-255
arg = _csi_arg_get(ty, &b);
if (arg <= -ESC_ARG_ERROR || arg > 255)
{
ERR("Invalid bg color %d", arg);
ty->decoding_error = EINA_TRUE;
}
else
{
if (arg == -ESC_ARG_NO_VALUE)
arg = 0;
ty->termstate.att.bg256 = 1;
ty->termstate.att.bg = arg;
}
break;
default:
ERR("Failed xterm 256 color bg (got %d)", arg);
ty->decoding_error = EINA_TRUE;
}
ty->termstate.att.bgintense = 0;
break;
case 49: // default bg color
ty->termstate.att.bg256 = 0;
ty->termstate.att.bg = COL_DEF;
ty->termstate.att.bgintense = 0;
break;
case 51:
WRN("TODO: support SGR 51 - framed attribute");
ty->termstate.att.framed = 1;
break;
case 52:
ty->termstate.att.encircled = 1;
break;
case 53:
WRN("TODO: support SGR 51 - overlined attribute");
ty->termstate.att.overlined = 1;
break;
case 54:
ty->termstate.att.framed = 0;
ty->termstate.att.encircled = 0;
break;
case 55:
ty->termstate.att.overlined = 0;
break;
case 90: // fg
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
ty->termstate.att.fg256 = 0;
ty->termstate.att.fg = (arg - 90) + COL_BLACK;
ty->termstate.att.fgintense = 1;
break;
case 99: // default fg color
ty->termstate.att.fg256 = 0;
ty->termstate.att.fg = COL_DEF;
ty->termstate.att.fgintense = 1;
break;
case 100: // bg
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
ty->termstate.att.bg256 = 0;
ty->termstate.att.bg = (arg - 100) + COL_BLACK;
ty->termstate.att.bgintense = 1;
break;
case 109: // default bg color
ty->termstate.att.bg256 = 0;
ty->termstate.att.bg = COL_DEF;
ty->termstate.att.bgintense = 1;
break;
default: // not handled???
WRN("Unhandled color cmd [%i]", arg);
ty->decoding_error = EINA_TRUE;
break;
}
}
}
static void
_handle_esc_csi_cnl(Termpty *ty, Eina_Unicode **ptr)
{
Eina_Unicode *b = *ptr;
int arg = _csi_arg_get(ty, &b);
int max = ty->h;
if (arg == -ESC_ARG_ERROR)
return;
if (arg < 1)
arg = 1;
DBG("CNL - Cursor Next Line: %d", arg);
ty->termstate.wrapnext = 0;
ty->cursor_state.cy += arg;
if (ty->termstate.bottom_margin)
{
max = ty->termstate.bottom_margin;
}
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cy,
ty->termstate.top_margin,
max);
ty->cursor_state.cx = ty->termstate.left_margin;
}
static void
_handle_esc_csi_cpl(Termpty *ty, Eina_Unicode **ptr)
{
Eina_Unicode *b = *ptr;
int arg = _csi_arg_get(ty, &b);
int max = ty->h;
if (arg == -ESC_ARG_ERROR)
return;
if (arg < 1)
arg = 1;
DBG("CPL - Cursor Previous Line: %d", arg);
ty->termstate.wrapnext = 0;
ty->cursor_state.cy -= arg;
if (ty->termstate.bottom_margin)
{
max = ty->termstate.bottom_margin;
}
TERMPTY_RESTRICT_FIELD(ty->cursor_state.cy,
ty->termstate.top_margin,
max);
ty->cursor_state.cx = ty->termstate.left_margin;
}
static void
_handle_esc_csi_dch(Termpty *ty, Eina_Unicode **ptr)
{
Eina_Unicode *b = *ptr;
int arg = _csi_arg_get(ty, &b);
Termcell *cells;
int x, lim, max;
if (arg == -ESC_ARG_ERROR)
return;
DBG("DCH - Delete Character: %d chars", arg);
cells = &(TERMPTY_SCREEN(ty, 0, ty->cursor_state.cy));
max = ty->w;
if (ty->termstate.left_margin)
{
if (ty->cursor_state.cx < ty->termstate.left_margin)
return;
}
if (ty->termstate.right_margin)
{
if (ty->cursor_state.cx > ty->termstate.right_margin)
return;
max = ty->termstate.right_margin;
}
TERMPTY_RESTRICT_FIELD(arg, 1, max + 1);
lim = max - arg;
for (x = ty->cursor_state.cx; x < max; x++)
{
if (x < lim)
TERMPTY_CELL_COPY(ty, &(cells[x + arg]), &(cells[x]), 1);
else
{
cells[x].codepoint = ' ';
if (EINA_UNLIKELY(cells[x].att.link_id))
term_link_refcount_dec(ty, cells[x].att.link_id, 1);
cells[x].att = ty->termstate.att;
cells[x].att.link_id = 0;
cells[x].att.dblwidth = 0;
}
}
}
static void
_handle_esc_csi_dsr(Termpty *ty, Eina_Unicode *b)
{
int arg, len;
char bf[32];
Eina_Bool question_mark = EINA_FALSE;
if (*b == '>')
{
WRN("TODO: disable key resources used by xterm");
ty->decoding_error = EINA_TRUE;
return;
}
if (*b == '?')
{
question_mark = EINA_TRUE;
b++;
}
arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case 5:
if (question_mark)
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
else
{
/* DSR-OS (Operating Status)
* Reply Ok */
TERMPTY_WRITE_STR("\033[0n");
}
break;
case 6:
DBG("CPR - Cursor Position Report");
{
int cx = ty->cursor_state.cx,
cy = ty->cursor_state.cy;
if (ty->termstate.restrict_cursor)
{
if (ty->termstate.top_margin)
cy -= ty->termstate.top_margin;
if (ty->termstate.left_margin)
cx -= ty->termstate.left_margin;
}
if (question_mark)
{
len = snprintf(bf, sizeof(bf), "\033[?%d;%d;1R",
cy + 1,
cx + 1);
}
else
{
len = snprintf(bf, sizeof(bf), "\033[%d;%dR",
cy + 1,
cx + 1);
}
termpty_write(ty, bf, len);
}
break;
case 15:
if (question_mark)
{
/* DSR-PP (Printer Port)
* Reply None */
termpty_write(ty, "\033[?13n",
strlen("\033[?13n"));
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
case 25:
if (question_mark)
{
/* DSR-UDK (User-Defined Keys)
* Reply Unlocked */
termpty_write(ty, "\033[?20n",
strlen("\033[?20n"));
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
case 26:
if (question_mark)
{
/* DSR-KBD (Keyboard)
* Reply North American */
termpty_write(ty, "\033[?27;1;0;0n",
strlen("\033[?27;1;0;0n"));
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
case 62:
if (question_mark)
{
/* DSR-MSR (Macro Space Report)
* Reply 0 */
termpty_write(ty, "\033[0000*{",
strlen("\033[0000*{"));
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
case 63:
if (question_mark)
{
/* DSR-DECCKSR (Memory Checksum) */
int pid = _csi_arg_get(ty, &b);
if (pid == -ESC_ARG_NO_VALUE)
pid = 65535;
len = snprintf(bf, sizeof(bf), "\033P%u!~0000\033\\",
((unsigned int)pid) % 65536);
termpty_write(ty, bf, len);
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
case 75:
if (question_mark)
{
/* DSR-DIR (Data Integrity Report) */
termpty_write(ty, "\033[?70n", strlen("\033[?70n"));
}
else
{
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
}
break;
/* TODO: -ESC_ARG_NO_VALUE */
default:
WRN("unhandled DSR (dec specific: %s) %d",
(question_mark)? "yes": "no", arg);
ty->decoding_error = EINA_TRUE;
break;
}
}
static void
_handle_esc_csi_decslrm(Termpty *ty, Eina_Unicode **b)
{
int left = _csi_arg_get(ty, b);
int right = _csi_arg_get(ty, b);
DBG("DECSLRM (%d;%d) Set Left and Right Margins", left, right);
if ((left == -ESC_ARG_ERROR) || (right == -ESC_ARG_ERROR))
goto bad;
TERMPTY_RESTRICT_FIELD(left, 1, ty->w);
if (right < 1)
right = ty->w;
TERMPTY_RESTRICT_FIELD(right, 3, ty->w+1);
if (left >= right)
goto bad;
if (right - left < 2)
goto bad;
if (right == ty->w)
right = 0;
ty->termstate.left_margin = left - 1;
ty->termstate.right_margin = right;
_move_cursor_to_origin(ty);
return;
bad:
ty->termstate.left_margin = 0;
ty->termstate.right_margin = 0;
}
static void
_handle_esc_csi_decstbm(Termpty *ty, Eina_Unicode **b)
{
int top = _csi_arg_get(ty, b);
int bottom = _csi_arg_get(ty, b);
DBG("DECSTBM (%d;%d) Set Top and Bottom Margins", top, bottom);
if ((top == -ESC_ARG_ERROR) || (bottom == -ESC_ARG_ERROR))
goto bad;
TERMPTY_RESTRICT_FIELD(top, 1, ty->h);
TERMPTY_RESTRICT_FIELD(bottom, 1, ty->h+1);
if (top >= bottom) goto bad;
if (bottom == ty->h)
bottom = 0;
ty->termstate.top_margin = top - 1;
ty->termstate.bottom_margin = bottom;
_move_cursor_to_origin(ty);
return;
bad:
ty->termstate.top_margin = 0;
ty->termstate.bottom_margin = 0;
}
static int
_clean_up_rect_coordinates(Termpty *ty,
int *top_ptr,
int *left_ptr,
int *bottom_ptr,
int *right_ptr)
{
int top = *top_ptr;
int left = *left_ptr;
int bottom = *bottom_ptr;
int right = *right_ptr;
TERMPTY_RESTRICT_FIELD(top, 1, ty->h);
if (ty->termstate.restrict_cursor)
{
top += ty->termstate.top_margin;
if (ty->termstate.bottom_margin && top >= ty->termstate.bottom_margin)
top = ty->termstate.bottom_margin;
}
top--;
TERMPTY_RESTRICT_FIELD(left, 1, ty->w);
if (ty->termstate.restrict_cursor)
{
left += ty->termstate.left_margin;
if (ty->termstate.right_margin && left >= ty->termstate.right_margin)
left = ty->termstate.right_margin;
}
left--;
if (right < 1)
right = ty->w;
if (ty->termstate.restrict_cursor)
{
right += ty->termstate.left_margin;
if (ty->termstate.right_margin && right >= ty->termstate.right_margin)
right = ty->termstate.right_margin;
}
if (right > ty->w)
right = ty->w;
if (bottom < 1)
bottom = ty->h;
if (ty->termstate.restrict_cursor)
{
bottom += ty->termstate.top_margin;
if (ty->termstate.bottom_margin && bottom >= ty->termstate.bottom_margin)
bottom = ty->termstate.bottom_margin;
}
bottom--;
if (bottom > ty->h)
bottom = ty->h - 1;
if ((bottom < top) || (right < left))
return -1;
*top_ptr = top;
*left_ptr = left;
*bottom_ptr = bottom;
*right_ptr = right;
return 0;
}
static int
_clean_up_from_to_coordinates(Termpty *ty,
int *top_ptr,
int *left_ptr,
int *bottom_ptr,
int *right_ptr,
int *left_border,
int *right_border)
{
int top = *top_ptr;
int left = *left_ptr;
int bottom = *bottom_ptr;
int right = *right_ptr;
TERMPTY_RESTRICT_FIELD(top, 1, ty->h);
if (ty->termstate.restrict_cursor)
{
top += ty->termstate.top_margin;
if (ty->termstate.bottom_margin && top >= ty->termstate.bottom_margin)
top = ty->termstate.bottom_margin;
}
top--;
TERMPTY_RESTRICT_FIELD(left, 1, ty->w);
if (ty->termstate.restrict_cursor)
{
left += ty->termstate.left_margin;
if (ty->termstate.right_margin && left >= ty->termstate.right_margin)
left = ty->termstate.right_margin;
}
left--;
if (right < 1)
right = ty->w;
if (ty->termstate.restrict_cursor)
{
right += ty->termstate.left_margin;
if (ty->termstate.right_margin && right >= ty->termstate.right_margin)
right = ty->termstate.right_margin;
}
if (right > ty->w)
right = ty->w;
if (bottom < 1)
bottom = ty->h;
if (ty->termstate.restrict_cursor)
{
bottom += ty->termstate.top_margin;
if (ty->termstate.bottom_margin && bottom >= ty->termstate.bottom_margin)
bottom = ty->termstate.bottom_margin;
}
bottom--;
if (bottom > ty->h)
bottom = ty->h - 1;
if ((bottom == top) && (right < left))
return -1;
*left_border = 0;
*right_border = ty->w-1;
if (ty->termstate.restrict_cursor)
{
*left_border = ty->termstate.left_margin;
*right_border = ty->termstate.right_margin;
}
*top_ptr = top;
*left_ptr = left;
*bottom_ptr = bottom;
*right_ptr = right;
return 0;
}
static void
_handle_esc_csi_decfra(Termpty *ty, Eina_Unicode **b)
{
int c = _csi_arg_get(ty, b);
int top = _csi_arg_get(ty, b);
int left = _csi_arg_get(ty, b);
int bottom = _csi_arg_get(ty, b);
int right = _csi_arg_get(ty, b);
int len;
DBG("DECFRA (%d; %d;%d;%d;%d) Fill Rectangular Area",
c, top, left, bottom, right);
if ((c == -ESC_ARG_ERROR) ||
(c == -ESC_ARG_NO_VALUE) ||
(top == -ESC_ARG_ERROR) ||
(left == -ESC_ARG_ERROR) ||
(bottom == -ESC_ARG_ERROR) ||
(right == -ESC_ARG_ERROR))
return;
if (! ((c >= 32 && c <= 126) || (c >= 160 && c <= 255)))
return;
if (_clean_up_rect_coordinates(ty, &top, &left, &bottom, &right) < 0)
return;
len = right - left;
for (; top <= bottom; top++)
{
Termcell *cells = &(TERMPTY_SCREEN(ty, left, top));
termpty_cells_att_fill_preserve_colors(ty, cells, c, len);
}
}
static void
_deccara(Termcell *cells, int len,
Eina_Bool set_bold, Eina_Bool reset_bold,
Eina_Bool set_underline, Eina_Bool reset_underline,
Eina_Bool set_blink, Eina_Bool reset_blink,
Eina_Bool set_inverse, Eina_Bool reset_inverse)
{
int i;
for (i = 0; i < len; i++)
{
Termatt * att = &cells[i].att;
if (set_bold)
att->bold = 1;
if (set_underline)
att->underline = 1;
if (set_blink)
att->blink = 1;
if (set_inverse)
att->inverse = 1;
if (reset_bold)
att->bold = 0;
if (reset_underline)
att->underline = 0;
if (reset_blink)
att->blink = 0;
if (reset_inverse)
att->inverse = 0;
}
}
static void
_handle_esc_csi_deccara(Termpty *ty, Eina_Unicode **ptr,
const Eina_Unicode * const end)
{
Eina_Unicode *b = *ptr;
Termcell *cells;
int top;
int left;
int bottom;
int right;
int len;
Eina_Bool set_bold = EINA_FALSE, reset_bold = EINA_FALSE;
Eina_Bool set_underline = EINA_FALSE, reset_underline = EINA_FALSE;
Eina_Bool set_blink = EINA_FALSE, reset_blink = EINA_FALSE;
Eina_Bool set_inverse = EINA_FALSE, reset_inverse = EINA_FALSE;
top = _csi_arg_get(ty, &b);
left = _csi_arg_get(ty, &b);
bottom = _csi_arg_get(ty, &b);
right = _csi_arg_get(ty, &b);
DBG("DECCARA (%d;%d;%d;%d) Change Attributes in Rectangular Area",
top, left, bottom, right);
if ((top == -ESC_ARG_ERROR) ||
(left == -ESC_ARG_ERROR) ||
(bottom == -ESC_ARG_ERROR) ||
(right == -ESC_ARG_ERROR))
return;
while (b && b < end)
{
int arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0:
set_bold = set_underline = set_blink = set_inverse = EINA_FALSE;
reset_bold = reset_underline = reset_blink = reset_inverse = EINA_TRUE;
break;
case 1:
set_bold = EINA_TRUE;
reset_bold = EINA_FALSE;
break;
case 4:
set_underline = EINA_TRUE;
reset_underline = EINA_FALSE;
break;
case 5:
set_blink = EINA_TRUE;
reset_blink = EINA_FALSE;
break;
case 7:
set_inverse = EINA_TRUE;
reset_inverse = EINA_FALSE;
break;
case 22:
set_bold = EINA_FALSE;
reset_bold = EINA_TRUE;
break;
case 24:
set_underline = EINA_FALSE;
reset_underline = EINA_TRUE;
break;
case 25:
set_blink = EINA_FALSE;
reset_blink = EINA_TRUE;
break;
case 27:
set_inverse = EINA_FALSE;
reset_inverse = EINA_TRUE;
break;
default:
WRN("Invalid change attribute [%i]", arg);
ty->decoding_error = EINA_TRUE;
return;
}
}
if (ty->termstate.sace_rectangular)
{
if (_clean_up_rect_coordinates(ty, &top, &left, &bottom, &right) < 0)
return;
len = right - left;
for (; top <= bottom; top++)
{
cells = &(TERMPTY_SCREEN(ty, left, top));
_deccara(cells, len, set_bold, reset_bold, set_underline,
reset_underline, set_blink, reset_blink, set_inverse,
reset_inverse);
}
}
else
{
int left_border = 0;
int right_border = ty->w - 1;
if (_clean_up_from_to_coordinates(ty, &top, &left, &bottom, &right,
&left_border, &right_border) < 0)
return;
if (top == bottom)
{
cells = &(TERMPTY_SCREEN(ty, left, top));
len = right - left;
_deccara(cells, len, set_bold, reset_bold,
set_underline, reset_underline,
set_blink, reset_blink,
set_inverse, reset_inverse);
}
else
{
/* First line */
cells = &(TERMPTY_SCREEN(ty, left, top));
len = right_border - left;
_deccara(cells, len, set_bold, reset_bold,
set_underline, reset_underline,
set_blink, reset_blink,
set_inverse, reset_inverse);
/* Middle */
len = right_border - left_border;
for (top = top + 1; top < bottom; top++)
{
cells = &(TERMPTY_SCREEN(ty, left_border, top));
_deccara(cells, len, set_bold, reset_bold,
set_underline, reset_underline,
set_blink, reset_blink,
set_inverse, reset_inverse);
}
/* Last line */
cells = &(TERMPTY_SCREEN(ty, left_border, bottom));
len = right - left_border;
_deccara(cells, len, set_bold, reset_bold,
set_underline, reset_underline,
set_blink, reset_blink,
set_inverse, reset_inverse);
}
}
}
static void
_decrara(Termcell *cells, int len,
Eina_Bool reverse_bold,
Eina_Bool reverse_underline,
Eina_Bool reverse_blink,
Eina_Bool reverse_inverse)
{
int i;
for (i = 0; i < len; i++)
{
Termatt * att = &cells[i].att;
if (reverse_bold)
att->bold = !att->bold;
if (reverse_underline)
att->underline = !att->underline;
if (reverse_blink)
att->blink = !att->blink;
if (reverse_inverse)
att->inverse = !att->inverse;
}
}
static void
_handle_esc_csi_decrara(Termpty *ty, Eina_Unicode **ptr,
const Eina_Unicode * const end)
{
Eina_Unicode *b = *ptr;
Termcell *cells;
int top;
int left;
int bottom;
int right;
int len;
Eina_Bool reverse_bold = EINA_FALSE;
Eina_Bool reverse_underline = EINA_FALSE;
Eina_Bool reverse_blink = EINA_FALSE;
Eina_Bool reverse_inverse = EINA_FALSE;
top = _csi_arg_get(ty, &b);
left = _csi_arg_get(ty, &b);
bottom = _csi_arg_get(ty, &b);
right = _csi_arg_get(ty, &b);
DBG("DECRARA (%d;%d;%d;%d) Reverse Attributes in Rectangular Area",
top, left, bottom, right);
if ((top == -ESC_ARG_ERROR) ||
(left == -ESC_ARG_ERROR) ||
(bottom == -ESC_ARG_ERROR) ||
(right == -ESC_ARG_ERROR))
return;
while (b && b < end)
{
int arg = _csi_arg_get(ty, &b);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0:
reverse_bold = reverse_underline = reverse_blink = reverse_inverse = EINA_TRUE;
break;
case 1:
reverse_bold = EINA_TRUE;
break;
case 4:
reverse_underline = EINA_TRUE;
break;
case 5:
reverse_blink = EINA_TRUE;
break;
case 7:
reverse_inverse = EINA_TRUE;
break;
default:
WRN("Invalid change attribute [%i]", arg);
ty->decoding_error = EINA_TRUE;
return;
}
}
if (ty->termstate.sace_rectangular)
{
if (_clean_up_rect_coordinates(ty, &top, &left, &bottom, &right) < 0)
return;
len = right - left;
for (; top <= bottom; top++)
{
cells = &(TERMPTY_SCREEN(ty, left, top));
_decrara(cells, len, reverse_bold, reverse_underline,
reverse_blink, reverse_inverse);
}
}
else
{
int left_border = 0;
int right_border = ty->w - 1;
if (_clean_up_from_to_coordinates(ty, &top, &left, &bottom, &right,
&left_border, &right_border) < 0)
return;
if (top == bottom)
{
cells = &(TERMPTY_SCREEN(ty, left, top));
len = right - left;
_decrara(cells, len, reverse_bold, reverse_underline,
reverse_blink, reverse_inverse);
}
else
{
/* First line */
cells = &(TERMPTY_SCREEN(ty, left, top));
len = right_border - left;
_decrara(cells, len, reverse_bold, reverse_underline,
reverse_blink, reverse_inverse);
/* Middle */
len = right_border - left_border;
for (top = top + 1; top < bottom; top++)
{
cells = &(TERMPTY_SCREEN(ty, left_border, top));
_decrara(cells, len, reverse_bold, reverse_underline,
reverse_blink, reverse_inverse);
}
/* Last line */
cells = &(TERMPTY_SCREEN(ty, left_border, bottom));
len = right - left_border;
_decrara(cells, len, reverse_bold, reverse_underline,
reverse_blink, reverse_inverse);
}
}
}
static void
_handle_esc_csi_decera(Termpty *ty, Eina_Unicode **b)
{
int top = _csi_arg_get(ty ,b);
int left = _csi_arg_get(ty, b);
int bottom = _csi_arg_get(ty, b);
int right = _csi_arg_get(ty, b);
int len;
DBG("DECERA (%d;%d;%d;%d) Erase Rectangular Area",
top, left, bottom, right);
if ((top == -ESC_ARG_ERROR) ||
(left == -ESC_ARG_ERROR) ||
(bottom == -ESC_ARG_ERROR) ||
(right == -ESC_ARG_ERROR))
return;
if (_clean_up_rect_coordinates(ty, &top, &left, &bottom, &right) < 0)
return;
len = right - left;
for (; top <= bottom; top++)
{
Termcell *cells = &(TERMPTY_SCREEN(ty, left, top));
termpty_cells_set_content(ty, cells, ' ', len);
}
}
static void
_handle_esc_csi_deccra(Termpty *ty, Eina_Unicode **b)
{
int top = _csi_arg_get(ty, b);
int left = _csi_arg_get(ty, b);
int bottom = _csi_arg_get(ty, b);
int right = _csi_arg_get(ty, b);
int p1 = _csi_arg_get(ty, b);
int to_top = _csi_arg_get(ty, b);
int to_left = _csi_arg_get(ty, b);
int p2 = _csi_arg_get(ty, b);
int to_bottom = ty->h - 1;
int to_right = ty->w;
int len;
DBG("DECFRA (%d;%d;%d;%d -> %d;%d) Copy Rectangular Area",
top, left, bottom, right, to_top, to_left);
if ((top == -ESC_ARG_ERROR) ||
(left == -ESC_ARG_ERROR) ||
(bottom == -ESC_ARG_ERROR) ||
(right == -ESC_ARG_ERROR) ||
(p1 == -ESC_ARG_ERROR) ||
(to_top == -ESC_ARG_ERROR) ||
(to_left == -ESC_ARG_ERROR) ||
(p2 == -ESC_ARG_ERROR))
return;
if (_clean_up_rect_coordinates(ty, &top, &left, &bottom, &right) < 0)
return;
TERMPTY_RESTRICT_FIELD(to_top, 1, ty->h);
if (ty->termstate.restrict_cursor)
{
to_top += ty->termstate.top_margin;
if (ty->termstate.bottom_margin)
{
if (to_top >= ty->termstate.bottom_margin)
to_top = ty->termstate.bottom_margin;
to_bottom = ty->termstate.bottom_margin - 1;
}
}
to_top--;
if (to_bottom - to_top > bottom - top)
to_bottom = to_top + bottom - top;
TERMPTY_RESTRICT_FIELD(to_left, 1, ty->w);
if (ty->termstate.restrict_cursor)
{
to_left += ty->termstate.left_margin;
if (ty->termstate.right_margin)
{
if (to_left >= ty->termstate.right_margin)
to_left = ty->termstate.right_margin;
to_right = ty->termstate.right_margin;
}
}
to_left--;
len = MIN(right - left, to_right - to_left);
if (to_top < top)
{
/* Up -> Bottom */
for (; top <= bottom && to_top <= to_bottom; top++, to_top++)
{
Termcell *cells_src = &(TERMPTY_SCREEN(ty, left, top));
Termcell *cells_dst = &(TERMPTY_SCREEN(ty, to_left, to_top));
int x;
if (to_left <= left)
{
/* -> */
for (x = 0; x < len; x++)
{
if (&(cells_src[x]) != &(cells_dst[x]))
{
TERMPTY_CELL_COPY(ty, &(cells_src[x]), &(cells_dst[x]), 1);
}
}
}
else
{
/* <- */
for (x = len - 1; x >= 0; x--)
{
if (&(cells_src[x]) != &(cells_dst[x]))
{
TERMPTY_CELL_COPY(ty, &(cells_src[x]), &(cells_dst[x]), 1);
}
}
}
}
}
else
{
/* Bottom -> Up */
for (; bottom >= top && to_bottom >= to_top; bottom--, to_bottom--)
{
Termcell *cells_src = &(TERMPTY_SCREEN(ty, left, bottom));
Termcell *cells_dst = &(TERMPTY_SCREEN(ty, to_left, to_bottom));
int x;
if (to_left <= left)
{
/* -> */
for (x = 0; x < len; x++)
{
if (&(cells_src[x]) != &(cells_dst[x]))
{
TERMPTY_CELL_COPY(ty, &(cells_src[x]), &(cells_dst[x]), 1);
}
}
}
else
{
/* <- */
for (x = len - 1; x >= 0; x--)
{
if (&(cells_src[x]) != &(cells_dst[x]))
{
TERMPTY_CELL_COPY(ty, &(cells_src[x]), &(cells_dst[x]), 1);
}
}
}
}
}
}
static void
_handle_esc_csi_cursor_pos_set(Termpty *ty, Eina_Unicode **b,
const Eina_Unicode *cc)
{
int cx = 0, cy = 0;
ty->termstate.wrapnext = 0;
cy = _csi_arg_get(ty, b);
cx = _csi_arg_get(ty, b);
if ((cx == -ESC_ARG_ERROR) || (cy == -ESC_ARG_ERROR))
return;
DBG("cursor pos set (%s) (%d;%d)", (*cc == 'H') ? "CUP" : "HVP",
cx, cy);
cx--;
if (cx < 0)
cx = 0;
if (ty->termstate.restrict_cursor)
{
cx += ty->termstate.left_margin;
if (ty->termstate.right_margin > 0 && cx >= ty->termstate.right_margin)
cx = ty->termstate.right_margin - 1;
}
if (cx >= ty->w)
cx = ty->w -1;
ty->cursor_state.cx = cx;
cy--;
if (cy < 0)
cy = 0;
if (ty->termstate.restrict_cursor)
{
cy += ty->termstate.top_margin;
if (ty->termstate.bottom_margin > 0 && cy >= ty->termstate.bottom_margin)
cy = ty->termstate.bottom_margin - 1;
}
if (cy >= ty->h)
cy = ty->h - 1;
ty->cursor_state.cy = cy;
}
static void
_handle_esc_csi_decscusr(Termpty *ty, Eina_Unicode **b)
{
int arg = _csi_arg_get(ty, b);
Cursor_Shape shape = CURSOR_SHAPE_BLOCK;
DBG("DECSCUSR (%d) Set Cursor Shape", arg);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0:
EINA_FALLTHROUGH;
case 1:
EINA_FALLTHROUGH;
case 2:
shape = CURSOR_SHAPE_BLOCK;
break;
case 3:
EINA_FALLTHROUGH;
case 4:
shape = CURSOR_SHAPE_UNDERLINE;
break;
case 5:
EINA_FALLTHROUGH;
case 6:
shape = CURSOR_SHAPE_BAR;
break;
default:
WRN("Invalid DECSCUSR %d", arg);
ty->decoding_error = EINA_TRUE;
return;
}
termio_set_cursor_shape(ty->obj, shape);
}
static void
_handle_esc_csi_term_version(Termpty *ty, Eina_Unicode **b)
{
int arg = _csi_arg_get(ty, b);
DBG("CSI Term version (%d)", arg);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0:
break;
default:
WRN("Invalid Term Version %d", arg);
ty->decoding_error = EINA_TRUE;
return;
}
TERMPTY_WRITE_STR("\033P>|" PACKAGE_NAME " " PACKAGE_VERSION "\033\\");
}
static void
_handle_esc_csi_decsace(Termpty *ty, Eina_Unicode **b)
{
int arg = _csi_arg_get(ty, b);
DBG("DECSACE (%d) Select Attribute Change Extent", arg);
switch (arg)
{
case -ESC_ARG_ERROR:
return;
case -ESC_ARG_NO_VALUE:
EINA_FALLTHROUGH;
case 0:
EINA_FALLTHROUGH;
case 1:
ty->termstate.sace_rectangular = 0;
break;
case 2:
ty->termstate.sace_rectangular = 1;
break;
default:
WRN("Invalid DECSACE %d", arg);
ty->decoding_error = EINA_TRUE;
return;
}
}
static void
_handle_esc_csi_decic(Termpty *ty, Eina_Unicode **b)
{
int arg = _csi_arg_get(ty, b);
int old_insert = ty->termstate.insert;
Eina_Unicode blank[1] = { ' ' };
int top = 0;
int bottom = ty->h;
int old_cx = ty->cursor_state.cx;
int old_cy = ty->cursor_state.cy;
DBG("DECIC Insert %d Column", arg);
if (arg == -ESC_ARG_ERROR)
return;
if (arg < 1)
arg = 1;
if (ty->termstate.top_margin > 0)
{
if (ty->cursor_state.cy < ty->termstate.top_margin)
return;
top = ty->termstate.top_margin;
}
if (ty->termstate.bottom_margin != 0)
{
if (ty->cursor_state.cy >= ty->termstate.bottom_margin)
return;
bottom = ty->termstate.bottom_margin;
}
if (((ty->termstate.left_margin > 0)
&& (ty->cursor_state.cx < ty->termstate.left_margin))
|| ((ty->termstate.right_margin > 0)
&& (ty->cursor_state.cx >= ty->termstate.right_margin)))
return;
ty->termstate.insert = 1;