Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implement smooth and precise scrolling #1454

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions kitty/child-monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ schedule_write_to_child(unsigned long id, unsigned int num, ...) {
}
screen->write_buf_sz = screen->write_buf_used + sz;
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
if (screen->write_buf == NULL) { fatal("Out of memory."); }
if (screen->write_buf == NULL) fatal("Out of memory.");
}
va_start(ap, num);
for (unsigned int i = 0; i < num; i++) {
Expand All @@ -259,7 +259,7 @@ schedule_write_to_child(unsigned long id, unsigned int num, ...) {
if (screen->write_buf_sz > BUFSIZ && screen->write_buf_used < BUFSIZ) {
screen->write_buf_sz = BUFSIZ;
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
if (screen->write_buf == NULL) { fatal("Out of memory."); }
if (screen->write_buf == NULL) fatal("Out of memory.");
}
if (screen->write_buf_used) wakeup_io_loop(self, false);
screen_mutex(unlock, write);
Expand Down Expand Up @@ -589,8 +589,13 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *

static inline void
render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
static bool first_time = true;
if (first_time) {
setup_scroll(os_window);
first_time = false;
}
// ensure all pixels are cleared to background color at least once in every buffer
if (os_window->clear_count++ < 3) blank_os_window(os_window);
if (os_window->clear_count++ < 2) blank_os_window(os_window);
Tab *tab = os_window->tabs + os_window->active_tab;
BorderRects *br = &tab->border_rects;
bool static_live_resize_in_progress = os_window->live_resize.in_progress && OPT(resize_draw_strategy) == RESIZE_DRAW_STATIC;
Expand All @@ -603,19 +608,24 @@ render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_windo
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
br->is_dirty = false;
}
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx * x_ratio, TD.dy * y_ratio, TD.screen, os_window, true, false);
for (unsigned int i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;
if (w->visible && WD.screen) {
before_render();
bool is_active_window = i == tab->active_window;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx * x_ratio, WD.dy * y_ratio, WD.screen, os_window, is_active_window, true);
if (WD.screen->start_visual_bell_at != 0) {
monotonic_t bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
set_maximum_wait(bell_left);
if (WD.screen->render_not_only_pixel_scroll) {
WD.screen->render_not_only_pixel_scroll = false;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx * x_ratio, WD.dy * y_ratio, WD.screen, os_window, is_active_window, true);
if (WD.screen->start_visual_bell_at != 0) {
monotonic_t bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
set_maximum_wait(bell_left);
}
}
after_render(os_window, (WD.screen->scrolled_by_pixels * 2.0) / os_window->viewport_height);
w->cursor_visible_at_last_render = WD.screen->cursor_render_info.is_visible; w->last_cursor_x = WD.screen->cursor_render_info.x; w->last_cursor_y = WD.screen->cursor_render_info.y; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
}
}
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true, false);
swap_window_buffers(os_window);
os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;
os_window->focused_at_last_render = os_window->is_focused;
Expand Down Expand Up @@ -688,7 +698,7 @@ render(monotonic_t now, bool input_read) {
bool needs_render = w->is_damaged || w->live_resize.in_progress;
if (w->viewport_size_dirty) {
w->clear_count = 0;
update_surface_size(w->viewport_width, w->viewport_height, w->offscreen_texture_id);
update_surface_size(w->viewport_width, w->viewport_height, w->offscreen_texture_id, w->scroll_texture_id);
w->viewport_size_dirty = false;
needs_render = true;
}
Expand Down
2 changes: 1 addition & 1 deletion kitty/fonts.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells)

