diff --git a/kitty/data-types.c b/kitty/data-types.c index 40892e9bb..8d7d2e8c3 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -69,6 +69,8 @@ PyInit_fast_data_types(void) { PyModule_AddIntMacro(m, CURSOR_BEAM); PyModule_AddIntMacro(m, CURSOR_UNDERLINE); PyModule_AddIntMacro(m, DECAWM); + PyModule_AddIntMacro(m, DECCOLM); + PyModule_AddIntMacro(m, DECOM); PyModule_AddIntMacro(m, IRM); PyModule_AddIntMacro(m, DATA_CELL_SIZE); diff --git a/kitty/parser.c b/kitty/parser.c index bf6ab790e..4490e0fad 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -119,7 +119,7 @@ screen_nel(Screen *screen) { screen_carriage_return(screen); screen_linefeed(scr static inline void handle_normal_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback) { -#define CALL_SCREEN_HANDLER(name) REPORT_COMMAND(name, ch); name(screen); break; +#define CALL_SCREEN_HANDLER(name) REPORT_COMMAND(name); name(screen); break; switch(ch) { case BEL: CALL_SCREEN_HANDLER(screen_bell); diff --git a/kitty/screen.c b/kitty/screen.c index 9f1676b9d..2e257615b 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -54,7 +54,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { return (PyObject*) self; } -void screen_reset(Screen *self) { +void +screen_reset(Screen *self) { if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self); linebuf_clear(self->linebuf, ' '); self->modes = empty_modes; @@ -72,21 +73,24 @@ void screen_reset(Screen *self) { tracker_update_screen(self->change_tracker); } -static inline HistoryBuf* realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) { +static inline HistoryBuf* +realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) { HistoryBuf *ans = alloc_historybuf(lines, columns); if (ans == NULL) { PyErr_NoMemory(); return NULL; } historybuf_rewrap(old, ans); return ans; } -static inline LineBuf* realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y, HistoryBuf *hb) { +static inline LineBuf* +realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y, HistoryBuf *hb) { LineBuf *ans = alloc_linebuf(lines, columns); if (ans == NULL) { PyErr_NoMemory(); return NULL; } linebuf_rewrap(old, ans, cursor_y, hb); return ans; } -static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns) { +static bool +screen_resize(Screen *self, unsigned int lines, unsigned int columns) { lines = MAX(1, lines); columns = MAX(1, columns); bool is_main = self->linebuf == self->main_linebuf; @@ -121,7 +125,8 @@ static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns return true; } -static bool screen_change_scrollback_size(Screen *self, unsigned int size) { +static bool +screen_change_scrollback_size(Screen *self, unsigned int size) { if (size != self->historybuf->ynum) return historybuf_resize(self->historybuf, size); return true; } @@ -564,9 +569,9 @@ screen_restore_cursor(Screen *self) { } void -screen_ensure_bounds(Screen *self, bool use_margins/*=false*/) { +screen_ensure_bounds(Screen *self, bool force_use_margins/*=false*/) { unsigned int top, bottom; - if (use_margins || self->modes.mDECOM) { + if (force_use_margins || self->modes.mDECOM) { top = self->margin_top; bottom = self->margin_bottom; } else { top = 0; bottom = self->lines - 1; @@ -581,7 +586,7 @@ screen_cursor_position(Screen *self, unsigned int line, unsigned int column) { column = (column == 0 ? 1: column) - 1; if (self->modes.mDECOM) { line += self->margin_top; - if (line < self->margin_bottom || line > self->margin_top) return; + line = MAX(self->margin_top, MIN(line, self->margin_bottom)); } unsigned int x = self->cursor->x, y = self->cursor->y; self->cursor->x = column; self->cursor->y = line; @@ -786,7 +791,8 @@ void report_device_status(Screen *self, unsigned int which, bool UNUSED private) } } -void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom) { +void +screen_set_margins(Screen *self, unsigned int top, unsigned int bottom) { if (!top) top = 1; if (!bottom) bottom = self->lines; top = MIN(self->lines, top); @@ -803,7 +809,8 @@ void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom) { } } -void screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) { +void +screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) { uint8_t shape; bool blink; switch(secondary) { case 0: // DECLL @@ -824,7 +831,8 @@ void screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) { } } -void set_title(Screen *self, PyObject *title) { +void +set_title(Screen *self, PyObject *title) { PyObject_CallMethod(self->callbacks, "title_changed", "O", title); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } @@ -944,7 +952,10 @@ WRAP0(set_tab_stop) WRAP1(clear_tab_stop, 0) WRAP0(backspace) WRAP0(tab) +WRAP0(linefeed) +WRAP0(carriage_return) WRAP2(resize, 1, 1) +WRAP2(set_margins, 1, 1) static PyObject* change_scrollback_size(Screen *self, PyObject *args) { @@ -1076,12 +1087,15 @@ static PyMethodDef methods[] = { MND(index, METH_NOARGS) MND(tab, METH_NOARGS) MND(backspace, METH_NOARGS) + MND(linefeed, METH_NOARGS) + MND(carriage_return, METH_NOARGS) MND(set_tab_stop, METH_NOARGS) MND(clear_tab_stop, METH_VARARGS) MND(reverse_index, METH_NOARGS) MND(is_dirty, METH_NOARGS) MND(mark_as_dirty, METH_NOARGS) MND(resize, METH_VARARGS) + MND(set_margins, METH_VARARGS) MND(set_scroll_cell_data, METH_VARARGS) MND(apply_selection, METH_VARARGS) MND(in_bracketed_paste_mode, METH_NOARGS) diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 3dd2f2afc..b3781735f 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -4,7 +4,7 @@ from . import BaseTest -from kitty.fast_data_types import DECAWM, IRM, Cursor +from kitty.fast_data_types import DECAWM, IRM, Cursor, DECCOLM, DECOM class TestScreen(BaseTest): @@ -271,3 +271,34 @@ class TestScreen(BaseTest): s.draw(' ') s.draw('*') self.ae(str(s.line(0)), str(s.line(1))) + + def test_margins(self): + # Taken from vttest/main.c + s = self.create_screen(cols=80, lines=24) + + def nl(): + s.carriage_return(), s.linefeed() + + for deccolm in (False, True): + if deccolm: + s.resize(24, 132) + s.set_mode(DECCOLM) + else: + s.reset_mode(DECCOLM) + region = s.lines - 6 + s.set_margins(3, region + 3) + s.set_mode(DECOM) + for i, ch in enumerate('ABCDEFGHIJKLMNOPQRSTUVWXYZ'): + which = i % 4 + if which == 0: + s.cursor_position(region + 1, 1), s.draw(ch) + s.cursor_position(region + 1, s.columns), s.draw(ch.lower()) + nl() + else: + s.cursor_position(region + 1, 1), nl() + s.cursor_position(region, 1), s.draw(ch) + s.cursor_position(region, s.columns), s.draw(ch.lower()) + for l in range(2, region + 2): + c = chr(ord('I') + l - 2) + self.ae(c + ' ' * (s.columns - 2) + c.lower(), str(s.line(l))) + s.reset_mode(DECOM)