Implement SGR

This commit is contained in:
Kovid Goyal
2016-11-17 08:34:55 +05:30
parent f5fa40de81
commit f56da9392c
5 changed files with 175 additions and 17 deletions

View File

@@ -36,7 +36,7 @@ repr(Cursor *self) {
);
}
static inline void cursor_reset_display_attrs(Cursor *self) {
void cursor_reset_display_attrs(Cursor *self) {
self->bg = 0; self->fg = 0; self->decoration_fg = 0;
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false;
}

View File

@@ -275,6 +275,7 @@ uint16_t* translation_table(char);
uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte);
void cursor_reset(Cursor*);
Cursor* cursor_copy(Cursor*);
void cursor_reset_display_attrs(Cursor*);
bool update_cell_range_data(SpriteMap *, Line *, unsigned int, unsigned int, ColorProfile *, const uint32_t, const uint32_t, unsigned int *);
uint32_t to_color(ColorProfile *, uint32_t, uint32_t);
@@ -314,6 +315,9 @@ void screen_reverse_index(Screen *self);
void screen_index(Screen *self);
void screen_reset(Screen *self);
void screen_set_tab_stop(Screen *self);
void screen_clear_tab_stop(Screen *self, unsigned int how);
void screen_set_mode(Screen *self, unsigned int mode);
void screen_reset_mode(Screen *self, unsigned int mode);
void screen_insert_characters(Screen *self, unsigned int count);
void screen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/);
void screen_cursor_to_column(Screen *self, unsigned int column);
@@ -321,10 +325,13 @@ void screen_cursor_down(Screen *self, unsigned int count/*=1*/);
void screen_cursor_forward(Screen *self, unsigned int count/*=1*/);
void screen_cursor_down1(Screen *self, unsigned int count/*=1*/);
void screen_cursor_up1(Screen *self, unsigned int count/*=1*/);
void screen_cursor_to_line(Screen *screen, unsigned int line);
void screen_insert_lines(Screen *self, unsigned int count/*=1*/);
void screen_delete_lines(Screen *self, unsigned int count/*=1*/);
void screen_delete_characters(Screen *self, unsigned int count);
void screen_erase_characters(Screen *self, unsigned int count);
void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary);
void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen, uint8_t ch);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)

View File

@@ -216,7 +216,7 @@ HANDLER(esc) {
// Parse CSI {{{
#define MAX_PARAMS 8
#define MAX_PARAMS 100
static inline unsigned int fill_params(Screen *screen, unsigned int *params, unsigned int expect) {
unsigned int start_pos = 1, i = 1, pi = 0;
@@ -256,20 +256,21 @@ HANDLER(csi) {
case NUL: \
case DEL: \
break; // no-op
#define END_DISPATCH SET_STATE(NORMAL_STATE); break;
#define CALL_CSI_HANDLER1(name, defval) \
p1 = fill_params(screen, params, 1) > 0 ? params[0] : defval; \
REPORT_COMMAND(name, p1); \
name(screen, p1); \
SET_STATE(NORMAL_STATE); \
break;
END_DISPATCH;
#define CALL_CSI_HANDLER1P(name, defval) \
#define CALL_CSI_HANDLER1P(name, defval, qch) \
p1 = fill_params(screen, params, 1) > 0 ? params[0] : defval; \
private = screen->parser_buf[0] == '?'; \
private = screen->parser_buf[0] == qch; \
REPORT_COMMAND(name, p1, private); \
name(screen, p1, private); \
SET_STATE(NORMAL_STATE); \
break;
END_DISPATCH;
#define CALL_CSI_HANDLER2(name, defval1, defval2) \
count = fill_params(screen, params, 2); \
@@ -277,8 +278,23 @@ HANDLER(csi) {
p2 = count > 1 ? params[1] : defval2; \
REPORT_COMMAND(name, p1, p2); \
name(screen, p1, p2); \
SET_STATE(NORMAL_STATE); \
break;
END_DISPATCH;
#define SET_MODE(func) \
count = fill_params(screen, params, MAX_PARAMS); \
p1 = screen->parser_buf[0] == '?' ? 5 : 0; \
for (i = 0; i < count; i++) { \
REPORT_COMMAND(func, params[i] << p1); \
func(screen, params[i] << p1); \
} \
END_DISPATCH;
#define CSI_HANDLER_MULTIPLE(name) \
count = fill_params(screen, params, MAX_PARAMS); \
REPORT_COMMAND(name, count); \
name(screen, params, count); \
END_DISPATCH;
#define DISPATCH_CSI \
case ICH: \
@@ -286,6 +302,7 @@ HANDLER(csi) {
case CUU: \
CALL_CSI_HANDLER1(screen_cursor_up2, 1); \
case CUD: \
case VPR: \
CALL_CSI_HANDLER1(screen_cursor_down, 1); \
case CUF: \
case HPR: \
@@ -298,12 +315,15 @@ HANDLER(csi) {
CALL_CSI_HANDLER1(screen_cursor_up1, 1); \
case CHA: \
CALL_CSI_HANDLER1(screen_cursor_to_column, 1); \
case VPA: \
CALL_CSI_HANDLER1(screen_cursor_to_line, 1); \
case CUP: \
case HVP: \
CALL_CSI_HANDLER2(screen_cursor_position, 1, 1); \
case ED: \
CALL_CSI_HANDLER1P(screen_erase_in_display, 0); \
CALL_CSI_HANDLER1P(screen_erase_in_display, 0, '?'); \
case EL: \
CALL_CSI_HANDLER1P(screen_erase_in_line, 0); \
CALL_CSI_HANDLER1P(screen_erase_in_line, 0, '?'); \
case IL: \
CALL_CSI_HANDLER1(screen_insert_lines, 1); \
case DL: \
@@ -312,9 +332,19 @@ HANDLER(csi) {
CALL_CSI_HANDLER1(screen_delete_characters, 1); \
case ECH: \
CALL_CSI_HANDLER1(screen_erase_characters, 1); \
case DA: \
CALL_CSI_HANDLER1P(report_device_attributes, 0, '>'); \
case TBC: \
CALL_CSI_HANDLER1(screen_clear_tab_stop, 0); \
case SM: \
SET_MODE(screen_set_mode); \
case RM: \
SET_MODE(screen_reset_mode); \
case SGR: \
CSI_HANDLER_MULTIPLE(select_graphic_rendition); \
uint8_t ch = buf[(*pos)++];
unsigned int params[MAX_PARAMS], p1, p2, count;
unsigned int params[MAX_PARAMS], p1, p2, count, i;
bool private;
switch(screen->parser_buf_pos) {
case 0: // CSI starting

View File

@@ -221,6 +221,74 @@ void screen_alignment_display(Screen *self) {
line_clear_text(self->linebuf->line, 0, self->linebuf->xnum, 'E');
}
}
void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count) {
#define SET_COLOR(which) \
if (i < count) { \
attr = params[i++];\
switch(attr) { \
case 5: \
if (i < count) \
self->cursor->which = (params[i++] & 0xFF) << 8 | 2; \
break; \
case 2: \
if (i < count - 2) { \
r = params[i++] & 0xFF; \
g = params[i++] & 0xFF; \
b = params[i++] & 0xFF; \
self->cursor->which = r << 24 | g << 16 | b << 8 | 3; \
}\
break; \
} \
} \
break;
unsigned int i = 0, attr;
uint8_t r, g, b;
if (!count) { params[0] = 0; count = 1; }
while (i < count) {
attr = params[i++];
switch(attr) {
case 0:
cursor_reset_display_attrs(self->cursor); break;
case 1:
self->cursor->bold = true; break;
case 3:
self->cursor->italic = true; break;
case 4:
self->cursor->decoration = 1; break;
case 7:
self->cursor->reverse = true; break;
case 9:
self->cursor->strikethrough = true; break;
case 22:
self->cursor->bold = false; break;
case 23:
self->cursor->italic = false; break;
case 24:
self->cursor->decoration = 0; break;
case 27:
self->cursor->reverse = false; break;
case 29:
self->cursor->strikethrough = false; break;
#pragma GCC diagnostic ignored "-Wpedantic"
case 30 ... 37:
case 39:
case 90 ... 97:
self->cursor->fg = (attr << 8) | 1; break;
case 40 ... 47:
case 49:
case 100 ... 107:
#pragma GCC diagnostic pop
self->cursor->bg = (attr << 8) | 1; break;
case 38:
SET_COLOR(fg);
case 48:
SET_COLOR(bg);
}
}
}
// }}}
// Modes {{{
@@ -242,7 +310,7 @@ void screen_toggle_screen_buffer(Screen *self) {
void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI
void screen_alternate_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI
static inline void set_mode_from_const(Screen *self, int mode, bool val) {
static inline void set_mode_from_const(Screen *self, unsigned int mode, bool val) {
switch(mode) {
case LNM:
self->modes.mLNM = val; break;
@@ -265,7 +333,7 @@ static inline void set_mode_from_const(Screen *self, int mode, bool val) {
}
}
void screen_set_mode(Screen *self, int mode) {
void screen_set_mode(Screen *self, unsigned int mode) {
if (mode == DECCOLM) {
// When DECCOLM mode is set, the screen is erased and the cursor
// moves to the home position.
@@ -311,7 +379,7 @@ enable_focus_tracking(Screen *self) {
return ans;
}
void screen_reset_mode(Screen *self, int mode) {
void screen_reset_mode(Screen *self, unsigned int mode) {
if (mode == DECCOLM) {
// When DECCOLM mode is set, the screen is erased and the cursor
// moves to the home position.
@@ -361,6 +429,17 @@ void screen_tab(Screen *self, uint8_t UNUSED ch) {
}
}
void screen_clear_tab_stop(Screen *self, unsigned int how) {
switch(how) {
case 0:
if ((unsigned int)self->cursor->x < self->columns) self->tabstops[self->cursor->x] = false;
break;
case 3:
break;
for (unsigned int i = 0; i < self->columns; i++) self->tabstops[i] = false;
}
}
void screen_set_tab_stop(Screen *self) {
if ((unsigned int)self->cursor->x < self->columns && self->cursor->x >= 0)
self->tabstops[self->cursor->x] = true;
@@ -506,6 +585,16 @@ void screen_cursor_position(Screen *self, unsigned int line, unsigned int column
if (x != self->cursor->x || y != self->cursor->y) tracker_cursor_changed(self->change_tracker);
}
void screen_cursor_to_line(Screen *self, unsigned int line) {
unsigned int y = MAX(line, 1) - 1;
y += self->margin_top;
if (y != (unsigned int)self->cursor->y) {
self->cursor->y = y;
screen_ensure_bounds(self, false); // TODO: should we also restrict the cursor to the scrolling region?
tracker_cursor_changed(self->change_tracker);
}
}
// }}}
// Editing {{{
@@ -645,6 +734,23 @@ void screen_erase_characters(Screen *self, unsigned int count) {
line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);
tracker_update_cell_range(self->change_tracker, self->cursor->y, x, MIN(x + num, self->columns) - 1);
}
// }}}
// Device control {{{
static inline void write_to_child(Screen *self, const char *data, unsigned int sz) {
if (sz) PyObject_CallMethod(self->callbacks, "write_to_child", "y#", data, sz);
else PyObject_CallMethod(self->callbacks, "write_to_child", "y", data);
if (PyErr_Occurred()) PyErr_Print();
PyErr_Clear();
}
void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary) {
// Do the same as libvte, which gives the below response regardless of mode and secondary
write_to_child(self, "\x1b[?62c", 0); // Corresponds to VT-220
}
// }}}
// Python interface {{{
@@ -829,4 +935,3 @@ PyTypeObject Screen_Type = {
INIT_TYPE(Screen)
// }}}

View File

@@ -76,4 +76,20 @@ class TestScreen(BaseTest):
pb('\033[J', ('screen_erase_in_display', 0, 0))
pb('\033[?J', ('screen_erase_in_display', 0, 1))
pb('\033[?2J', ('screen_erase_in_display', 2, 1))
pb('\033[h')
pb('\033[20;4h', ('screen_set_mode', 20), ('screen_set_mode', 4))
pb('\033[?20;5h', ('screen_set_mode', 20 << 5), ('screen_set_mode', 5 << 5))
pb('\033[20;4;145l', ('screen_reset_mode', 20), ('screen_reset_mode', 4), ('screen_reset_mode', 145))
s.reset()
pb('\033[1;3;4;7;9;34;44m', ('select_graphic_rendition', 7))
for attr in 'bold italic reverse strikethrough'.split():
self.assertTrue(getattr(s.cursor, attr))
self.ae(s.cursor.decoration, 1)
self.ae(s.cursor.fg, 34 << 8 | 1)
self.ae(s.cursor.bg, 44 << 8 | 1)
pb('\033[38;5;1;48;5;7m', ('select_graphic_rendition', 6))
self.ae(s.cursor.fg, 1 << 8 | 2)
self.ae(s.cursor.bg, 7 << 8 | 2)
pb('\033[38;2;1;2;3;48;2;7;8;9m', ('select_graphic_rendition', 10))
self.ae(s.cursor.fg, 1 << 24 | 2 << 16 | 3 << 8 | 3)
self.ae(s.cursor.bg, 7 << 24 | 8 << 16 | 9 << 8 | 3)