diff --git a/input.c b/input.c index 27bbbbcd..27009810 100644 --- a/input.c +++ b/input.c @@ -1328,17 +1328,25 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, const bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0; const bool scroll_up = y < term->margins.top; - const bool scroll_down = y >= term->height - term->margins.bottom; + const bool scroll_down = y > term->height - term->margins.bottom; + + if (!scroll_up && !scroll_down) + selection_stop_scroll_timer(term); /* Update selection */ if (!term->is_searching) { - if (scroll_up || scroll_down) { - if (scroll_up) - cmd_scrollback_up(term, 1); - if (scroll_down) - cmd_scrollback_down(term, 1); - cursor_is_on_new_cell = true; + int distance = scroll_up + ? term->margins.top - y + : y - (term->height - term->margins.bottom); + + assert(distance > 0); + distance /= term->scale; + + selection_start_scroll_timer( + term, 100000000 / (distance > 0 ? distance : 1), + scroll_up ? SELECTION_SCROLL_UP : SELECTION_SCROLL_DOWN, + selection_col); } if (cursor_is_on_new_cell || term->selection.end.row < 0) diff --git a/selection.c b/selection.c index 7475f229..c774c713 100644 --- a/selection.c +++ b/selection.c @@ -15,6 +15,7 @@ #include "log.h" #include "async.h" +#include "commands.h" #include "config.h" #include "extract.h" #include "grid.h" @@ -619,6 +620,7 @@ selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial) if (!term->selection.ongoing) return; + selection_stop_scroll_timer(term); term->selection.ongoing = false; if (term->selection.start.row < 0 || term->selection.end.row < 0) @@ -647,6 +649,10 @@ selection_cancel(struct terminal *term) term->selection.start.row, term->selection.start.col, term->selection.end.row, term->selection.end.col); + if (term->selection.auto_scroll.fd >= 0) { + fdm_del(term->fdm, term->selection.auto_scroll.fd); + term->selection.auto_scroll.fd = -1; + } if (term->selection.start.row >= 0 && term->selection.end.row >= 0) { foreach_selected( @@ -789,6 +795,103 @@ selection_mark_row( selection_finalize(seat, term, serial); } +static bool +fdm_scroll_timer(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + + uint64_t expiration_count; + ssize_t ret = read( + term->selection.auto_scroll.fd, + &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read selection scroll timer"); + return false; + } + + switch (term->selection.auto_scroll.direction) { + case SELECTION_SCROLL_UP: + for (uint64_t i = 0; i < expiration_count; i++) + cmd_scrollback_up(term, 1); + selection_update(term, term->selection.auto_scroll.col, 0); + break; + + case SELECTION_SCROLL_DOWN: + for (uint64_t i = 0; i < expiration_count; i++) + cmd_scrollback_down(term, 1); + selection_update(term, term->selection.auto_scroll.col, term->rows - 1); + break; + } + + + return true; +} + +void +selection_start_scroll_timer(struct terminal *term, int interval_ns, + enum selection_scroll_direction direction, int col) +{ + if (!term->selection.ongoing) + return; + + if (term->selection.auto_scroll.fd < 0) { + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + if (fd < 0) { + LOG_ERRNO("failed to create selection scroll timer"); + goto err; + } + + if (!fdm_add(term->fdm, fd, EPOLLIN, &fdm_scroll_timer, term)) { + close(fd); + return; + } + + term->selection.auto_scroll.fd = fd; + } + + struct itimerspec timer; + if (timerfd_gettime(term->selection.auto_scroll.fd, &timer) < 0) { + LOG_ERRNO("failed to get current selection scroll timer value"); + goto err; + } + + if (timer.it_value.tv_sec == 0 && timer.it_value.tv_nsec == 0) + timer.it_value.tv_nsec = 1; + + timer.it_interval.tv_sec = interval_ns / 1000000000; + timer.it_interval.tv_nsec = interval_ns % 1000000000; + + if (timerfd_settime(term->selection.auto_scroll.fd, 0, &timer, NULL) < 0) { + LOG_ERRNO("failed to set new selection scroll timer value"); + goto err; + } + + term->selection.auto_scroll.direction = direction; + term->selection.auto_scroll.col = col; + + return; + +err: + selection_stop_scroll_timer(term); + return; +} + +void +selection_stop_scroll_timer(struct terminal *term) +{ + if (term->selection.auto_scroll.fd < 0) + return; + + fdm_del(term->fdm, term->selection.auto_scroll.fd); + term->selection.auto_scroll.fd = -1; +} static void target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) diff --git a/selection.h b/selection.h index 547da2fc..888b2f05 100644 --- a/selection.h +++ b/selection.h @@ -74,3 +74,8 @@ void text_from_primary( struct seat *seat, struct terminal *term, void (*cb)(const char *data, size_t size, void *user), void (*dont)(void *user), void *user); + +void selection_start_scroll_timer( + struct terminal *term, int interval_ns, + enum selection_scroll_direction direction, int col); +void selection_stop_scroll_timer(struct terminal *term); diff --git a/terminal.c b/terminal.c index 0afe39d5..2cb53ad3 100644 --- a/terminal.c +++ b/terminal.c @@ -985,6 +985,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .selection = { .start = {-1, -1}, .end = {-1, -1}, + .auto_scroll = { + .fd = -1, + }, }, .normal = {.scroll_damage = tll_init(), .sixel_images = tll_init()}, .alt = {.scroll_damage = tll_init(), .sixel_images = tll_init()}, @@ -1163,6 +1166,7 @@ term_shutdown(struct terminal *term) term_cursor_blink_disable(term); + fdm_del(term->fdm, term->selection.auto_scroll.fd); fdm_del(term->fdm, term->render.app_sync_updates.timer_fd); fdm_del(term->fdm, term->delayed_render_timer.lower_fd); fdm_del(term->fdm, term->delayed_render_timer.upper_fd); @@ -1175,6 +1179,7 @@ term_shutdown(struct terminal *term) else close(term->ptmx); + term->selection.auto_scroll.fd = -1; term->render.app_sync_updates.timer_fd = -1; term->delayed_render_timer.lower_fd = -1; term->delayed_render_timer.upper_fd = -1; @@ -1225,6 +1230,7 @@ term_destroy(struct terminal *term) } } + fdm_del(term->fdm, term->selection.auto_scroll.fd); fdm_del(term->fdm, term->render.app_sync_updates.timer_fd); fdm_del(term->fdm, term->delayed_render_timer.lower_fd); fdm_del(term->fdm, term->delayed_render_timer.upper_fd); diff --git a/terminal.h b/terminal.h index b6fb2aec..0a71abb4 100644 --- a/terminal.h +++ b/terminal.h @@ -182,6 +182,7 @@ enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR }; enum selection_kind { SELECTION_NONE, SELECTION_NORMAL, SELECTION_BLOCK }; enum selection_direction {SELECTION_UNDIR, SELECTION_LEFT, SELECTION_RIGHT}; +enum selection_scroll_direction {SELECTION_SCROLL_UP, SELECTION_SCROLL_DOWN}; struct ptmx_buffer { void *data; @@ -346,6 +347,12 @@ struct terminal { struct coord start; struct coord end; bool ongoing; + + struct { + int fd; + int col; + enum selection_scroll_direction direction; + } auto_scroll; } selection; bool is_searching;