Merge branch 'master' of ssh://git.code.sf.net/p/tmux/tmux-code

pull/1/head
Nicholas Marriott 2014-06-21 19:48:03 +01:00
commit f8481f93c5
15 changed files with 220 additions and 149 deletions

3
FAQ
View File

@ -95,9 +95,6 @@ aware of are (bearing in mind I haven't used screen for a few years now):
- screen has builtin serial and telnet support; this is bloat and is unlikely - screen has builtin serial and telnet support; this is bloat and is unlikely
to be added to tmux. to be added to tmux.
- screen has support for updating utmp. Nobody has really come up with a clean,
portable way to do this without making tmux setuid or setgid yet.
- Environment handling is different. - Environment handling is different.
- tmux tends to be more demanding on the terminal so tends to show up terminal - tmux tends to be more demanding on the terminal so tends to show up terminal

View File

@ -29,12 +29,12 @@
enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *);
enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int);
const struct cmd_entry cmd_bind_key_entry = { const struct cmd_entry cmd_bind_key_entry = {
"bind-key", "bind", "bind-key", "bind",
"cnrt:", 1, -1, "cnrt:", 1, -1,
"[-cnr] [-t key-table] key command [arguments]", "[-cnr] [-t mode-table] key command [arguments]",
0, 0,
NULL, NULL,
cmd_bind_key_exec cmd_bind_key_exec
@ -67,7 +67,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
} }
if (args_has(args, 't')) if (args_has(args, 't'))
return (cmd_bind_key_table(self, cmdq, key)); return (cmd_bind_key_mode_table(self, cmdq, key));
cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
&cause); &cause);
@ -84,7 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
} }
enum cmd_retval enum cmd_retval
cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key)
{ {
struct args *args = self->args; struct args *args = self->args;
const char *tablename; const char *tablename;

View File

@ -94,6 +94,7 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq,
int n, with_codes, escape_c0, join_lines; int n, with_codes, escape_c0, join_lines;
u_int i, sx, top, bottom, tmp; u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line; char *cause, *buf, *line;
const char *Sflag, *Eflag;
size_t linelen; size_t linelen;
sx = screen_size_x(&wp->base); sx = screen_size_x(&wp->base);
@ -109,27 +110,37 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq,
} else } else
gd = wp->base.grid; gd = wp->base.grid;
n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); Sflag = args_get(args, 'S');
if (cause != NULL) { if (Sflag != NULL && strcmp(Sflag, "-") == 0)
top = gd->hsize;
free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize)
top = 0; top = 0;
else else {
top = gd->hsize + n; n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause);
if (top > gd->hsize + gd->sy - 1) if (cause != NULL) {
top = gd->hsize + gd->sy - 1; top = gd->hsize;
free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize)
top = 0;
else
top = gd->hsize + n;
if (top > gd->hsize + gd->sy - 1)
top = gd->hsize + gd->sy - 1;
}
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); Eflag = args_get(args, 'E');
if (cause != NULL) { if (Eflag != NULL && strcmp(Eflag, "-") == 0)
bottom = gd->hsize + gd->sy - 1;
free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize)
bottom = 0;
else
bottom = gd->hsize + n;
if (bottom > gd->hsize + gd->sy - 1)
bottom = gd->hsize + gd->sy - 1; bottom = gd->hsize + gd->sy - 1;
else {
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause);
if (cause != NULL) {
bottom = gd->hsize + gd->sy - 1;
free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize)
bottom = 0;
else
bottom = gd->hsize + n;
if (bottom > gd->hsize + gd->sy - 1)
bottom = gd->hsize + gd->sy - 1;
}
if (bottom < top) { if (bottom < top) {
tmp = bottom; tmp = bottom;

View File

@ -27,12 +27,12 @@
*/ */
enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *);
enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int);
const struct cmd_entry cmd_unbind_key_entry = { const struct cmd_entry cmd_unbind_key_entry = {
"unbind-key", "unbind", "unbind-key", "unbind",
"acnt:", 0, 1, "acnt:", 0, 1,
"[-acn] [-t key-table] key", "[-acn] [-t mode-table] key",
0, 0,
NULL, NULL,
cmd_unbind_key_exec cmd_unbind_key_exec
@ -64,7 +64,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
} }
if (args_has(args, 't')) if (args_has(args, 't'))
return (cmd_unbind_key_table(self, cmdq, key)); return (cmd_unbind_key_mode_table(self, cmdq, key));
if (key == KEYC_NONE) { if (key == KEYC_NONE) {
while (!RB_EMPTY(&key_bindings)) { while (!RB_EMPTY(&key_bindings)) {
@ -81,7 +81,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
} }
enum cmd_retval enum cmd_retval
cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key)
{ {
struct args *args = self->args; struct args *args = self->args;
const char *tablename; const char *tablename;

View File

@ -552,6 +552,13 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_active", "%d", wp == wp->window->active);
format_add(ft, "pane_dead", "%d", wp->fd == -1); format_add(ft, "pane_dead", "%d", wp->fd == -1);
if (window_pane_visible(wp)) {
format_add(ft, "pane_left", "%u", wp->xoff);
format_add(ft, "pane_top", "%u", wp->yoff);
format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1);
format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
}
format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
format_add(ft, "pane_synchronized", "%d", format_add(ft, "pane_synchronized", "%d",
!!options_get_number(&wp->window->options, "synchronize-panes")); !!options_get_number(&wp->window->options, "synchronize-panes"));

104
input.c
View File

@ -52,6 +52,7 @@ int input_split(struct input_ctx *);
int input_get(struct input_ctx *, u_int, int, int); int input_get(struct input_ctx *, u_int, int, int);
void input_reply(struct input_ctx *, const char *, ...); void input_reply(struct input_ctx *, const char *, ...);
void input_set_state(struct window_pane *, const struct input_transition *); void input_set_state(struct window_pane *, const struct input_transition *);
void input_reset_cell(struct input_ctx *);
/* Transition entry/exit handlers. */ /* Transition entry/exit handlers. */
void input_clear(struct input_ctx *); void input_clear(struct input_ctx *);
@ -104,19 +105,23 @@ enum input_esc_type {
INPUT_ESC_NEL, INPUT_ESC_NEL,
INPUT_ESC_RI, INPUT_ESC_RI,
INPUT_ESC_RIS, INPUT_ESC_RIS,
INPUT_ESC_SCSOFF_G0, INPUT_ESC_SCSG0_OFF,
INPUT_ESC_SCSON_G0, INPUT_ESC_SCSG0_ON,
INPUT_ESC_SCSG1_OFF,
INPUT_ESC_SCSG1_ON,
}; };
/* Escape command table. */ /* Escape command table. */
const struct input_table_entry input_esc_table[] = { const struct input_table_entry input_esc_table[] = {
{ '0', "(", INPUT_ESC_SCSOFF_G0 }, { '0', "(", INPUT_ESC_SCSG0_ON },
{ '0', ")", INPUT_ESC_SCSG1_ON },
{ '7', "", INPUT_ESC_DECSC }, { '7', "", INPUT_ESC_DECSC },
{ '8', "", INPUT_ESC_DECRC }, { '8', "", INPUT_ESC_DECRC },
{ '8', "#", INPUT_ESC_DECALN }, { '8', "#", INPUT_ESC_DECALN },
{ '=', "", INPUT_ESC_DECKPAM }, { '=', "", INPUT_ESC_DECKPAM },
{ '>', "", INPUT_ESC_DECKPNM }, { '>', "", INPUT_ESC_DECKPNM },
{ 'B', "(", INPUT_ESC_SCSON_G0 }, { 'B', "(", INPUT_ESC_SCSG0_OFF },
{ 'B', ")", INPUT_ESC_SCSG1_OFF },
{ 'D', "", INPUT_ESC_IND }, { 'D', "", INPUT_ESC_IND },
{ 'E', "", INPUT_ESC_NEL }, { 'E', "", INPUT_ESC_NEL },
{ 'H', "", INPUT_ESC_HTS }, { 'H', "", INPUT_ESC_HTS },
@ -684,17 +689,26 @@ input_table_compare(const void *key, const void *value)
return (strcmp(ictx->interm_buf, entry->interm)); return (strcmp(ictx->interm_buf, entry->interm));
} }
/* Reset cell state to default. */
void
input_reset_cell(struct input_ctx *ictx)
{
memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell);
ictx->cell.set = 0;
ictx->cell.g0set = ictx->cell.g1set = 0;
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
ictx->old_cx = 0;
ictx->old_cy = 0;
}
/* Initialise input parser. */ /* Initialise input parser. */
void void
input_init(struct window_pane *wp) input_init(struct window_pane *wp)
{ {
struct input_ctx *ictx = &wp->ictx; struct input_ctx *ictx = &wp->ictx;
memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); input_reset_cell(ictx);
memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell);
ictx->old_cx = 0;
ictx->old_cy = 0;
*ictx->interm_buf = '\0'; *ictx->interm_buf = '\0';
ictx->interm_len = 0; ictx->interm_len = 0;
@ -903,8 +917,18 @@ input_ground(struct input_ctx *ictx)
int int
input_print(struct input_ctx *ictx) input_print(struct input_ctx *ictx)
{ {
grid_cell_one(&ictx->cell, ictx->ch); int set;
screen_write_cell(&ictx->ctx, &ictx->cell);
set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set;
if (set == 1)
ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
else
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
grid_cell_one(&ictx->cell.cell, ictx->ch);
screen_write_cell(&ictx->ctx, &ictx->cell.cell);
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
return (0); return (0);
} }
@ -1000,10 +1024,10 @@ input_c0_dispatch(struct input_ctx *ictx)
screen_write_carriagereturn(sctx); screen_write_carriagereturn(sctx);
goto count_c0; goto count_c0;
case '\016': /* SO */ case '\016': /* SO */
ictx->cell.attr |= GRID_ATTR_CHARSET; ictx->cell.set = 1;
break; break;
case '\017': /* SI */ case '\017': /* SI */
ictx->cell.attr &= ~GRID_ATTR_CHARSET; ictx->cell.set = 0;
break; break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
@ -1014,7 +1038,7 @@ input_c0_dispatch(struct input_ctx *ictx)
count_c0: count_c0:
trigger = options_get_number(&wp->window->options, "c0-change-trigger"); trigger = options_get_number(&wp->window->options, "c0-change-trigger");
if (++wp->changes == trigger) { if (trigger != 0 && ++wp->changes >= trigger) {
wp->flags |= PANE_DROP; wp->flags |= PANE_DROP;
window_pane_timer_start(wp); window_pane_timer_start(wp);
} }
@ -1043,11 +1067,7 @@ input_esc_dispatch(struct input_ctx *ictx)
switch (entry->type) { switch (entry->type) {
case INPUT_ESC_RIS: case INPUT_ESC_RIS:
memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); input_reset_cell(ictx);
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
ictx->old_cx = 0;
ictx->old_cy = 0;
screen_write_reset(sctx); screen_write_reset(sctx);
break; break;
case INPUT_ESC_IND: case INPUT_ESC_IND:
@ -1082,16 +1102,17 @@ input_esc_dispatch(struct input_ctx *ictx)
case INPUT_ESC_DECALN: case INPUT_ESC_DECALN:
screen_write_alignmenttest(sctx); screen_write_alignmenttest(sctx);
break; break;
case INPUT_ESC_SCSON_G0: case INPUT_ESC_SCSG0_ON:
/* ictx->cell.g0set = 1;
* Not really supported, but fake it up enough for those that
* use it to switch character sets (by redefining G0 to
* graphics set, rather than switching to G1).
*/
ictx->cell.attr &= ~GRID_ATTR_CHARSET;
break; break;
case INPUT_ESC_SCSOFF_G0: case INPUT_ESC_SCSG0_OFF:
ictx->cell.attr |= GRID_ATTR_CHARSET; ictx->cell.g0set = 0;
break;
case INPUT_ESC_SCSG1_ON:
ictx->cell.g1set = 1;
break;
case INPUT_ESC_SCSG1_OFF:
ictx->cell.g1set = 0;
break; break;
} }
@ -1332,7 +1353,8 @@ input_csi_dispatch_rm(struct input_ctx *ictx)
void void
input_csi_dispatch_rm_private(struct input_ctx *ictx) input_csi_dispatch_rm_private(struct input_ctx *ictx)
{ {
u_int i; struct window_pane *wp = ictx->wp;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
@ -1366,10 +1388,10 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
break; break;
case 47: case 47:
case 1047: case 1047:
window_pane_alternate_off(ictx->wp, &ictx->cell, 0); window_pane_alternate_off(wp, &ictx->cell.cell, 0);
break; break;
case 1049: case 1049:
window_pane_alternate_off(ictx->wp, &ictx->cell, 1); window_pane_alternate_off(wp, &ictx->cell.cell, 1);
break; break;
case 2004: case 2004:
screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE);
@ -1403,7 +1425,8 @@ input_csi_dispatch_sm(struct input_ctx *ictx)
void void
input_csi_dispatch_sm_private(struct input_ctx *ictx) input_csi_dispatch_sm_private(struct input_ctx *ictx)
{ {
u_int i; struct window_pane *wp = ictx->wp;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) { for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) { switch (input_get(ictx, i, 0, -1)) {
@ -1436,7 +1459,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
if (ictx->ctx.s->mode & MODE_FOCUSON) if (ictx->ctx.s->mode & MODE_FOCUSON)
break; break;
screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); screen_write_mode_set(&ictx->ctx, MODE_FOCUSON);
ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */ wp->flags |= PANE_FOCUSPUSH; /* force update */
break; break;
case 1005: case 1005:
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8);
@ -1446,10 +1469,10 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
break; break;
case 47: case 47:
case 1047: case 1047:
window_pane_alternate_on(ictx->wp, &ictx->cell, 0); window_pane_alternate_on(wp, &ictx->cell.cell, 0);
break; break;
case 1049: case 1049:
window_pane_alternate_on(ictx->wp, &ictx->cell, 1); window_pane_alternate_on(wp, &ictx->cell.cell, 1);
break; break;
case 2004: case 2004:
screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE);
@ -1514,15 +1537,12 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
void void
input_csi_dispatch_sgr(struct input_ctx *ictx) input_csi_dispatch_sgr(struct input_ctx *ictx)
{ {
struct grid_cell *gc = &ictx->cell; struct grid_cell *gc = &ictx->cell.cell;
u_int i; u_int i;
int n, m; int n, m;
u_char attr;
if (ictx->param_list_len == 0) { if (ictx->param_list_len == 0) {
attr = gc->attr;
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
gc->attr |= (attr & GRID_ATTR_CHARSET);
return; return;
} }
@ -1560,9 +1580,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
switch (n) { switch (n) {
case 0: case 0:
case 10: case 10:
attr = gc->attr;
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
gc->attr |= (attr & GRID_ATTR_CHARSET);
break; break;
case 1: case 1:
gc->attr |= GRID_ATTR_BRIGHT; gc->attr |= GRID_ATTR_BRIGHT;
@ -1806,8 +1824,8 @@ input_utf8_close(struct input_ctx *ictx)
utf8_append(&ictx->utf8data, ictx->ch); utf8_append(&ictx->utf8data, ictx->ch);
grid_cell_set(&ictx->cell, &ictx->utf8data); grid_cell_set(&ictx->cell.cell, &ictx->utf8data);
screen_write_cell(&ictx->ctx, &ictx->cell); screen_write_cell(&ictx->ctx, &ictx->cell.cell);
return (0); return (0);
} }

View File

@ -27,7 +27,6 @@
RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
struct key_bindings key_bindings; struct key_bindings key_bindings;
struct key_bindings dead_key_bindings;
int int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
@ -78,20 +77,8 @@ key_bindings_remove(int key)
if ((bd = key_bindings_lookup(key)) == NULL) if ((bd = key_bindings_lookup(key)) == NULL)
return; return;
RB_REMOVE(key_bindings, &key_bindings, bd); RB_REMOVE(key_bindings, &key_bindings, bd);
RB_INSERT(key_bindings, &dead_key_bindings, bd); cmd_list_free(bd->cmdlist);
} free(bd);
void
key_bindings_clean(void)
{
struct key_binding *bd;
while (!RB_EMPTY(&dead_key_bindings)) {
bd = RB_ROOT(&dead_key_bindings);
RB_REMOVE(key_bindings, &dead_key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
}
} }
void void

11
paste.c
View File

@ -175,7 +175,7 @@ paste_add(char *data, size_t size)
int int
paste_rename(const char *oldname, const char *newname, char **cause) paste_rename(const char *oldname, const char *newname, char **cause)
{ {
struct paste_buffer *pb; struct paste_buffer *pb, *pb_new;
if (cause != NULL) if (cause != NULL)
*cause = NULL; *cause = NULL;
@ -194,7 +194,14 @@ paste_rename(const char *oldname, const char *newname, char **cause)
pb = paste_get_name(oldname); pb = paste_get_name(oldname);
if (pb == NULL) { if (pb == NULL) {
if (cause != NULL) if (cause != NULL)
xasprintf(cause, "no buffer %s", oldname); xasprintf(cause, "no buffer %s", oldname);
return (-1);
}
pb_new = paste_get_name(newname);
if (pb_new != NULL) {
if (cause != NULL)
xasprintf(cause, "buffer %s already exists", newname);
return (-1); return (-1);
} }

View File

@ -209,7 +209,6 @@ server_loop(void)
server_window_loop(); server_window_loop();
server_client_loop(); server_client_loop();
key_bindings_clean();
server_clean_dead(); server_clean_dead();
} }
} }

