From b426210e388fc4e35acf86b7907928a0a1dee3c8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Nov 2016 20:48:51 +0530 Subject: [PATCH] Implement keyboard shortcuts for scrolling --- kitty/boss.py | 11 +++++++++++ kitty/char_grid.py | 15 ++++++++++++--- kitty/data-types.h | 1 + kitty/history.c | 4 ++++ kitty/screen.c | 34 ++++++++++++++++++++++++++++++++-- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 4abbcb2c2..a951e86b7 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -281,4 +281,15 @@ class Boss(Thread): text = subprocess.check_output(['xsel']) self.paste(text) + def scroll_line_up(self): + self.queue_action(self.char_grid.scroll, 'line', True) + + def scroll_line_down(self): + self.queue_action(self.char_grid.scroll, 'line', False) + + def scroll_page_up(self): + self.queue_action(self.char_grid.scroll, 'page', True) + + def scroll_page_down(self): + self.queue_action(self.char_grid.scroll, 'page', False) # }}} diff --git a/kitty/char_grid.py b/kitty/char_grid.py index 82199742f..c9fb6d48e 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -218,15 +218,25 @@ class CharGrid: self.render_queue.put(RenderData(clear_color=self.default_bg)) self.clear_count = 4 + def scroll(self, amt, upwards=True): + amt = 1 if amt == 'line' else self.screen.lines - 1 if amt == 'page' else max(0, min(int(amt), self.screen.lines - 1)) + if not upwards: + amt *= -1 + y = max(0, min(self.scrolled_by + amt, self.screen.historybuf.count)) + if y != self.scrolled_by: + self.scrolled_by = y + self.update_cell_data() + def update_cell_data(self, add_viewport_data=False): - rd = RenderData(sprite_layout=self.sprites.layout) + rd = RenderData(sprite_layout=self.sprites.layout, cell_data_changed=True) if add_viewport_data: rd.viewport = Size(self.width, self.height) rd.screen_geometry = self.screen_geometry with self.sprites_lock: - cursor_changed = self.screen.update_cell_data( + cursor_changed, history_line_added_count = self.screen.update_cell_data( self.sprites.backend, self.color_profile, addressof(self.main_sprite_map), self.default_fg, self.default_bg, add_viewport_data) if self.scrolled_by: + self.scrolled_by = min(self.scrolled_by + history_line_added_count, self.screen.historybuf.count) self.screen.set_scroll_cell_data( self.sprites.backend, self.color_profile, addressof(self.main_sprite_map), self.default_fg, self.default_bg, self.scrolled_by, addressof(self.scroll_sprite_map)) @@ -234,7 +244,6 @@ class CharGrid: data = self.scroll_sprite_map if self.scrolled_by else self.main_sprite_map with self.buffer_lock: memmove(self.render_buf, data, sizeof(type(data))) - rd.cell_data_changed = True if cursor_changed: c = self.screen.cursor rd.cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink) diff --git a/kitty/data-types.h b/kitty/data-types.h index 4465602d4..cd7034fc9 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -323,6 +323,7 @@ unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y); bool historybuf_resize(HistoryBuf *self, index_type lines); void historybuf_add_line(HistoryBuf *self, const Line *line); void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other); +void historybuf_init_line(HistoryBuf *self, index_type num, Line *l); void screen_restore_cursor(Screen *); void screen_save_cursor(Screen *); diff --git a/kitty/history.c b/kitty/history.c index 7b0fcf7b6..e07ec156b 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -70,6 +70,10 @@ static inline void init_line(HistoryBuf *self, index_type num, Line *l) { l->combining_chars = (combining_type*)(l->decoration_fg + self->xnum); } +void historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) { + init_line(self, index_of(self, lnum), l); +} + static inline index_type historybuf_push(HistoryBuf *self) { index_type idx = (self->start_of_data + self->count) % self->ynum; init_line(self, idx, self->line); diff --git a/kitty/screen.c b/kitty/screen.c index a76df4ff8..e6a9a0796 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1029,11 +1029,40 @@ screen_update_cell_data(Screen *self, PyObject *args) { if (!PyArg_ParseTuple(args, "O!O!O!kkp", &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL; data = PyLong_AsVoidPtr(dp); PyObject *cursor_changed = self->change_tracker->cursor_changed ? Py_True : Py_False; + unsigned int history_line_added_count = self->change_tracker->history_line_added_count; + if (!tracker_update_cell_data(self->change_tracker, self->linebuf, spm, color_profile, data, default_fg, default_bg, (bool)force_screen_refresh)) return NULL; - Py_INCREF(cursor_changed); - return cursor_changed; + return Py_BuildValue("OI", cursor_changed, history_line_added_count); } +static PyObject* +set_scroll_cell_data(Screen *self, PyObject *args) { + SpriteMap *spm; + ColorProfile *color_profile; + PyObject *dp, *sp; + unsigned int *data, *src, scrolled_by; + unsigned long default_bg, default_fg; + if (!PyArg_ParseTuple(args, "O!O!O!kkIO", &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &sp, &default_fg, &default_bg, &scrolled_by, &dp)) return NULL; + data = PyLong_AsVoidPtr(dp); + src = PyLong_AsVoidPtr(sp); + unsigned int line_size = 9 * self->columns; + + scrolled_by = MIN(self->historybuf->count, scrolled_by); + + for (index_type y = 0; y < MIN(self->lines, scrolled_by); y++) { + historybuf_init_line(self->historybuf, scrolled_by - y, self->historybuf->line); + self->historybuf->line->ynum = y; + if (!update_cell_range_data(spm, self->historybuf->line, 0, self->columns - 1, color_profile, default_bg, default_fg, data)) return NULL; + } + if (scrolled_by < self->lines) { + // Less than a full screen has been scrolled, copy some lines from the screen buffer to the scroll buffer + index_type num_to_copy = self->lines - scrolled_by; + index_type offset = line_size * scrolled_by; + memcpy(data + offset, src, line_size * num_to_copy * sizeof(unsigned int)); + } + Py_RETURN_NONE; +} + static PyObject* is_dirty(Screen *self) { PyObject *ans = self->change_tracker->dirty ? Py_True : Py_False; Py_INCREF(ans); @@ -1096,6 +1125,7 @@ static PyMethodDef methods[] = { MND(reverse_index, METH_NOARGS) MND(is_dirty, METH_NOARGS) MND(resize, METH_VARARGS) + MND(set_scroll_cell_data, METH_VARARGS) {"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""}, {NULL} /* Sentinel */