mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
commit
d75e50230e
6 changed files with 164 additions and 1 deletions
|
|
@ -37,6 +37,10 @@
|
|||
* **csd.preferred** can now be set to `none` to disable window
|
||||
decorations. Note that some compositors will render SSDs despite
|
||||
this option being used (https://codeberg.org/dnkl/foot/issues/163).
|
||||
* Terminal content is now auto-scrolled when moving the mouse above or
|
||||
below the window while selecting
|
||||
(https://codeberg.org/dnkl/foot/issues/149).
|
||||
|
||||
|
||||
### Changed
|
||||
### Deprecated
|
||||
|
|
|
|||
38
input.c
38
input.c
|
|
@ -1325,10 +1325,46 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
= old_col != seat->mouse.col || old_row != seat->mouse.row;
|
||||
|
||||
/* Cursor is inside the grid, i.e. *not* in the margins */
|
||||
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
||||
const bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
||||
|
||||
enum selection_scroll_direction auto_scroll_direction
|
||||
= y < term->margins.top ? SELECTION_SCROLL_UP
|
||||
: y > term->height - term->margins.bottom ? SELECTION_SCROLL_DOWN
|
||||
: SELECTION_SCROLL_NOT;
|
||||
|
||||
if (auto_scroll_direction == SELECTION_SCROLL_NOT)
|
||||
selection_stop_scroll_timer(term);
|
||||
|
||||
/* Update selection */
|
||||
if (!term->is_searching) {
|
||||
if (auto_scroll_direction != SELECTION_SCROLL_NOT) {
|
||||
/*
|
||||
* Start ‘selection auto-scrolling’
|
||||
*
|
||||
* The speed of the scrolling is proportional to the
|
||||
* distance between the mouse and the grid; the
|
||||
* further away the mouse is, the faster we scroll.
|
||||
*
|
||||
* Note that the speed is measured in ‘intervals (in
|
||||
* ns) between each timed scroll of a single line’.
|
||||
*
|
||||
* Thus, the further away the mouse is, the smaller
|
||||
* interval value we use.
|
||||
*/
|
||||
|
||||
int distance = auto_scroll_direction == SELECTION_SCROLL_UP
|
||||
? term->margins.top - y
|
||||
: y - (term->height - term->margins.bottom);
|
||||
|
||||
assert(distance > 0);
|
||||
int divisor
|
||||
= distance * term->conf->scrollback.multiplier / term->scale;
|
||||
|
||||
selection_start_scroll_timer(
|
||||
term, 400000000 / (divisor > 0 ? divisor : 1),
|
||||
auto_scroll_direction, selection_col);
|
||||
}
|
||||
|
||||
if (cursor_is_on_new_cell || term->selection.end.row < 0)
|
||||
selection_update(term, selection_col, selection_row);
|
||||
}
|
||||
|
|
|
|||
105
selection.c
105
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,7 @@ selection_cancel(struct terminal *term)
|
|||
term->selection.start.row, term->selection.start.col,
|
||||
term->selection.end.row, term->selection.end.col);
|
||||
|
||||
selection_stop_scroll_timer(term);
|
||||
|
||||
if (term->selection.start.row >= 0 && term->selection.end.row >= 0) {
|
||||
foreach_selected(
|
||||
|
|
@ -789,6 +792,108 @@ 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_NOT:
|
||||
return true;
|
||||
|
||||
case SELECTION_SCROLL_UP:
|
||||
cmd_scrollback_up(term, expiration_count);
|
||||
selection_update(term, term->selection.auto_scroll.col, 0);
|
||||
break;
|
||||
|
||||
case SELECTION_SCROLL_DOWN:
|
||||
cmd_scrollback_down(term, expiration_count);
|
||||
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)
|
||||
{
|
||||
assert(direction != SELECTION_SCROLL_NOT);
|
||||
|
||||
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) {
|
||||
assert(term->selection.auto_scroll.direction == SELECTION_SCROLL_NOT);
|
||||
return;
|
||||
}
|
||||
|
||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||
term->selection.auto_scroll.fd = -1;
|
||||
term->selection.auto_scroll.direction = SELECTION_SCROLL_NOT;
|
||||
}
|
||||
|
||||
static void
|
||||
target(void *data, struct wl_data_source *wl_data_source, const char *mime_type)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()},
|
||||
|
|
@ -1164,6 +1167,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);
|
||||
|
|
@ -1176,6 +1180,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;
|
||||
|
|
@ -1226,6 +1231,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);
|
||||
|
|
|
|||
|
|
@ -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_NOT, SELECTION_SCROLL_UP, SELECTION_SCROLL_DOWN};
|
||||
|
||||
struct ptmx_buffer {
|
||||
void *data;
|
||||
|
|
@ -349,6 +350,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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue