From 4feaf135564ec2210eddc35ced20b76e8f6d4317 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Sep 2017 15:06:23 +0530 Subject: [PATCH] Implement drag scrolling in C --- kitty/child-monitor.c | 10 +++++++++ kitty/data-types.h | 1 + kitty/glfw.c | 22 ++++++++++++++----- kitty/mouse.c | 49 ++++++++++++++++++++++++++++++++++--------- kitty/screen.c | 36 +++++++++++++++---------------- kitty/screen.h | 4 +++- kitty/state.h | 1 + 7 files changed, 88 insertions(+), 35 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 3622599c6..6b3a2eb4b 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -475,6 +475,8 @@ render_cursor(Window *w, double now) { } } +extern bool drag_scroll(Window *); + static inline bool render(ChildMonitor *self, double now) { double time_since_last_render = now - last_render_at; @@ -489,6 +491,14 @@ render(ChildMonitor *self, double now) { Window *w = tab->windows + i; #define WD w->render_data if (w->visible && WD.screen) { + if (w->last_drag_scroll_at > 0) { + if (now - w->last_drag_scroll_at >= 0.02) { + if (drag_scroll(w)) { + w->last_drag_scroll_at = now; + set_maximum_wait(0.02); + } else w->last_drag_scroll_at = 0; + } else set_maximum_wait(now - w->last_drag_scroll_at); + } draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen); if (WD.screen->start_visual_bell_at != 0) { double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at); diff --git a/kitty/data-types.h b/kitty/data-types.h index 84a605c95..58c02a740 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -34,6 +34,7 @@ typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_U #define ERROR_PREFIX "[PARSE ERROR]" typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MODE } MouseTrackingMode; typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol; +typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; #define MAX_CHILDREN 256 #define BLANK_CHAR 0 diff --git a/kitty/glfw.c b/kitty/glfw.c index 1c5d79652..52c8b495c 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -37,7 +37,7 @@ typedef struct { GLFWwindow *window; PyObject *framebuffer_size_callback, *char_mods_callback, *key_callback, *mouse_button_callback, *scroll_callback, *cursor_pos_callback, *window_focus_callback; - GLFWcursor *standard_cursor, *click_cursor; + GLFWcursor *standard_cursor, *click_cursor, *arrow_cursor; } WindowWrapper; // callbacks {{{ @@ -111,8 +111,18 @@ window_focus_callback(GLFWwindow UNUSED *w, int focused) { // }}} void -set_click_cursor(bool yes) { - glfwSetCursor(the_window->window, yes ? the_window->click_cursor : the_window->standard_cursor); +set_mouse_cursor(MouseShape type) { + switch(type) { + case HAND: + glfwSetCursor(the_window->window, the_window->click_cursor); + break; + case ARROW: + glfwSetCursor(the_window->window, the_window->arrow_cursor); + break; + default: + glfwSetCursor(the_window->window, the_window->standard_cursor); + break; + } } static PyObject* @@ -131,8 +141,10 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { global_state.viewport_width = width; global_state.viewport_height = height; self->standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); self->click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); - if (self->standard_cursor == NULL || self->click_cursor == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; } - set_click_cursor(false); + self->arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + if (self->standard_cursor == NULL || self->click_cursor == NULL || self->arrow_cursor == NULL) { + Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; } + set_mouse_cursor(0); glfwSetFramebufferSizeCallback(self->window, framebuffer_size_callback); glfwSetCharModsCallback(self->window, char_mods_callback); glfwSetKeyCallback(self->window, key_callback); diff --git a/kitty/mouse.c b/kitty/mouse.c index 0990aa730..530246944 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -10,8 +10,8 @@ #include "lineops.h" #include -extern void set_click_cursor(bool yes); -static bool has_click_cursor = false; +extern void set_mouse_cursor(MouseShape); +static MouseShape mouse_cursor_shape = BEAM; #define call_boss(name, ...) { \ PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \ @@ -52,12 +52,37 @@ update_drag(bool from_button, Window *w, bool is_release) { } } + +bool +drag_scroll(Window *w) { + unsigned int margin = global_state.cell_height / 2; + double x = global_state.mouse_x, y = global_state.mouse_y; + if (y < w->geometry.top || y > w->geometry.bottom) return false; + if (x < w->geometry.left || x > w->geometry.right) return false; + bool upwards = y <= w->geometry.top + margin ? true : false; + if (upwards || y >= w->geometry.bottom - margin) { + Screen *screen = w->render_data.screen; + if (screen->linebuf == screen->main_linebuf) { + screen_history_scroll(screen, SCROLL_LINE, upwards); + update_drag(false, w, false); + global_state.last_mouse_activity_at = monotonic(); + if (mouse_cursor_shape != ARROW) { + mouse_cursor_shape = ARROW; + set_mouse_cursor(mouse_cursor_shape); + } + return true; + } + } + return false; +} + + HANDLER(handle_move_event) { unsigned int x, y; if (!cell_for_pos(w, &x, &y)) return; Line *line = screen_visual_line(w->render_data.screen, y); - has_click_cursor = (line && line_url_start_at(line, x) < line->xnum) ? true : false; - if (x == w->mouse_cell_x && y == w->mouse_cell_y) return; + mouse_cursor_shape = (line && line_url_start_at(line, x) < line->xnum) ? HAND : BEAM; + bool mouse_cell_changed = x != w->mouse_cell_x || y != w->mouse_cell_y ? true : false; w->mouse_cell_x = x; w->mouse_cell_y = y; Screen *screen = w->render_data.screen; bool handle_in_kitty = ( @@ -67,9 +92,14 @@ HANDLER(handle_move_event) { ) ? false : true; if (handle_in_kitty) { if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) { - update_drag(false, w, false); + double now = monotonic(); + if ((now - w->last_drag_scroll_at) >= 0.02 || mouse_cell_changed) { + update_drag(false, w, false); + w->last_drag_scroll_at = monotonic(); + } } } else { + if (!mouse_cell_changed) return; // TODO: Implement this } } @@ -174,11 +204,10 @@ handle_tab_bar_mouse(int button, int UNUSED modifiers) { void mouse_event(int button, int modifiers) { - bool old_has_click_cursor = has_click_cursor; + MouseShape old_cursor = mouse_cursor_shape; bool in_tab_bar = global_state.num_tabs > 1 && global_state.mouse_y >= global_state.viewport_height - global_state.cell_height ? true : false; - has_click_cursor = false; if (in_tab_bar) { - has_click_cursor = true; + mouse_cursor_shape = HAND; handle_tab_bar_mouse(button, modifiers); } else { Tab *t = global_state.tabs + global_state.active_tab; @@ -189,7 +218,7 @@ mouse_event(int button, int modifiers) { } } } - if (has_click_cursor != old_has_click_cursor) { - set_click_cursor(has_click_cursor); + if (mouse_cursor_shape != old_cursor) { + set_mouse_cursor(mouse_cursor_shape); } } diff --git a/kitty/screen.c b/kitty/screen.c index c26ca4729..7cbc05af5 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -5,10 +5,6 @@ * Distributed under terms of the GPL3 license. */ -#define SCROLL_LINE -1 -#define SCROLL_PAGE -2 -#define SCROLL_FULL -3 - #define EXTRA_INIT PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL); #include "state.h" @@ -1371,10 +1367,8 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_ #undef is_ok } -static PyObject* -scroll(Screen *self, PyObject *args) { - int amt, upwards; - if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL; +bool +screen_history_scroll(Screen *self, int amt, bool upwards) { switch(amt) { case SCROLL_LINE: amt = 1; @@ -1386,24 +1380,28 @@ scroll(Screen *self, PyObject *args) { amt = self->historybuf->count; break; default: - if (amt < 0) { - PyErr_SetString(PyExc_ValueError, "scroll amounts must be positive numbers"); - return NULL; - } + amt = MAX(0, amt); break; } if (!upwards) { amt = MIN((unsigned int)amt, self->scrolled_by); amt *= -1; } - if (amt != 0) { - unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count); - if (new_scroll != self->scrolled_by) { - self->scrolled_by = new_scroll; - self->scroll_changed = true; - Py_RETURN_TRUE; - } + if (amt == 0) return false; + unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count); + if (new_scroll != self->scrolled_by) { + self->scrolled_by = new_scroll; + self->scroll_changed = true; + return true; } + return false; +} + +static PyObject* +scroll(Screen *self, PyObject *args) { + int amt, upwards; + if (!PyArg_ParseTuple(args, "ip", &amt, &upwards)) return NULL; + if (screen_history_scroll(self, amt, upwards)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } diff --git a/kitty/screen.h b/kitty/screen.h index ed448447a..6fc229d61 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -6,6 +6,8 @@ #pragma once +typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType; + void screen_align(Screen*); void screen_restore_cursor(Screen *); void screen_save_cursor(Screen *); @@ -68,6 +70,7 @@ bool screen_selection_range_for_line(Screen *self, index_type y, index_type *sta bool screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *start, index_type *end); void screen_start_selection(Screen *self, index_type x, index_type y); void screen_update_selection(Screen *self, index_type x, index_type y, bool ended); +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); #define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen); @@ -76,4 +79,3 @@ DECLARE_CH_SCREEN_HANDLER(backspace) DECLARE_CH_SCREEN_HANDLER(tab) DECLARE_CH_SCREEN_HANDLER(linefeed) DECLARE_CH_SCREEN_HANDLER(carriage_return) - diff --git a/kitty/state.h b/kitty/state.h index d161d8aa7..a8ae6472f 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -46,6 +46,7 @@ typedef struct { unsigned int mouse_cell_x, mouse_cell_y; WindowGeometry geometry; ClickQueue click_queue; + double last_drag_scroll_at; } Window; typedef struct {