static inline void
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, bool center_glyph) {
static SpritePosition* sprite_position[16];
static SpritePosition* sprite_position[16]; // TODO: Remove magic number
int error = 0;
num_cells = MIN(arraysz(sprite_position), num_cells);
for (unsigned int i = 0; i < num_cells; i++) {
Expand Down
6 changes: 5 additions & 1 deletion kitty/gl.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@ gl_init() {
}

void
update_surface_size(int w, int h, GLuint offscreen_texture_id) {
update_surface_size(int w, int h, GLuint offscreen_texture_id, GLuint scroll_texture_id) {
glViewport(0, 0, w, h);
if (scroll_texture_id) {
glBindTexture(GL_TEXTURE_2D, scroll_texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
if (offscreen_texture_id) {
glBindTexture(GL_TEXTURE_2D, offscreen_texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
Expand Down
2 changes: 1 addition & 1 deletion kitty/gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ typedef struct {


void gl_init(void);
void update_surface_size(int w, int h, GLuint offscreen_texture_id);
void update_surface_size(int w, int h, GLuint offscreen_texture_id, GLuint scroll_texture_id);
void free_texture(GLuint *tex_id);
void free_framebuffer(GLuint *fb_id);
void remove_vao(ssize_t vao_idx);
Expand Down
2 changes: 1 addition & 1 deletion kitty/glfw.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
window->live_resize.width = MAX(0, width); window->live_resize.height = MAX(0, height);
window->live_resize.num_of_resize_events++;
make_os_window_context_current(window);
update_surface_size(width, height, window->offscreen_texture_id);
update_surface_size(width, height, window->offscreen_texture_id, window->scroll_texture_id);
request_tick_callback();
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
global_state.callback_os_window = NULL;
Expand Down
1 change: 1 addition & 0 deletions kitty/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ on_key_input(GLFWkeyevent *ev) {
}
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
pixel_scroll(screen, 0);
}
bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD;
if (ok_to_send) {
Expand Down
23 changes: 12 additions & 11 deletions kitty/mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ do_drag_scroll(Window *w, bool upwards) {
Screen *screen = w->render_data.screen;
if (screen->linebuf == screen->main_linebuf) {
screen_history_scroll(screen, SCROLL_LINE, upwards);
pixel_scroll(screen, 0);
update_drag(false, w, false, 0);
if (mouse_cursor_shape != ARROW) {
mouse_cursor_shape = ARROW;
Expand Down Expand Up @@ -651,18 +652,12 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
int s;
bool is_high_resolution = flags & 1;

double pixels = screen->pending_scroll_pixels;
if (is_high_resolution) {
yoffset *= OPT(touch_scroll_multiplier);
if (yoffset * screen->pending_scroll_pixels < 0) {
screen->pending_scroll_pixels = 0; // change of direction
}
double pixels = screen->pending_scroll_pixels + yoffset;
if (fabs(pixels) < global_state.callback_os_window->fonts_data->cell_height) {
screen->pending_scroll_pixels = pixels;
return;
}
pixels += yoffset;
s = (int)round(pixels) / (int)global_state.callback_os_window->fonts_data->cell_height;
screen->pending_scroll_pixels = pixels - s * (int) global_state.callback_os_window->fonts_data->cell_height;
pixels = pixels - s * (int) global_state.callback_os_window->fonts_data->cell_height;
} else {
if (screen->linebuf == screen->main_linebuf || !screen->modes.mouse_tracking_mode) {
// Only use wheel_scroll_multiplier if we are scrolling kitty scrollback or in mouse
Expand All @@ -677,13 +672,18 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
// apparently on cocoa some mice generate really small yoffset values
// when scrolling slowly https://github.com/kovidgoyal/kitty/issues/1238
if (s == 0 && yoffset != 0) s = yoffset > 0 ? 1 : -1;
screen->pending_scroll_pixels = 0;
}
if (s == 0) return;
bool upwards = s > 0;
//printf("asdf %f\n", pixels);
if (screen->linebuf == screen->main_linebuf) {
screen_history_scroll(screen, abs(s), upwards);
if (screen->scrolled_by == 0 && pixels < 0) pixels = 0;
if (screen->scrolled_by == screen->historybuf->count && pixels > 0) pixels = 0;
screen->pending_scroll_pixels = pixels;
pixel_scroll(screen, (int)pixels);
} else {
pixels = 0.0;
pixel_scroll(screen, (int)pixels);
if (screen->modes.mouse_tracking_mode) {
int sz = encode_mouse_event(w, upwards ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5, PRESS, 0);
if (sz > 0) {
Expand All @@ -696,6 +696,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
fake_scroll(abs(s), upwards);
}
}
screen->pending_scroll_pixels = pixels;
}

static PyObject*
Expand Down
11 changes: 11 additions & 0 deletions kitty/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->modes = empty_modes;
self->is_dirty = true;
self->scroll_changed = false;
self->pixel_scroll_changed = false;
self->render_not_only_pixel_scroll = false;
self->margin_top = 0; self->margin_bottom = self->lines - 1;
self->history_line_added_count = 0;
RESET_CHARSETS;
Expand Down Expand Up @@ -657,6 +659,7 @@ screen_toggle_screen_buffer(Screen *self) {
self->grman = self->main_grman;
}
screen_history_scroll(self, SCROLL_FULL, false);
pixel_scroll(self, 0);
self->is_dirty = true;
self->selection = EMPTY_SELECTION;
}
Expand Down Expand Up @@ -1199,6 +1202,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
self->scrolled_by = 0;
self->scroll_changed = true;
}
pixel_scroll(self, 0);
}
}

Expand Down Expand Up @@ -2205,6 +2209,12 @@ screen_selection_range_for_word(Screen *self, const index_type x, const index_ty
#undef is_ok
}

void pixel_scroll(Screen *self, int amt) {
//printf("pixel_scroll(%d)\n", amt);
self->scrolled_by_pixels = amt;
self->pixel_scroll_changed = true;
}

bool
screen_history_scroll(Screen *self, int amt, bool upwards) {
switch(amt) {
Expand Down Expand Up @@ -2239,6 +2249,7 @@ static PyObject*
scroll(Screen *self, PyObject *args) {
int amt, upwards;
if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL;
pixel_scroll(self, 0);
if (screen_history_scroll(self, amt, upwards)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}
Expand Down
4 changes: 3 additions & 1 deletion kitty/screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ typedef struct {
PyObject_HEAD

unsigned int columns, lines, margin_top, margin_bottom, charset, scrolled_by;
int scrolled_by_pixels;
double pending_scroll_pixels;
CellPixelSize cell_size;
OverlayLine overlay_line;
Expand All @@ -87,7 +88,7 @@ typedef struct {
IterationData selection, url;
unsigned int cursor_x, cursor_y, scrolled_by;
} last_rendered;
bool use_latin1, is_dirty, scroll_changed, reload_all_gpu_data;
bool use_latin1, is_dirty, scroll_changed, pixel_scroll_changed, reload_all_gpu_data, render_not_only_pixel_scroll;
Cursor *cursor;
SavepointBuffer main_savepoints, alt_savepoints;
SavemodesBuffer modes_savepoints;
Expand Down Expand Up @@ -197,6 +198,7 @@ bool screen_selection_range_for_line(Screen *self, index_type y, index_type *sta
bool screen_selection_range_for_word(Screen *self, const index_type x, const index_type y, index_type *, index_type *, index_type *start, index_type *end, bool);
void screen_start_selection(Screen *self, index_type x, index_type y, bool, bool, SelectionExtendMode);
void screen_update_selection(Screen *self, index_type x, index_type y, bool in_left_half, bool ended, bool start_extended_selection);
void pixel_scroll(Screen *self, int amt);
bool screen_history_scroll(Screen *self, int amt, bool upwards);
Line* screen_visual_line(Screen *self, index_type y);
unsigned long screen_current_char_width(Screen *self);
Expand Down
11 changes: 11 additions & 0 deletions kitty/scroll_fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version GLSL_VERSION
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D screenTexture;

void main()
{
FragColor = texture(screenTexture, TexCoords);
}
13 changes: 13 additions & 0 deletions kitty/scroll_vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#version GLSL_VERSION
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTexCoords;

uniform float offset;

out vec2 TexCoords;

void main()
{
gl_Position = vec4(aPos.x, aPos.y - offset, 0.0, 1.0);
TexCoords = aTexCoords;
}
Loading