1. Fixed when selecting text in copy-mode, don't cancel when reaching

the bottom of the window
2. Fixed the selection so that it does not change when the pane scrolls
3. Made it so you can modify the selection by dragging in it.
This commit is contained in:
Michael Grant
2026-07-09 07:09:52 +01:00
parent f92cb43ce4
commit 125c77c5fc

View File

@@ -48,6 +48,8 @@ static int window_copy_pagedown1(struct window_mode_entry *, int, int);
static void window_copy_next_paragraph(struct window_mode_entry *);
static void window_copy_previous_paragraph(struct window_mode_entry *);
static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
static void window_copy_redraw_lines1(struct window_mode_entry *, u_int,
u_int, int);
static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
u_int);
static void window_copy_redraw_screen(struct window_mode_entry *);
@@ -67,6 +69,8 @@ static u_int window_copy_cursor_unoffset(struct window_mode_entry *, u_int,
u_int);
static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int);
static void window_copy_write_line1(struct window_mode_entry *,
struct screen_write_ctx *, u_int, int);
static void window_copy_write_lines(struct window_mode_entry *,
struct screen_write_ctx *, u_int, u_int);
static char *window_copy_match_at_cursor(struct window_copy_mode_data *);
@@ -103,6 +107,10 @@ static void window_copy_goto_line(struct window_mode_entry *, const char *);
static void window_copy_update_cursor(struct window_mode_entry *, u_int,
u_int);
static void window_copy_start_selection(struct window_mode_entry *);
static int window_copy_mouse_in_selection(struct window_mode_entry *,
u_int, u_int, int *, int *);
static int window_copy_update_selection_view(struct window_mode_entry *,
int, int);
static int window_copy_adjust_selection(struct window_mode_entry *,
u_int *, u_int *);
static int window_copy_set_selection(struct window_mode_entry *, int, int);
@@ -211,6 +219,7 @@ enum {
};
enum window_copy_cmd_action {
WINDOW_COPY_CMD_NOREDRAW,
WINDOW_COPY_CMD_NOTHING,
WINDOW_COPY_CMD_REDRAW,
WINDOW_COPY_CMD_CANCEL,
@@ -854,7 +863,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
window_copy_cursor_end_of_line(wme);
}
if (scroll_exit && data->oy == 0) {
if (scroll_exit && data->oy == 0 && data->screen.sel == NULL) {
window_pane_reset_mode(wp);
return;
}
@@ -972,7 +981,7 @@ window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
window_copy_cursor_end_of_line(wme);
}
if (scroll_exit && data->oy == 0)
if (scroll_exit && data->oy == 0 && data->screen.sel == NULL)
return (1);
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
@@ -1548,7 +1557,7 @@ window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
window_copy_copy_selection(wme, prefix, set_paste, set_clip);
free(prefix);
return (WINDOW_COPY_CMD_NOTHING);
return (WINDOW_COPY_CMD_NOREDRAW);
}
static enum window_copy_cmd_action
@@ -2349,11 +2358,28 @@ window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix;
int dragging;
for (; np != 0; np--)
if (data->oy == 0)
return (WINDOW_COPY_CMD_NOREDRAW);
dragging = (cs->c != NULL && cs->c->tty.mouse_drag_flag != 0);
if (data->screen.sel != NULL && !dragging) {
data->cursordrag = CURSORDRAG_NONE;
data->lineflag = LINE_SEL_NONE;
window_copy_scroll_up(wme, np);
return (WINDOW_COPY_CMD_NOTHING);
}
for (; np != 0; np--) {
if (data->scroll_exit && data->oy == 0 &&
data->screen.sel != NULL)
break;
window_copy_cursor_down(wme, 1);
if (data->scroll_exit && data->oy == 0)
return (WINDOW_COPY_CMD_CANCEL);
if (data->scroll_exit && data->oy == 0 &&
data->screen.sel == NULL)
return (WINDOW_COPY_CMD_CANCEL);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -2375,7 +2401,20 @@ static enum window_copy_cmd_action
window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int np = wme->prefix;
int dragging;
if (data->oy == screen_hsize(data->backing))
return (WINDOW_COPY_CMD_NOREDRAW);
dragging = (cs->c != NULL && cs->c->tty.mouse_drag_flag != 0);
if (data->screen.sel != NULL && !dragging) {
data->cursordrag = CURSORDRAG_NONE;
data->lineflag = LINE_SEL_NONE;
window_copy_scroll_down(wme, np);
return (WINDOW_COPY_CMD_NOTHING);
}
for (; np != 0; np--)
window_copy_cursor_up(wme, 1);
@@ -2551,7 +2590,7 @@ window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
free(command);
free(prefix);
return (WINDOW_COPY_CMD_NOTHING);
return (WINDOW_COPY_CMD_NOREDRAW);
}
static enum window_copy_cmd_action
@@ -3764,6 +3803,8 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
window_pane_reset_mode(wp);
else if (action == WINDOW_COPY_CMD_REDRAW)
window_copy_redraw_screen(wme);
else if (action == WINDOW_COPY_CMD_NOREDRAW)
return;
else if (action == WINDOW_COPY_CMD_NOTHING) {
/*
* Nothing is not actually nothing - most commands at least
@@ -5142,6 +5183,13 @@ window_copy_get_current_offset(struct window_pane *wp, u_int *offset,
static void
window_copy_write_line(struct window_mode_entry *wme,
struct screen_write_ctx *ctx, u_int py)
{
window_copy_write_line1(wme, ctx, py, 1);
}
static void
window_copy_write_line1(struct window_mode_entry *wme,
struct screen_write_ctx *ctx, u_int py, int clear)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
@@ -5166,7 +5214,8 @@ window_copy_write_line(struct window_mode_entry *wme,
content_sx = sx;
screen_write_cursormove(ctx, 0, py, 0);
screen_write_clearline(ctx, 8);
if (clear)
screen_write_clearline(ctx, 8);
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
@@ -5265,11 +5314,18 @@ window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
if (end < gd->sy + data->oy - 1)
end++;
}
window_copy_redraw_lines(wme, start, end - start + 1);
window_copy_redraw_lines1(wme, start, end - start + 1, 0);
}
static void
window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
{
window_copy_redraw_lines1(wme, py, ny, 1);
}
static void
window_copy_redraw_lines1(struct window_mode_entry *wme, u_int py, u_int ny,
int clear)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
@@ -5280,7 +5336,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
if (window_copy_line_number_width(wme) != 0) {
screen_write_start(&ctx, &data->screen);
for (i = py; i < py + ny; i++)
window_copy_write_line(wme, &ctx, i);
window_copy_write_line1(wme, &ctx, i, clear);
screen_write_cursormove(&ctx,
window_copy_cursor_offset(wme, data->cx, screen_size_x(s)),
data->cy, 0);
@@ -5294,7 +5350,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
else
screen_write_start_pane(&ctx, wp, NULL);
for (i = py; i < py + ny; i++)
window_copy_write_line(wme, &ctx, i);
window_copy_write_line1(wme, &ctx, i, clear);
screen_write_cursormove(&ctx,
window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy,
0);
@@ -5486,6 +5542,71 @@ window_copy_start_selection(struct window_mode_entry *wme)
window_copy_set_selection(wme, 1, 0);
}
static int
window_copy_mouse_in_selection(struct window_mode_entry *wme, u_int x, u_int y,
int *on_start, int *on_end)
{
struct window_copy_mode_data *data = wme->data;
u_int hsize, screeny;
u_int mx, my, selx, sely, endselx, endsely;
long long mpos, spos, epos, dstart, dend;
if (on_start != NULL)
*on_start = 0;
if (on_end != NULL)
*on_end = 0;
if (data->screen.sel == NULL)
return (0);
hsize = screen_hsize(data->backing);
screeny = hsize - data->oy;
selx = window_copy_cursor_offset(wme, data->selx,
screen_size_x(&data->screen));
sely = data->sely - screeny;
if (data->sely >= screeny && sely < screen_size_y(&data->screen) &&
x == selx && y == sely) {
if (on_start != NULL)
*on_start = 1;
return (1);
}
endselx = window_copy_cursor_offset(wme, data->endselx,
screen_size_x(&data->screen));
endsely = data->endsely - screeny;
if (data->endsely >= screeny &&
endsely < screen_size_y(&data->screen) &&
x == endselx && y == endsely) {
if (on_end != NULL)
*on_end = 1;
return (1);
}
if (!screen_check_selection(&data->screen, x, y))
return (0);
if (on_start != NULL || on_end != NULL) {
mx = window_copy_cursor_unoffset(wme, x,
screen_size_x(&data->screen));
my = screeny + y;
mpos = (long long)my * (screen_size_x(&data->screen) + 1) + mx;
spos = (long long)data->sely *
(screen_size_x(&data->screen) + 1) + data->selx;
epos = (long long)data->endsely *
(screen_size_x(&data->screen) + 1) + data->endselx;
dstart = llabs(mpos - spos);
dend = llabs(mpos - epos);
if (dstart <= dend) {
if (on_start != NULL)
*on_start = 1;
} else {
if (on_end != NULL)
*on_end = 1;
}
}
return (1);
}
static int
window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
u_int *sely)
@@ -5531,6 +5652,31 @@ window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
return (window_copy_set_selection(wme, may_redraw, no_reset));
}
static int
window_copy_update_selection_view(struct window_mode_entry *wme, int may_redraw,
int no_reset)
{
struct window_copy_mode_data *data = wme->data;
u_int selx, sely, endselx, endsely;
int changed;
if (data->cursordrag != CURSORDRAG_NONE)
return (window_copy_update_selection(wme, may_redraw, no_reset));
selx = data->selx;
sely = data->sely;
endselx = data->endselx;
endsely = data->endsely;
changed = window_copy_update_selection(wme, may_redraw, 1);
data->selx = selx;
data->sely = sely;
data->endselx = endselx;
data->endsely = endsely;
return (changed);
}
static int
window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
int no_reset)
@@ -6560,7 +6706,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
if (window_copy_line_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
@@ -6629,7 +6775,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 0, 0);
window_copy_update_selection_view(wme, 0, 0);
if (window_copy_line_numbers_active(wme)) {
if (window_copy_line_number_mode(wme) !=
WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) {
@@ -6718,6 +6864,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
struct window_mode_entry *wme;
struct window_copy_mode_data *data;
u_int x, y, yg;
int inside_selection, on_start, on_end;
if (c == NULL)
return;
@@ -6738,10 +6885,16 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
c->tty.mouse_drag_release = window_copy_drag_release;
data = wme->data;
on_start = on_end = 0;
inside_selection = window_copy_mouse_in_selection(wme, x, y,
&on_start, &on_end);
x = window_copy_cursor_unoffset(wme, x, screen_size_x(&data->screen));
yg = screen_hsize(data->backing) + y - data->oy;
if (x < data->selrx || x > data->endselrx || yg != data->selry)
if (on_start || on_end || !inside_selection ||
x < data->selrx || x > data->endselrx || yg != data->selry) {
data->lineflag = LINE_SEL_NONE;
data->selflag = SEL_CHAR;
}
switch (data->selflag) {
case SEL_WORD:
if (data->separators != NULL) {
@@ -6757,7 +6910,15 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
break;
case SEL_CHAR:
window_copy_update_cursor(wme, x, y);
window_copy_start_selection(wme);
if (!inside_selection)
window_copy_start_selection(wme);
else {
if (on_start)
data->cursordrag = CURSORDRAG_SEL;
else if (on_end)
data->cursordrag = CURSORDRAG_ENDSEL;
window_copy_update_selection(wme, 1, 0);
}
break;
}
@@ -6833,6 +6994,7 @@ window_copy_drag_release(struct client *c, struct mouse_event *m)
data = wme->data;
if (window_copy_line_numbers_active(wme))
window_copy_drag_update(c, m);
data->cursordrag = CURSORDRAG_NONE;
evtimer_del(&data->dragtimer);
}