mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-15 22:05:24 -05:00
selection: auto-scroll: selection keeps scrolling while mouse is outside grid
Moving the mouse outside the grid while we have an on-going selection now starts a timer. The interval of this timer depends on the mouse’s distance from the grid - the further away the mouse is, the shorter interval. On each timer timeout, we scroll one line, and update the selection. Thus, the shorter the interval, the faster we scroll. The timer is canceled as soon as the mouse enters the grid again, or the selection is either canceled or finalized. The timer FD is created and destroyed on-demand. Most of the logic is now in selection.c. The exception is the calculation of the timer interval, which depends on the mouse’s position. Thus, this is done in input.c. The scroll+selection update logic needs to know a) which direction we’re scrolling in, and b) which *column* the selection should be updated with. If the mouse is outside the grid’s left or right margins, the stored mouse column will be -1. I.e. we don’t know whether the mouse is on the left or right side of the grid. This is why the caller, that starts the timer, must provide this value. The same applies to top and bottom margins, but since we already have the scroll *direction*, which row value to use can be derived from this.
This commit is contained in:
parent
2303affc87
commit
7fedf2f801
5 changed files with 136 additions and 7 deletions
103
selection.c
103
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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue