mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
Implement SGR
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
113
kitty/screen.c
113
kitty/screen.c
@@ -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)
|
||||
// }}}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user