Fix cursor moving one line up when resizing
This commit is contained in:
parent
564f19d248
commit
3ca45ab241
@ -143,6 +143,22 @@ line(HistoryBuf *self, PyObject *val) {
|
||||
return (PyObject*)self->line;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
__str__(HistoryBuf *self) {
|
||||
PyObject *lines = PyTuple_New(self->ynum);
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->count; i++) {
|
||||
init_line(self, index_of(self, i), self->line);
|
||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||
PyTuple_SET_ITEM(lines, i, t);
|
||||
}
|
||||
PyObject *sep = PyUnicode_FromString("\n");
|
||||
PyObject *ans = PyUnicode_Join(sep, lines);
|
||||
Py_CLEAR(lines); Py_CLEAR(sep);
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
push(HistoryBuf *self, PyObject *args) {
|
||||
#define push_doc "Push a line into this buffer, removing the oldest line, if necessary"
|
||||
@ -203,6 +219,7 @@ PyTypeObject HistoryBuf_Type = {
|
||||
.tp_doc = "History buffers",
|
||||
.tp_methods = methods,
|
||||
.tp_members = members,
|
||||
.tp_str = (reprfunc)__str__,
|
||||
.tp_new = new
|
||||
};
|
||||
|
||||
|
||||
@ -367,6 +367,22 @@ linebuf_refresh_sprite_positions(LineBuf *self) {
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
__str__(LineBuf *self) {
|
||||
PyObject *lines = PyTuple_New(self->ynum);
|
||||
if (lines == NULL) return PyErr_NoMemory();
|
||||
for (index_type i = 0; i < self->ynum; i++) {
|
||||
init_line(self, self->line, self->line_map[i]);
|
||||
PyObject *t = PyObject_Str((PyObject*)self->line);
|
||||
if (t == NULL) { Py_CLEAR(lines); return NULL; }
|
||||
PyTuple_SET_ITEM(lines, i, t);
|
||||
}
|
||||
PyObject *sep = PyUnicode_FromString("\n");
|
||||
PyObject *ans = PyUnicode_Join(sep, lines);
|
||||
Py_CLEAR(lines); Py_CLEAR(sep);
|
||||
return ans;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
static PyObject*
|
||||
copy_old(LineBuf *self, PyObject *y);
|
||||
@ -410,6 +426,7 @@ PyTypeObject LineBuf_Type = {
|
||||
.tp_doc = "Line buffers",
|
||||
.tp_methods = methods,
|
||||
.tp_members = members,
|
||||
.tp_str = (reprfunc)__str__,
|
||||
.tp_new = new
|
||||
};
|
||||
|
||||
@ -437,7 +454,7 @@ copy_old(LineBuf *self, PyObject *y) {
|
||||
#include "rewrap.h"
|
||||
|
||||
void
|
||||
linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *historybuf) {
|
||||
linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_before, index_type *num_content_lines_after, HistoryBuf *historybuf) {
|
||||
index_type first, i;
|
||||
bool is_empty = true;
|
||||
|
||||
@ -446,34 +463,41 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *his
|
||||
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
|
||||
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
|
||||
memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell));
|
||||
*num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first line that contains some content
|
||||
for (first = self->ynum - 1; true; first--) {
|
||||
Cell *cells = lineptr(self, first);
|
||||
first = self->ynum;
|
||||
do {
|
||||
first--;
|
||||
Cell *cells = lineptr(self, self->line_map[first]);
|
||||
for(i = 0; i < self->xnum; i++) {
|
||||
if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; }
|
||||
}
|
||||
if (!is_empty || !first) break;
|
||||
} while(is_empty && first > 0);
|
||||
|
||||
if (is_empty) { // All lines are empty
|
||||
*num_content_lines_after = 0;
|
||||
*num_content_lines_before = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (first == 0) { *cursor_y_out = 0; return; } // All lines are empty
|
||||
|
||||
rewrap_inner(self, other, first + 1, historybuf);
|
||||
*cursor_y_out = other->line->ynum;
|
||||
*num_content_lines_after = other->line->ynum + 1;
|
||||
*num_content_lines_before = first + 1;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
rewrap(LineBuf *self, PyObject *args) {
|
||||
LineBuf* other;
|
||||
HistoryBuf *historybuf;
|
||||
int cursor_y = -1;
|
||||
unsigned int nclb, ncla;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!O!", &LineBuf_Type, &other, &HistoryBuf_Type, &historybuf)) return NULL;
|
||||
linebuf_rewrap(self, other, &cursor_y, historybuf);
|
||||
linebuf_rewrap(self, other, &nclb, &ncla, historybuf);
|
||||
|
||||
return Py_BuildValue("i", cursor_y);
|
||||
return Py_BuildValue("II", nclb, ncla);
|
||||
}
|
||||
|
||||
LineBuf *alloc_linebuf(unsigned int lines, unsigned int columns) {
|
||||
|
||||
@ -78,7 +78,7 @@ void linebuf_clear_line(LineBuf *self, index_type y);
|
||||
void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);
|
||||
void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);
|
||||
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
|
||||
void linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *);
|
||||
void linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *, index_type *, HistoryBuf *);
|
||||
unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y);
|
||||
void linebuf_refresh_sprite_positions(LineBuf *self);
|
||||
bool historybuf_resize(HistoryBuf *self, index_type lines);
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
if (dest_y >= dest->ynum - 1) { \
|
||||
linebuf_index(dest, 0, dest->ynum - 1); \
|
||||
if (historybuf != NULL) { \
|
||||
linebuf_init_line(dest, dest->ynum - 1); \
|
||||
init_dest_line(dest->ynum - 1); \
|
||||
historybuf_add_line(historybuf, dest->line); \
|
||||
}\
|
||||
linebuf_clear_line(dest, dest->ynum - 1); \
|
||||
|
||||
@ -123,44 +123,40 @@ realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) {
|
||||
}
|
||||
|
||||
static inline LineBuf*
|
||||
realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, int *cursor_y, HistoryBuf *hb) {
|
||||
realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, index_type *nclb, index_type *ncla, HistoryBuf *hb) {
|
||||
LineBuf *ans = alloc_linebuf(lines, columns);
|
||||
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
|
||||
linebuf_rewrap(old, ans, cursor_y, hb);
|
||||
linebuf_rewrap(old, ans, nclb, ncla, hb);
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline void
|
||||
adjust_cursor_position_after_resize(Cursor *c, unsigned int lines, unsigned int columns, index_type num_content_lines_before, index_type num_content_lines_after) {
|
||||
if (c->y >= num_content_lines_before - 1) {
|
||||
index_type delta = c->y - (num_content_lines_before - 1);
|
||||
c->y = num_content_lines_after - 1 + delta;
|
||||
}
|
||||
c->y = MIN(lines - 1, c->y); c->x = MIN(columns - 1, c->x);
|
||||
}
|
||||
|
||||
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, is_x_shrink = columns < self->columns;
|
||||
int cursor_y = -1; unsigned int cursor_x = self->cursor->x;
|
||||
bool is_main = self->linebuf == self->main_linebuf;
|
||||
index_type num_content_lines_before, num_content_lines_after;
|
||||
HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns);
|
||||
if (nh == NULL) return false;
|
||||
Py_CLEAR(self->historybuf); self->historybuf = nh;
|
||||
LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &cursor_y, self->historybuf);
|
||||
LineBuf *n = realloc_lb(self->main_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, self->historybuf);
|
||||
if (n == NULL) return false;
|
||||
Py_CLEAR(self->main_linebuf); self->main_linebuf = n;
|
||||
bool index_after_resize = false;
|
||||
if (is_main) {
|
||||
index_type cy = MIN(self->cursor->y, lines - 1);
|
||||
linebuf_init_line(self->main_linebuf, cy);
|
||||
if (is_x_shrink && (self->main_linebuf->continued_map[cy] || line_length(self->main_linebuf->line) > columns)) {
|
||||
// If the client is in line drawing mode, it will redraw the cursor
|
||||
// line, this can cause rendering artifacts, so ensure that the
|
||||
// cursor is on a new line
|
||||
index_after_resize = true;
|
||||
}
|
||||
self->cursor->y = MAX(0, cursor_y);
|
||||
}
|
||||
cursor_y = -1;
|
||||
n = realloc_lb(self->alt_linebuf, lines, columns, &cursor_y, NULL);
|
||||
if (is_main) adjust_cursor_position_after_resize(self->cursor, lines, columns, num_content_lines_before, num_content_lines_after);
|
||||
n = realloc_lb(self->alt_linebuf, lines, columns, &num_content_lines_before, &num_content_lines_after, NULL);
|
||||
if (n == NULL) return false;
|
||||
Py_CLEAR(self->alt_linebuf); self->alt_linebuf = n;
|
||||
if (!is_main) self->cursor->y = MAX(0, cursor_y);
|
||||
if (!is_main) adjust_cursor_position_after_resize(self->cursor, lines, columns, num_content_lines_before, num_content_lines_after);
|
||||
self->linebuf = is_main ? self->main_linebuf : self->alt_linebuf;
|
||||
if (is_x_shrink && cursor_x >= columns) self->cursor->x = columns - 1;
|
||||
|
||||
self->lines = lines; self->columns = columns;
|
||||
self->margin_top = 0; self->margin_bottom = self->lines - 1;
|
||||
@ -175,7 +171,6 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||
self->is_dirty = true;
|
||||
self->selection = EMPTY_SELECTION;
|
||||
self->url_range = EMPTY_SELECTION;
|
||||
if (index_after_resize) screen_index(self);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ class TestDataTypes(BaseTest):
|
||||
def rewrap(self, lb, lb2):
|
||||
hb = HistoryBuf(lb2.ynum, lb2.xnum)
|
||||
cy = lb.rewrap(lb2, hb)
|
||||
return hb, cy
|
||||
return hb, cy[1]
|
||||
|
||||
def test_rewrap_simple(self):
|
||||
' Same width buffers '
|
||||
@ -268,7 +268,7 @@ class TestDataTypes(BaseTest):
|
||||
self.ae(lb2.line(i), lb.line(i))
|
||||
lb2 = LineBuf(8, 5)
|
||||
cy = self.rewrap(lb, lb2)[1]
|
||||
self.ae(cy, 4)
|
||||
self.ae(cy, 5)
|
||||
for i in range(lb.ynum):
|
||||
self.ae(lb2.line(i), lb.line(i))
|
||||
empty = LineBuf(1, lb2.xnum)
|
||||
@ -276,7 +276,7 @@ class TestDataTypes(BaseTest):
|
||||
self.ae(str(lb2.line(i)), str(empty.line(0)))
|
||||
lb2 = LineBuf(3, 5)
|
||||
cy = self.rewrap(lb, lb2)[1]
|
||||
self.ae(cy, 2)
|
||||
self.ae(cy, 3)
|
||||
for i in range(lb2.ynum):
|
||||
self.ae(lb2.line(i), lb.line(i + 2))
|
||||
|
||||
|
||||
@ -223,14 +223,19 @@ class TestScreen(BaseTest):
|
||||
s.resize(5, 1)
|
||||
self.ae(str(s.line(0)), '4')
|
||||
hb = s.historybuf
|
||||
for i in range(hb.ynum):
|
||||
self.ae(str(hb.line(i)), '4' if i == 0 else '3')
|
||||
s = self.create_screen(scrollback=6)
|
||||
s.draw(''.join([str(i) * s.columns for i in range(s.lines*2)]))
|
||||
self.ae(str(s.line(4)), '9'*5)
|
||||
self.ae(str(hb), '3\n3\n3\n3\n3\n2')
|
||||
s = self.create_screen(scrollback=20)
|
||||
s.draw(''.join(str(i) * s.columns for i in range(s.lines*2)))
|
||||
self.ae(str(s.linebuf), '55555\n66666\n77777\n88888\n99999')
|
||||
s.resize(5, 2)
|
||||
self.ae(str(s.line(3)), '9')
|
||||
self.ae(str(s.line(4)), '')
|
||||
self.ae(str(s.linebuf), '88\n88\n99\n99\n9')
|
||||
|
||||
def test_cursor_after_resize(self):
|
||||
s = self.create_screen()
|
||||
s.draw('123'), s.linefeed(), s.carriage_return(), s.draw('123'), s.linefeed(), s.carriage_return()
|
||||
y_before = s.cursor.y
|
||||
s.resize(s.lines, s.columns-1)
|
||||
self.ae(y_before, s.cursor.y)
|
||||
|
||||
def test_tab_stops(self):
|
||||
# Taken from vttest/main.c
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user