20
tmux.1
View File

@ -1152,6 +1152,12 @@ and
.Fl E .Fl E
specify the starting and ending line numbers, zero is the first line of the specify the starting and ending line numbers, zero is the first line of the
visible pane and negative numbers are lines in the history. visible pane and negative numbers are lines in the history.
.Ql -
to
.Fl S
is the start of the history and to
.Fl E
the end of the visible pane.
The default is to capture only the visible contents of the pane. The default is to capture only the visible contents of the pane.
.It Xo .It Xo
.Ic choose-client .Ic choose-client
@ -1865,7 +1871,7 @@ Commands related to key bindings are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo Ic bind-key .It Xo Ic bind-key
.Op Fl cnr .Op Fl cnr
.Op Fl t Ar key-table .Op Fl t Ar mode-table
.Ar key Ar command Op Ar arguments .Ar key Ar command Op Ar arguments
.Xc .Xc
.D1 (alias: Ic bind ) .D1 (alias: Ic bind )
@ -1894,7 +1900,7 @@ If
is present, is present,
.Ar key .Ar key
is bound in is bound in
.Ar key-table : .Ar mode-table :
the binding for command mode with the binding for command mode with
.Fl c .Fl c
or for normal mode without. or for normal mode without.
@ -1952,7 +1958,7 @@ Send the prefix key, or with
the secondary prefix key, to a window as if it was pressed. the secondary prefix key, to a window as if it was pressed.
.It Xo Ic unbind-key .It Xo Ic unbind-key
.Op Fl acn .Op Fl acn
.Op Fl t Ar key-table .Op Fl t Ar mode-table
.Ar key .Ar key
.Xc .Xc
.D1 (alias: Ic unbind ) .D1 (alias: Ic unbind )
@ -1974,7 +1980,7 @@ If
is present, is present,
.Ar key .Ar key
in in
.Ar key-table .Ar mode-table
is unbound: the binding for command mode with is unbound: the binding for command mode with
.Fl c .Fl c
or for normal mode without. or for normal mode without.
@ -3079,18 +3085,22 @@ The following variables are available, where appropriate:
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag"
.It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_active" Ta "" Ta "1 if active pane"
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
.It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_command" Ta "" Ta "Current command if available"
.It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead" Ta "" Ta "1 if pane is dead"
.It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_height" Ta "" Ta "Height of pane"
.It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_id" Ta "#D" Ta "Unique pane ID"
.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" .It Li "pane_in_mode" Ta "" Ta "If pane is in a mode"
.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized"
.It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_index" Ta "#P" Ta "Index of pane"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane"
.It Li "pane_right" Ta "" Ta "Right of pane"
.It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_start_command" Ta "" Ta "Command pane started with"
.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_tabs" Ta "" Ta "Pane tab positions"
.It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_title" Ta "#T" Ta "Title of pane"
.It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pane_width" Ta "" Ta "Width of pane"
.It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" .It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane"

15
tmux.h
View File

@ -800,14 +800,22 @@ struct screen_write_ctx {
#define screen_hsize(s) ((s)->grid->hsize) #define screen_hsize(s) ((s)->grid->hsize)
#define screen_hlimit(s) ((s)->grid->hlimit) #define screen_hlimit(s) ((s)->grid->hlimit)
/* Input parser cell. */
struct input_cell {
struct grid_cell cell;
int set;
int g0set; /* 1 if ACS */
int g1set; /* 1 if ACS */
};
/* Input parser context. */ /* Input parser context. */
struct input_ctx { struct input_ctx {
struct window_pane *wp; struct window_pane *wp;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct grid_cell cell; struct input_cell cell;
struct grid_cell old_cell; struct input_cell old_cell;
u_int old_cx; u_int old_cx;
u_int old_cy; u_int old_cy;
@ -819,7 +827,7 @@ struct input_ctx {
#define INPUT_BUF_START 32 #define INPUT_BUF_START 32
#define INPUT_BUF_LIMIT 1048576 #define INPUT_BUF_LIMIT 1048576
u_char* input_buf; u_char *input_buf;
size_t input_len; size_t input_len;
size_t input_space; size_t input_space;
@ -1886,7 +1894,6 @@ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
struct key_binding *key_bindings_lookup(int); struct key_binding *key_bindings_lookup(int);
void key_bindings_add(int, int, struct cmd_list *); void key_bindings_add(int, int, struct cmd_list *);
void key_bindings_remove(int); void key_bindings_remove(int);
void key_bindings_clean(void);
void key_bindings_init(void); void key_bindings_init(void);
void key_bindings_dispatch(struct key_binding *, struct client *); void key_bindings_dispatch(struct key_binding *, struct client *);

View File

@ -475,6 +475,8 @@ tty_keys_next(struct tty *tty)
goto complete_key; goto complete_key;
case -1: /* no, or not valid */ case -1: /* no, or not valid */
break; break;
case -2: /* yes, but we don't care. */
goto discard_key;
case 1: /* partial */ case 1: /* partial */
goto partial_key; goto partial_key;
} }
@ -586,6 +588,14 @@ complete_key:
server_client_handle_key(tty->client, key); server_client_handle_key(tty->client, key);
return (1); return (1);
discard_key:
log_debug("discard key %.*s %#x", (int) size, buf, key);
/* Remove data from buffer. */
evbuffer_drain(tty->event->input, size);
return (1);
} }
/* Key timer callback. */ /* Key timer callback. */
@ -730,6 +740,15 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
sgr = 1; sgr = 1;
sgr_rel = (c == 'm'); sgr_rel = (c == 'm');
/*
* Some terminals (like PuTTY 0.63) mistakenly send
* button-release events for scroll-wheel button-press event.
* Discard it before it reaches any program running inside
* tmux.
*/
if (sgr_rel && (sgr_b & 64))
return (-2);
/* Figure out what b would be in old format. */ /* Figure out what b would be in old format. */
b = sgr_b; b = sgr_b;
if (sgr_rel) if (sgr_rel)
@ -764,6 +783,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
else if (b == 1) else if (b == 1)
m->wheel = MOUSE_WHEEL_DOWN; m->wheel = MOUSE_WHEEL_DOWN;
m->event = MOUSE_EVENT_WHEEL; m->event = MOUSE_EVENT_WHEEL;
m->button = 3;
} else if ((b & MOUSE_MASK_BUTTONS) == 3) { } else if ((b & MOUSE_MASK_BUTTONS) == 3) {
if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y)
m->event = MOUSE_EVENT_CLICK; m->event = MOUSE_EVENT_CLICK;

View File

@ -1360,7 +1360,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
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; u_int i, xx, yy, sx, sy, ex, ey, ey_last;
u_int firstsx, lastex, restex, restsx; u_int firstsx, lastex, restex, restsx;
int keys; int keys;
@ -1389,9 +1389,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
} }
/* Trim ex to end of line. */ /* Trim ex to end of line. */
xx = window_copy_find_length(wp, ey); ey_last = window_copy_find_length(wp, ey);
if (ex > xx) if (ex > ey_last)
ex = xx; ex = ey_last;
/* /*
* Deal with rectangle-copy if necessary; four situations: start of * Deal with rectangle-copy if necessary; four situations: start of
@ -1442,17 +1442,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
} }
/* Copy the lines. */ /* Copy the lines. */
if (sy == ey) for (i = sy; i <= ey; i++) {
window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); window_copy_copy_line(wp, &buf, &off, i,
else { (i == sy ? firstsx : restsx),
window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); (i == ey ? lastex : restex));
if (ey - sy > 1) {
for (i = sy + 1; i < ey; i++) {
window_copy_copy_line(
wp, &buf, &off, i, restsx, restex);
}
}
window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
} }
/* Don't bother if no data. */ /* Don't bother if no data. */
@ -1460,7 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
free(buf); free(buf);
return (NULL); return (NULL);
} }
*len = off - 1; /* remove final \n */ if (keys == MODEKEY_EMACS || lastex <= ey_last)
off -= 1; /* remove final \n (unless at end in vi mode) */
*len = off;
return (buf); return (buf);
} }

View File

@ -891,7 +891,6 @@ window_pane_timer_callback(unused int fd, unused short events, void *data)
if (wp->changes_redraw++ == interval) { if (wp->changes_redraw++ == interval) {
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
wp->changes_redraw = 0; wp->changes_redraw = 0;
} }
if (trigger == 0 || wp->changes < trigger) { if (trigger == 0 || wp->changes < trigger) {

View File

@ -40,8 +40,8 @@
* We accept any but always output the latter (it comes first in the table). * We accept any but always output the latter (it comes first in the table).
*/ */
int xterm_keys_match(const char *, const char *, size_t); int xterm_keys_match(const char *, const char *, size_t, size_t *, u_int *);
int xterm_keys_modifiers(const char *, const char *, size_t); int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *);
struct xterm_keys_entry { struct xterm_keys_entry {
int key; int key;
@ -122,49 +122,62 @@ const struct xterm_keys_entry xterm_keys_table[] = {
* 0 for match, 1 if the end of the buffer is reached (need more data). * 0 for match, 1 if the end of the buffer is reached (need more data).
*/ */
int int
xterm_keys_match(const char *template, const char *buf, size_t len) xterm_keys_match(const char *template, const char *buf, size_t len,
size_t *size, u_int *modifiers)
{ {
size_t pos; size_t pos;
int retval;
if (len == 0) if (len == 0)
return (0); return (0);
pos = 0; pos = 0;
do { do {
if (*template == '_' && buf[pos] >= '1' && buf[pos] <= '8') if (*template == '_') {
retval = xterm_keys_modifiers(buf, len, &pos,
modifiers);
if (retval != 0)
return (retval);
continue; continue;
}
if (buf[pos] != *template) if (buf[pos] != *template)
return (-1); return (-1);
} while (*++template != '\0' && ++pos != len); pos++;
} while (*++template != '\0' && pos != len);
if (*template != '\0') /* partial */ if (*template != '\0') /* partial */
return (1); return (1);
*size = pos;
return (0); return (0);
} }
/* Find modifiers based on template. */ /* Find modifiers from buffer. */
int int
xterm_keys_modifiers(const char *template, const char *buf, size_t len) xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers)
{ {
size_t idx; u_int flags;
int param, modifiers;
idx = strcspn(template, "_"); if (len - *pos < 2)
if (idx >= len) return (1);
return (0);
param = buf[idx] - '1';
modifiers = 0; if (buf[*pos] < '0' || buf[*pos] > '9')
if (param & 1) return (-1);
modifiers |= KEYC_SHIFT; flags = buf[(*pos)++] - '0';
if (param & 2) if (buf[*pos] >= '0' && buf[*pos] <= '9')
modifiers |= KEYC_ESCAPE; flags = (flags * 10) + (buf[(*pos)++] - '0');
if (param & 4) flags -= 1;
modifiers |= KEYC_CTRL;
if (param & 8) *modifiers = 0;
modifiers |= KEYC_ESCAPE; if (flags & 1)
return (modifiers); *modifiers |= KEYC_SHIFT;
if (flags & 2)
*modifiers |= KEYC_ESCAPE;
if (flags & 4)
*modifiers |= KEYC_CTRL;
if (flags & 8)
*modifiers |= KEYC_ESCAPE;
return (0);
} }
/* /*
@ -175,19 +188,19 @@ int
xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) xterm_keys_find(const char *buf, size_t len, size_t *size, int *key)
{ {
const struct xterm_keys_entry *entry; const struct xterm_keys_entry *entry;
u_int i; u_int i, modifiers;
int matched;
for (i = 0; i < nitems(xterm_keys_table); i++) { for (i = 0; i < nitems(xterm_keys_table); i++) {
entry = &xterm_keys_table[i]; entry = &xterm_keys_table[i];
switch (xterm_keys_match(entry->template, buf, len)) {
case 0: matched = xterm_keys_match(entry->template, buf, len, size,
*size = strlen(entry->template); &modifiers);
*key = entry->key; if (matched == -1)
*key |= xterm_keys_modifiers(entry->template, buf, len); continue;
return (0); if (matched == 0)
case 1: *key = entry->key | modifiers;
return (1); return (matched);
}
} }
return (-1); return (-1);
} }