diff --git a/docs/changelog.rst b/docs/changelog.rst index 0eebd16a9..cfd2ab5a2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -54,6 +54,8 @@ Detailed list of changes using the panel kitten for all compositors that support the requisite Wayland protocol which is practically speaking all of them but GNOME (:pull:`2590`) +- A new option :opt:`scrollback_indicator_opacity` to show a small indicator on the right hand side of the screen of how much you have scrolled + - Wayland: Support fractional scales so that there is no wasted drawing at larger scale followed by resizing in the compositor - Wayland: Support preferred integer scales diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 270af8389..07ec9dc18 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -374,6 +374,14 @@ is changed it will only affect newly created windows, not existing ones. ''' ) +opt('scrollback_indicator_opacity', '0.0', + option_type='unit_float', ctype='float', long_text=''' +The opacity of the scrollback indicator which is a small colored rectangle that moves +along the right hand side of the window as you scroll, indicating what fraction you +have scrolled. The default is zero which means fully transparent, aka invisible. +Set to a value between zero and one to see the indicator.''') + + opt('scrollback_pager', 'less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER', option_type='to_cmdline', long_text=''' diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 550bc40b4..32af7520e 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1172,6 +1172,9 @@ class Parser: def scrollback_fill_enlarged_window(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['scrollback_fill_enlarged_window'] = to_bool(val) + def scrollback_indicator_opacity(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['scrollback_indicator_opacity'] = unit_float(val) + def scrollback_lines(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['scrollback_lines'] = scrollback_lines(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 4e987456c..82ac22744 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -135,6 +135,19 @@ convert_from_opts_cursor_stop_blinking_after(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_scrollback_indicator_opacity(PyObject *val, Options *opts) { + opts->scrollback_indicator_opacity = PyFloat_AsFloat(val); +} + +static void +convert_from_opts_scrollback_indicator_opacity(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "scrollback_indicator_opacity"); + if (ret == NULL) return; + convert_from_python_scrollback_indicator_opacity(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_scrollback_pager_history_size(PyObject *val, Options *opts) { opts->scrollback_pager_history_size = PyLong_AsUnsignedLong(val); @@ -1132,6 +1145,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_cursor_stop_blinking_after(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_scrollback_indicator_opacity(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_scrollback_pager_history_size(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_scrollback_fill_enlarged_window(py_opts, opts); diff --git a/kitty/options/types.py b/kitty/options/types.py index bb5469bb5..9c2bdf3f0 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -402,6 +402,7 @@ option_names = ( # {{{ 'resize_debounce_time', 'resize_in_steps', 'scrollback_fill_enlarged_window', + 'scrollback_indicator_opacity', 'scrollback_lines', 'scrollback_pager', 'scrollback_pager_history_size', @@ -562,6 +563,7 @@ class Options: resize_debounce_time: typing.Tuple[float, float] = (0.1, 0.5) resize_in_steps: bool = False scrollback_fill_enlarged_window: bool = False + scrollback_indicator_opacity: float = 0 scrollback_lines: int = 2000 scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER'] scrollback_pager_history_size: int = 0 diff --git a/kitty/shaders.c b/kitty/shaders.c index 77529cbce..734197136 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -594,6 +594,31 @@ draw_tint(bool premult, Screen *screen, const CellRenderData *crd) { glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +static bool +draw_scroll_indicator(bool premult, Screen *screen, const CellRenderData *crd) { + if (OPT(scrollback_indicator_opacity) <= 0 || screen->linebuf != screen->main_linebuf || !screen->scrolled_by) return false; + glEnable(GL_BLEND); + if (premult) { BLEND_PREMULT } else { BLEND_ONTO_OPAQUE } + bind_program(TINT_PROGRAM); + const color_type bar_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg).rgb; + GLfloat alpha = 0.8f; + float frac = (float)screen->scrolled_by / (float)screen->historybuf->count; + const GLfloat bar_height = crd->gl.dy; + GLfloat bottom = (crd->gl.ystart - crd->gl.height); + bottom += MAX(0, crd->gl.height - bar_height) * frac; +#define C(shift) srgb_color((bar_color >> shift) & 0xFF) * premult_factor + GLfloat premult_factor = premult ? alpha : 1.0f; + glUniform4f(tint_program_layout.uniforms.tint_color, C(16), C(8), C(0), alpha); +#undef C + GLfloat width = 0.5f * crd->gl.dx; + GLfloat left = (GLfloat)(crd->gl.xstart + (screen->columns * crd->gl.dx - width)); + glUniform4f(tint_program_layout.uniforms.edges, left, bottom, left + width, bottom + bar_height); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisable(GL_BLEND); + return true; +} + + static float prev_inactive_text_alpha = -1; static void @@ -972,14 +997,16 @@ draw_cells(ssize_t vao_idx, const WindowRenderData *srd, OSWindow *os_window, bo scale_rendered_graphic(grman->render_data.item + i, srd->xstart, srd->ystart, crd.x_ratio, crd.y_ratio); } } + bool use_premult = false; has_underlying_image |= grman->num_of_below_refs > 0 || grman->num_of_negative_refs > 0; if (os_window->is_semi_transparent) { - if (has_underlying_image) draw_cells_interleaved_premult(vao_idx, screen, os_window, &crd, wl); + if (has_underlying_image) { draw_cells_interleaved_premult(vao_idx, screen, os_window, &crd, wl); use_premult = true; } else draw_cells_simple(vao_idx, screen, &crd, os_window->is_semi_transparent); } else { if (has_underlying_image) draw_cells_interleaved(vao_idx, screen, os_window, &crd, wl); else draw_cells_simple(vao_idx, screen, &crd, os_window->is_semi_transparent); } + draw_scroll_indicator(use_premult, screen, &crd); if (screen->start_visual_bell_at) { GLfloat intensity = get_visual_bell_intensity(screen); diff --git a/kitty/state.h b/kitty/state.h index 3b5204803..19fb28b3c 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -53,7 +53,7 @@ typedef struct { float macos_thicken_font; WindowTitleIn macos_show_window_title_in; char *bell_path, *bell_theme; - float background_opacity, dim_opacity; + float background_opacity, dim_opacity, scrollback_indicator_opacity; float text_contrast, text_gamma_adjustment; bool text_old_gamma;