1
0
mirror of https://github.com/tmate-io/tmate-ssh-server.git synced 2020-11-18 19:53:51 -08:00

Rectangle copy support, from Robin Lee Powell.

This commit is contained in:
Nicholas Marriott 2010-02-06 17:35:01 +00:00
parent 8aba77b7be
commit 3ef3802629
5 changed files with 181 additions and 58 deletions

View File

@ -89,6 +89,7 @@ struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
{ MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" }, { MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" },
{ MODEKEYCOPY_HISTORYTOP, "history-top" }, { MODEKEYCOPY_HISTORYTOP, "history-top" },
{ MODEKEYCOPY_LEFT, "cursor-left" }, { MODEKEYCOPY_LEFT, "cursor-left" },
{ MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" },
{ MODEKEYCOPY_MIDDLELINE, "middle-line" }, { MODEKEYCOPY_MIDDLELINE, "middle-line" },
{ MODEKEYCOPY_NEXTPAGE, "page-down" }, { MODEKEYCOPY_NEXTPAGE, "page-down" },
{ MODEKEYCOPY_NEXTSPACE, "next-space" }, { MODEKEYCOPY_NEXTSPACE, "next-space" },
@ -207,6 +208,7 @@ const struct mode_key_entry mode_key_vi_copy[] = {
{ 'l', 0, MODEKEYCOPY_RIGHT }, { 'l', 0, MODEKEYCOPY_RIGHT },
{ 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, { 'n', 0, MODEKEYCOPY_SEARCHAGAIN },
{ 'q', 0, MODEKEYCOPY_CANCEL }, { 'q', 0, MODEKEYCOPY_CANCEL },
{ 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE },
{ 'w', 0, MODEKEYCOPY_NEXTWORD }, { 'w', 0, MODEKEYCOPY_NEXTWORD },
{ KEYC_BSPACE, 0, MODEKEYCOPY_LEFT }, { KEYC_BSPACE, 0, MODEKEYCOPY_LEFT },
{ KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN },
@ -276,9 +278,10 @@ struct mode_key_tree mode_key_tree_emacs_choice;
/* emacs copy mode keys. */ /* emacs copy mode keys. */
const struct mode_key_entry mode_key_emacs_copy[] = { const struct mode_key_entry mode_key_emacs_copy[] = {
{ ' ', 0, MODEKEYCOPY_NEXTPAGE }, { ' ', 0, MODEKEYCOPY_NEXTPAGE },
{ '<' | KEYC_ESCAPE,0, MODEKEYCOPY_HISTORYTOP }, { '<' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYTOP },
{ '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM }, { '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM },
{ 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE }, { 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE },
{ 'R', 0, MODEKEYCOPY_RECTANGLETOGGLE },
{ '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION }, { '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION },
{ '\001' /* C-a */, 0, MODEKEYCOPY_STARTOFLINE }, { '\001' /* C-a */, 0, MODEKEYCOPY_STARTOFLINE },
{ '\002' /* C-b */, 0, MODEKEYCOPY_LEFT }, { '\002' /* C-b */, 0, MODEKEYCOPY_LEFT },

113
screen.c
View File

@ -228,41 +228,17 @@ screen_resize_y(struct screen *s, u_int sy)
/* Set selection. */ /* Set selection. */
void void
screen_set_selection(struct screen *s, screen_set_selection(struct screen *s, u_int sx, u_int sy,
u_int sx, u_int sy, u_int ex, u_int ey, struct grid_cell *gc) u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc)
{ {
struct screen_sel *sel = &s->sel; struct screen_sel *sel = &s->sel;
memcpy(&sel->cell, gc, sizeof sel->cell); memcpy(&sel->cell, gc, sizeof sel->cell);
sel->flag = 1; sel->flag = 1;
sel->rectflag = rectflag;
/* starting line < ending line -- downward selection. */ sel->sx = sx; sel->sy = sy;
if (sy < ey) { sel->ex = ex; sel->ey = ey;
sel->sx = sx; sel->sy = sy;
sel->ex = ex; sel->ey = ey;
return;
}
/* starting line > ending line -- upward selection. */
if (sy > ey) {
if (sx > 0) {
sel->sx = ex; sel->sy = ey;
sel->ex = sx - 1; sel->ey = sy;
} else {
sel->sx = ex; sel->sy = ey;
sel->ex = -1; sel->ey = sy - 1;
}
return;
}
/* starting line == ending line. */
if (ex < sx) {
sel->sx = ex; sel->sy = ey;
sel->ex = sx - 1; sel->ey = sy;
} else {
sel->sx = sx; sel->sy = sy;
sel->ex = ex; sel->ey = ey;
}
} }
/* Clear selection. */ /* Clear selection. */
@ -280,16 +256,81 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
{ {
struct screen_sel *sel = &s->sel; struct screen_sel *sel = &s->sel;
if (!sel->flag || py < sel->sy || py > sel->ey) if (!sel->flag)
return (0); return (0);
if (py == sel->sy && py == sel->ey) { if (sel->rectflag) {
if (px < sel->sx || px > sel->ex) if (sel->sy < sel->ey) {
return (0); /* start line < end line -- downward selection. */
return (1); if (py < sel->sy || py > sel->ey)
return (0);
} else if (sel->sy > sel->ey) {
/* start line > end line -- upward selection. */
if (py > sel->sy || py < sel->ey)
return (0);
} else {
/* starting line == ending line. */
if (py != sel->sy)
return (0);
}
/*
* Need to include the selection start row, but not the cursor
* row, which means the selection changes depending on which
* one is on the left.
*/
if (sel->ex < sel->sx) {
/* Cursor (ex) is on the left. */
if (px <= sel->ex)
return (0);
if (px > sel->sx)
return (0);
} else {
/* Selection start (sx) is on the left. */
if (px < sel->sx)
return (0);
if (px >= sel->ex)
return (0);
}
} else {
/*
* Like emacs, keep the top-left-most character, and drop the
* bottom-right-most, regardless of copy direction.
*/
if (sel->sy < sel->ey) {
/* starting line < ending line -- downward selection. */
if (py < sel->sy || py > sel->ey)
return (0);
if ((py == sel->sy && px < sel->sx)
|| (py == sel->ey && px > sel->ex))
return (0);
} else if (sel->sy > sel->ey) {
/* starting line > ending line -- upward selection. */
if (py > sel->sy || py < sel->ey)
return (0);
if ((py == sel->sy && px >= sel->sx)
|| (py == sel->ey && px < sel->ex))
return (0);
} else {
/* starting line == ending line. */
if (py != sel->sy)
return (0);
if (sel->ex < sel->sx) {
/* cursor (ex) is on the left */
if (px > sel->sx || px < sel->ex)
return (0);
} else {
/* selection start (sx) is on the left */
if (px < sel->sx || px > sel->ex)
return (0);
}
}
} }
if ((py == sel->sy && px < sel->sx) || (py == sel->ey && px > sel->ex))
return (0);
return (1); return (1);
} }

1
tmux.1
View File

@ -564,6 +564,7 @@ The following keys are supported as appropriate for the mode:
.It Li "Previous word" Ta "b" Ta "M-b" .It Li "Previous word" Ta "b" Ta "M-b"
.It Li "Previous space" Ta "B" Ta "" .It Li "Previous space" Ta "B" Ta ""
.It Li "Quit mode" Ta "q" Ta "Escape" .It Li "Quit mode" Ta "q" Ta "Escape"
.It Li "Rectangle toggle" Ta "v" Ta "R"
.It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down" .It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down"
.It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up" .It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up"
.It Li "Search again" Ta "n" Ta "n" .It Li "Search again" Ta "n" Ta "n"

6
tmux.h
View File

@ -469,6 +469,7 @@ enum mode_key_cmd {
MODEKEYCOPY_PREVIOUSPAGE, MODEKEYCOPY_PREVIOUSPAGE,
MODEKEYCOPY_PREVIOUSSPACE, MODEKEYCOPY_PREVIOUSSPACE,
MODEKEYCOPY_PREVIOUSWORD, MODEKEYCOPY_PREVIOUSWORD,
MODEKEYCOPY_RECTANGLETOGGLE,
MODEKEYCOPY_RIGHT, MODEKEYCOPY_RIGHT,
MODEKEYCOPY_SCROLLDOWN, MODEKEYCOPY_SCROLLDOWN,
MODEKEYCOPY_SCROLLUP, MODEKEYCOPY_SCROLLUP,
@ -674,6 +675,7 @@ SLIST_HEAD(joblist, job);
/* Screen selection. */ /* Screen selection. */
struct screen_sel { struct screen_sel {
int flag; int flag;
int rectflag;
u_int sx; u_int sx;
u_int sy; u_int sy;
@ -1774,8 +1776,8 @@ void screen_free(struct screen *);
void screen_reset_tabs(struct screen *); void screen_reset_tabs(struct screen *);
void screen_set_title(struct screen *, const char *); void screen_set_title(struct screen *, const char *);
void screen_resize(struct screen *, u_int, u_int); void screen_resize(struct screen *, u_int, u_int);
void screen_set_selection( void screen_set_selection(struct screen *,
struct screen *, u_int, u_int, u_int, u_int, struct grid_cell *); u_int, u_int, u_int, u_int, u_int, struct grid_cell *);
void screen_clear_selection(struct screen *); void screen_clear_selection(struct screen *);
int screen_check_selection(struct screen *, u_int, u_int); int screen_check_selection(struct screen *, u_int, u_int);

View File

@ -68,6 +68,7 @@ void window_copy_cursor_next_word_end(struct window_pane *, const char *);
void window_copy_cursor_previous_word(struct window_pane *, const char *); void window_copy_cursor_previous_word(struct window_pane *, const char *);
void window_copy_scroll_up(struct window_pane *, u_int); void window_copy_scroll_up(struct window_pane *, u_int);
void window_copy_scroll_down(struct window_pane *, u_int); void window_copy_scroll_down(struct window_pane *, u_int);
void window_copy_rectangle_toggle(struct window_pane *);
const struct window_mode window_copy_mode = { const struct window_mode window_copy_mode = {
window_copy_init, window_copy_init,
@ -95,6 +96,8 @@ struct window_copy_mode_data {
u_int selx; u_int selx;
u_int sely; u_int sely;
u_int rectflag; /* are we in rectangle copy mode? */
u_int cx; u_int cx;
u_int cy; u_int cy;
@ -126,6 +129,8 @@ window_copy_init(struct window_pane *wp)
data->lastcx = 0; data->lastcx = 0;
data->lastsx = 0; data->lastsx = 0;
data->rectflag = 0;
data->inputtype = WINDOW_COPY_OFF; data->inputtype = WINDOW_COPY_OFF;
data->inputprompt = NULL; data->inputprompt = NULL;
data->inputstr = xstrdup(""); data->inputstr = xstrdup("");
@ -379,6 +384,9 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
data->inputprompt = "Goto Line"; data->inputprompt = "Goto Line";
*data->inputstr = '\0'; *data->inputstr = '\0';
goto input_on; goto input_on;
case MODEKEYCOPY_RECTANGLETOGGLE:
window_copy_rectangle_toggle(wp);
return;
default: default:
break; break;
} }
@ -823,7 +831,7 @@ window_copy_update_selection(struct window_pane *wp)
struct screen *s = &data->screen; struct screen *s = &data->screen;
struct options *oo = &wp->window->options; struct options *oo = &wp->window->options;
struct grid_cell gc; struct grid_cell gc;
u_int sx, sy, ty; u_int sx, sy, ty, cy;
if (!s->sel.flag) if (!s->sel.flag)
return (0); return (0);
@ -841,17 +849,33 @@ window_copy_update_selection(struct window_pane *wp)
sx = data->selx; sx = data->selx;
sy = data->sely; sy = data->sely;
if (sy < ty) { /* above screen */ if (sy < ty) { /* above screen */
sx = 0; if (!data->rectflag)
sx = 0;
sy = 0; sy = 0;
} else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
sx = screen_size_x(s) - 1; if (!data->rectflag)
sx = screen_size_x(s) - 1;
sy = screen_size_y(s) - 1; sy = screen_size_y(s) - 1;
} else } else
sy -= ty; sy -= ty;
sy = screen_hsize(s) + sy; sy = screen_hsize(s) + sy;
screen_set_selection( screen_set_selection(s,
s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc); sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
if (data->rectflag) {
/*
* Can't rely on the caller to redraw the right lines for
* rectangle selection - find the highest line and the number
* of lines, and redraw just past that in both directions
*/
cy = data->cy;
if (sy < cy)
window_copy_redraw_lines(wp, sy, cy - sy + 1);
else
window_copy_redraw_lines(wp, cy, sy - cy + 1);
}
return (1); return (1);
} }
@ -861,8 +885,9 @@ window_copy_copy_selection(struct window_pane *wp, struct client *c)
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen; struct screen *s = &data->screen;
char *buf; char *buf;
size_t off; size_t off;
u_int i, xx, yy, sx, sy, ex, ey, limit; u_int i, xx, yy, sx, sy, ex, ey, limit;
u_int firstsx, lastex, restex, restsx;
if (!s->sel.flag) if (!s->sel.flag)
return; return;
@ -893,17 +918,53 @@ window_copy_copy_selection(struct window_pane *wp, struct client *c)
if (ex > xx) if (ex > xx)
ex = xx; ex = xx;
/*
* Deal with rectangle-copy if necessary; four situations: start of
* first line (firstsx), end of last line (lastex), start (restsx) and
* end (restex) of all other lines.
*/
xx = screen_size_x(s);
if (data->rectflag) {
/*
* Need to ignore the column with the cursor in it, which for
* rectangular copy means knowing which side the cursor is on.
*/
if (data->selx < data->cx) {
/* Selection start is on the left. */
lastex = data->cx;
restex = data->cx;
firstsx = data->selx;
restsx = data->selx;
} else {
/* Cursor is on the left. */
lastex = data->selx + 1;
restex = data->selx + 1;
firstsx = data->cx + 1;
restsx = data->cx + 1;
}
} else {
/*
* Like emacs, keep the top-left-most character, and drop the
* bottom-right-most, regardless of copy direction.
*/
lastex = ex;
restex = xx;
firstsx = sx;
restsx = 0;
}
/* Copy the lines. */ /* Copy the lines. */
if (sy == ey) if (sy == ey)
window_copy_copy_line(wp, &buf, &off, sy, sx, ex); window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
else { else {
xx = screen_size_x(s); window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
if (ey - sy > 1) { if (ey - sy > 1) {
for (i = sy + 1; i < ey; i++) for (i = sy + 1; i < ey; i++) {
window_copy_copy_line(wp, &buf, &off, i, 0, xx); window_copy_copy_line(
wp, &buf, &off, i, restsx, restex);
}
} }
window_copy_copy_line(wp, &buf, &off, ey, 0, ex); window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
} }
/* Don't bother if no data. */ /* Don't bother if no data. */
@ -1288,7 +1349,6 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny)
if (ny == 0) if (ny == 0)
return; return;
data->oy -= ny; data->oy -= ny;
window_copy_update_selection(wp);
screen_write_start(&ctx, wp, NULL); screen_write_start(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
@ -1299,9 +1359,12 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny)
window_copy_write_line(wp, &ctx, 1); window_copy_write_line(wp, &ctx, 1);
if (screen_size_y(s) > 3) if (screen_size_y(s) > 3)
window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
if (s->sel.flag && screen_size_y(s) > ny) if (s->sel.flag && screen_size_y(s) > ny) {
window_copy_update_selection(wp);
window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
}
screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_cursormove(&ctx, data->cx, data->cy);
window_copy_update_selection(wp);
screen_write_stop(&ctx); screen_write_stop(&ctx);
} }
@ -1320,16 +1383,29 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny)
if (ny == 0) if (ny == 0)
return; return;
data->oy += ny; data->oy += ny;
window_copy_update_selection(wp);
screen_write_start(&ctx, wp, NULL); screen_write_start(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
screen_write_insertline(&ctx, ny); screen_write_insertline(&ctx, ny);
window_copy_write_lines(wp, &ctx, 0, ny); window_copy_write_lines(wp, &ctx, 0, ny);
if (s->sel.flag && screen_size_y(s) > ny) if (s->sel.flag && screen_size_y(s) > ny) {
window_copy_update_selection(wp);
window_copy_write_line(wp, &ctx, ny); window_copy_write_line(wp, &ctx, ny);
else if (ny == 1) /* nuke position */ } else if (ny == 1) /* nuke position */
window_copy_write_line(wp, &ctx, 1); window_copy_write_line(wp, &ctx, 1);
screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_cursormove(&ctx, data->cx, data->cy);
window_copy_update_selection(wp);
screen_write_stop(&ctx); screen_write_stop(&ctx);
} }
void
window_copy_rectangle_toggle(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
data->rectflag = !data->rectflag;
window_copy_update_selection(wp);
window_copy_redraw_screen(wp);
}