From e08238d5a07f48bb212a8bcd1eaa136445f80d38 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sun, 16 Sep 2018 00:46:43 +0900 Subject: [PATCH] pager history buffer: add config option, keep buffer on resize --- kitty/config_data.py | 14 ++++++++++++++ kitty/data-types.h | 4 ++-- kitty/history.c | 36 +++++++++++++++++++++++------------- kitty/screen.c | 5 +++-- kitty/state.c | 1 + kitty/state.h | 1 + 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/kitty/config_data.py b/kitty/config_data.py index e37f79a1f..bea951c3d 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -323,6 +323,13 @@ def scrollback_lines(x): return x +def size_mb(x): + x = int(x) + if x < 0 or x > 2 ** 12 - 1: + raise ValueError('Size {} is in MB (positive & <4GB)'.format(x)) + return x + + o('scrollback_lines', 2000, option_type=scrollback_lines, long_text=_(''' Number of lines of history to keep in memory for scrolling back. Memory is allocated on demand. Negative numbers are (effectively) infinite scrollback. Note that using @@ -336,6 +343,13 @@ use can handle ANSI escape sequences for colors and text formatting. INPUT_LINE_NUMBER in the command line above will be replaced by an integer representing which line should be at the top of the screen.''')) +o('scrollback_pager_history_size', 10, option_type=size_mb, long_text=_(''' +Separate scrollback history size, used only for pager, in MB. +This separate buffer is not available for interactive scrolling but will be +prepended to the pager program when viewing scrollback. +The current implementation stores one character in 4 bytes, so approximatively +2500 lines per megabyte at 100 chars per line''')) + o('wheel_scroll_multiplier', 5.0, long_text=_(''' Modify the amount scrolled by the mouse wheel. Note this is only used for low precision scrolling devices, not for high precision scrolling on platforms such diff --git a/kitty/data-types.h b/kitty/data-types.h index 8f31820ce..0e1858108 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -185,7 +185,7 @@ typedef struct { index_type xnum, ynum, num_segments; HistoryBufSegment *segments; - PagerHistoryBuf pagerhist; + PagerHistoryBuf *pagerhist; Line *line; index_type start_of_data, count; } HistoryBuf; @@ -262,7 +262,7 @@ const char* base64_decode(const uint32_t *src, size_t src_sz, uint8_t *dest, siz Line* alloc_line(); Cursor* alloc_cursor(); LineBuf* alloc_linebuf(unsigned int, unsigned int); -HistoryBuf* alloc_historybuf(unsigned int, unsigned int); +HistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int); ColorProfile* alloc_color_profile(); PyObject* create_256_color_table(); PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *); diff --git a/kitty/history.c b/kitty/history.c index fc368947b..05c3b1756 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -54,12 +54,24 @@ attrptr(HistoryBuf *self, index_type y) { seg_ptr(line_attrs, 1); } +static inline PagerHistoryBuf* +alloc_pagerhist(unsigned int pagerhist_sz) { + PagerHistoryBuf *ph; + if (!pagerhist_sz) return NULL; + pagerhist_sz *= 1024*1024; + ph = PyMem_Calloc(1, sizeof(PagerHistoryBuf)); + ph->bufsize = pagerhist_sz / sizeof(Py_UCS4); + ph->buffer = PyMem_RawMalloc(pagerhist_sz); + if (!ph->buffer) { PyMem_Free(ph); return NULL; } + return ph; +} + static PyObject * new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { HistoryBuf *self; - unsigned int xnum = 1, ynum = 1; + unsigned int xnum = 1, ynum = 1, pagerhist_sz = 0; - if (!PyArg_ParseTuple(args, "II", &ynum, &xnum)) return NULL; + if (!PyArg_ParseTuple(args, "II|I", &ynum, &xnum, &pagerhist_sz)) return NULL; if (xnum == 0 || ynum == 0) { PyErr_SetString(PyExc_ValueError, "Cannot create an empty history buffer"); @@ -74,10 +86,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { add_segment(self); self->line = alloc_line(); self->line->xnum = xnum; - self->pagerhist.start = self->pagerhist.end = self->pagerhist.bufend = 0; - self->pagerhist.buffer = PyMem_RawMalloc(1024*1024*10); - self->pagerhist.bufsize = 1024*1024*10 / sizeof(Py_UCS4); - // abort if allocation failed? + self->pagerhist = alloc_pagerhist(pagerhist_sz); } return (PyObject*)self; @@ -92,7 +101,8 @@ dealloc(HistoryBuf* self) { PyMem_Free(self->segments[i].line_attrs); } PyMem_Free(self->segments); - PyMem_Free(self->pagerhist.buffer); + if (self->pagerhist) PyMem_Free(self->pagerhist->buffer); + PyMem_Free(self->pagerhist); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -139,8 +149,8 @@ historybuf_clear(HistoryBuf *self) { static inline void pagerhist_push(HistoryBuf *self) { - PagerHistoryBuf *ph = &self->pagerhist; - if (!ph->buffer) return; + PagerHistoryBuf *ph = self->pagerhist; + if (!ph) return; Line l = {.xnum=self->xnum}; init_line(self, self->start_of_data, &l); if (ph->start != ph->end && !l.continued) { @@ -241,10 +251,10 @@ get_line(HistoryBuf *self, index_type y, Line *l) { init_line(self, index_of(sel static PyObject * pagerhist_as_text(HistoryBuf *self, PyObject *callback) { - PagerHistoryBuf *ph = &self->pagerhist; + PagerHistoryBuf *ph = self->pagerhist; PyObject *ret = NULL, *t = NULL; Py_UCS4 *buf = NULL; - if (!ph->buffer) Py_RETURN_NONE; + if (!ph) Py_RETURN_NONE; index_type num = (ph->bufend ? ph->bufend : ph->end) - ph->start; buf = ph->buffer + ph->start; @@ -340,8 +350,8 @@ PyTypeObject HistoryBuf_Type = { INIT_TYPE(HistoryBuf) -HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns) { - return (HistoryBuf*)new(&HistoryBuf_Type, Py_BuildValue("II", lines, columns), NULL); +HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned int pagerhist_sz) { + return (HistoryBuf*)new(&HistoryBuf_Type, Py_BuildValue("III", lines, columns, pagerhist_sz), NULL); } // }}} diff --git a/kitty/screen.c b/kitty/screen.c index 82e12f9f9..ec8fdf961 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -107,7 +107,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->color_profile = alloc_color_profile(); self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns); self->linebuf = self->main_linebuf; - self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns); + self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size)); self->main_grman = grman_alloc(); self->alt_grman = grman_alloc(); self->grman = self->main_grman; @@ -165,8 +165,9 @@ screen_dirty_sprite_positions(Screen *self) { static inline HistoryBuf* realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) { - HistoryBuf *ans = alloc_historybuf(lines, columns); + HistoryBuf *ans = alloc_historybuf(lines, columns, 0); if (ans == NULL) { PyErr_NoMemory(); return NULL; } + ans->pagerhist = old->pagerhist; old->pagerhist = NULL; historybuf_rewrap(old, ans); return ans; } diff --git a/kitty/state.c b/kitty/state.c index 089e70053..0b08820b4 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -367,6 +367,7 @@ PYWRAP1(set_options) { S(dynamic_background_opacity, PyObject_IsTrue); S(inactive_text_alpha, PyFloat_AsDouble); S(window_padding_width, PyFloat_AsDouble); + S(scrollback_pager_history_size, PyLong_AsLong); S(cursor_shape, PyLong_AsLong); S(url_style, PyLong_AsUnsignedLong); S(tab_bar_edge, PyLong_AsLong); diff --git a/kitty/state.h b/kitty/state.h index 94a6f7038..6bbab865a 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -19,6 +19,7 @@ typedef struct { unsigned int open_url_modifiers; unsigned int rectangle_select_modifiers; unsigned int url_style; + unsigned int scrollback_pager_history_size; char_type select_by_word_characters[256]; size_t select_by_word_characters_count; color_type url_color, background, active_border_color, inactive_border_color, bell_border_color; double repaint_delay, input_delay;