diff --git a/arguments.c b/arguments.c index 387790ac..f3152ddf 100644 --- a/arguments.c +++ b/arguments.c @@ -189,7 +189,7 @@ out: /* Parse flags argument. */ static int args_parse_flags(const struct args_parse *parse, struct args_value *values, - u_int count, char **cause, struct args *args, int *i) + u_int count, char **cause, struct args *args, u_int *i) { struct args_value *value; u_char flag; diff --git a/client.c b/client.c index 374c1146..9cd5ad85 100644 --- a/client.c +++ b/client.c @@ -245,9 +245,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, u_int ncaps = 0; struct args_value *values; - /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ - signal(SIGCHLD, SIG_IGN); - /* Set up the initial command. */ if (shell_command != NULL) { msg = MSG_SHELL; @@ -533,11 +530,22 @@ client_signal(int sig) { struct sigaction sigact; int status; + pid_t pid; log_debug("%s: %s", __func__, strsignal(sig)); - if (sig == SIGCHLD) - waitpid(WAIT_ANY, &status, WNOHANG); - else if (!client_attached) { + if (sig == SIGCHLD) { + for (;;) { + pid = waitpid(WAIT_ANY, &status, WNOHANG); + if (pid == 0) + break; + if (pid == -1) { + if (errno == ECHILD) + break; + log_debug("waitpid failed: %s", + strerror(errno)); + } + } + } else if (!client_attached) { if (sig == SIGTERM || sig == SIGHUP) proc_exit(client_proc); } else { diff --git a/cmd-find.c b/cmd-find.c index 154cadac..8a3499a1 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -582,7 +582,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) /* Try special characters. */ if (strcmp(pane, "!") == 0) { - fs->wp = fs->w->last; + fs->wp = TAILQ_FIRST(&fs->w->last_panes); if (fs->wp == NULL) return (-1); return (0); diff --git a/cmd-resize-window.c b/cmd-resize-window.c index ad739165..c420451c 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -53,8 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s; const char *errstr; char *cause; - u_int adjust, sx, sy; - int xpixel = -1, ypixel = -1; + u_int adjust, sx, sy, xpixel = 0, ypixel = 0; if (args_count(args) == 0) adjust = 1; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index ae21d4ce..135729f5 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -98,7 +98,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct options_entry *o; if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { - lastwp = w->last; + /* + * Check for no last pane found in case the other pane was + * spawned without being visited (for example split-window -d). + */ + lastwp = TAILQ_FIRST(&w->last_panes); if (lastwp == NULL && window_count_panes(w) == 2) { lastwp = TAILQ_PREV(w->active, window_panes, entry); if (lastwp == NULL) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 80c20c80..6931bd16 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -128,10 +128,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) window_set_active_pane(dst_w, src_wp, 1); } if (src_w != dst_w) { - if (src_w->last == src_wp) - src_w->last = NULL; - if (dst_w->last == dst_wp) - dst_w->last = NULL; + window_pane_stack_remove(&src_w->last_panes, src_wp); + window_pane_stack_remove(&dst_w->last_panes, dst_wp); colour_palette_from_option(&src_wp->palette, src_wp->options); colour_palette_from_option(&dst_wp->palette, dst_wp->options); } diff --git a/configure.ac b/configure.ac index 4d85624d..ca9b9473 100644 --- a/configure.ac +++ b/configure.ac @@ -315,7 +315,7 @@ fi if test "x$found_ncurses" = xno; then AC_SEARCH_LIBS( setupterm, - [tinfo ncurses ncursesw], + [tinfo terminfo ncurses ncursesw], found_ncurses=yes, found_ncurses=no ) @@ -461,7 +461,7 @@ AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes]) # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) - AC_LINK_IFELSE([AC_LANG_PROGRAM( +AC_LINK_IFELSE([AC_LANG_PROGRAM( [ #include #include diff --git a/format.c b/format.c index f69330b2..b20e90a8 100644 --- a/format.c +++ b/format.c @@ -1902,7 +1902,7 @@ static void * format_cb_pane_last(struct format_tree *ft) { if (ft->wp != NULL) { - if (ft->wp == ft->wp->window->last) + if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes)) return (xstrdup("1")); return (xstrdup("0")); } @@ -3664,7 +3664,9 @@ format_skip(const char *s, const char *end) for (; *s != '\0'; s++) { if (*s == '#' && s[1] == '{') brackets++; - if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { + if (*s == '#' && + s[1] != '\0' && + strchr(",#{}:", s[1]) != NULL) { s++; continue; } @@ -3813,7 +3815,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, argc = 0; /* Single argument with no wrapper character. */ - if (!ispunct(cp[1]) || cp[1] == '-') { + if (!ispunct((u_char)cp[1]) || cp[1] == '-') { end = format_skip(cp + 1, ":;"); if (end == NULL) break; diff --git a/grid.c b/grid.c index 3afbfb6a..edada819 100644 --- a/grid.c +++ b/grid.c @@ -37,7 +37,7 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 + { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0 }; /* @@ -45,12 +45,12 @@ const struct grid_cell grid_default_cell = { * appears in the grid - because of this, they are always extended cells. */ static const struct grid_cell grid_padding_cell = { - { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0, 0 + { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0 }; /* Cleared grid cell data. */ static const struct grid_cell grid_cleared_cell = { - { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0, 0 + { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0 }; static const struct grid_cell_entry grid_cleared_entry = { { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED @@ -528,7 +528,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) gc->bg = gce->data.bg; if (gce->flags & GRID_FLAG_BG256) gc->bg |= COLOUR_FLAG_256; - gc->us = 0; + gc->us = 8; utf8_set(&gc->data, gce->data.data); gc->link = 0; } @@ -956,7 +956,7 @@ grid_string_cells_code(const struct grid_cell *lastgc, for (i = 0; i < nitems(attrs); i++) { if (((~attr & attrs[i].mask) && (lastattr & attrs[i].mask)) || - (lastgc->us != 0 && gc->us == 0)) { + (lastgc->us != 8 && gc->us == 8)) { s[n++] = 0; lastattr &= GRID_ATTR_CHARSET; break; @@ -1044,7 +1044,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, const char *data; char *buf, code[8192]; size_t len, off, size, codelen; - u_int xx, has_link = 0, end; + u_int xx, end; + int has_link = 0; const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { diff --git a/hyperlinks.c b/hyperlinks.c index cde500c0..70c2f4e3 100644 --- a/hyperlinks.c +++ b/hyperlinks.c @@ -42,7 +42,7 @@ #define MAX_HYPERLINKS 5000 -static uint64_t hyperlinks_next_external_id = 1; +static long long hyperlinks_next_external_id = 1; static u_int global_hyperlinks_count; struct hyperlinks_uri { diff --git a/input.c b/input.c index d32dfd67..324e58f5 100644 --- a/input.c +++ b/input.c @@ -144,6 +144,7 @@ static void input_osc_104(struct input_ctx *, const char *); static void input_osc_110(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *); static void input_osc_112(struct input_ctx *, const char *); +static void input_osc_133(struct input_ctx *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -2069,7 +2070,7 @@ static void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell.cell; - u_int i; + u_int i, link; int n; if (ictx->param_list_len == 0) { @@ -2101,7 +2102,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) switch (n) { case 0: + link = gc->link; memcpy(gc, &grid_default_cell, sizeof *gc); + gc->link = link; break; case 1: gc->attr |= GRID_ATTR_BRIGHT; @@ -2187,7 +2190,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr &= ~GRID_ATTR_OVERLINE; break; case 59: - gc->us = 0; + gc->us = 8; break; case 90: case 91: @@ -2363,6 +2366,9 @@ input_exit_osc(struct input_ctx *ictx) case 112: input_osc_112(ictx, p); break; + case 133: + input_osc_133(ictx, p); + break; default: log_debug("%s: unknown '%u'", __func__, option); break; @@ -2752,6 +2758,24 @@ input_osc_112(struct input_ctx *ictx, const char *p) screen_set_cursor_colour(ictx->ctx.s, -1); } +/* Handle the OSC 133 sequence. */ +static void +input_osc_133(struct input_ctx *ictx, const char *p) +{ + struct grid *gd = ictx->ctx.s->grid; + u_int line = ictx->ctx.s->cy + gd->hsize; + struct grid_line *gl; + + if (line > gd->hsize + gd->sy - 1) + return; + gl = grid_get_line(gd, line); + + switch (*p) { + case 'A': + gl->flags |= GRID_LINE_START_PROMPT; + break; + } +} /* Handle the OSC 52 sequence for setting the clipboard. */ static void @@ -2858,9 +2882,11 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, const char *end) { char *out = NULL; - size_t outlen = 0; + int outlen = 0; if (buf != NULL && len != 0) { + if (len >= ((size_t)INT_MAX * 3 / 4) - 1) + return; outlen = 4 * ((len + 2) / 3) + 1; out = xmalloc(outlen); if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { diff --git a/notify.c b/notify.c index 97919487..d42e2b9d 100644 --- a/notify.c +++ b/notify.c @@ -193,7 +193,7 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, ne->client = c; ne->session = s; ne->window = w; - ne->pane = (wp != NULL ? wp->id : -1); + ne->pane = (wp != NULL ? (int)wp->id : -1); ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL); ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); @@ -240,7 +240,7 @@ notify_hook(struct cmdq_item *item, const char *name) ne.client = cmdq_get_client(item); ne.session = target->s; ne.window = target->w; - ne.pane = (target->wp != NULL ? target->wp->id : -1); + ne.pane = (target->wp != NULL ? (int)target->wp->id : -1); ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); format_add(ne.formats, "hook", "%s", name); diff --git a/popup.c b/popup.c index dc37f002..5686ad17 100644 --- a/popup.c +++ b/popup.c @@ -787,6 +787,8 @@ popup_editor(struct client *c, const char *buf, size_t len, if (fd == -1) return (-1); f = fdopen(fd, "w"); + if (f == NULL) + return (-1); if (fwrite(buf, len, 1, f) != 1) { fclose(f); return (-1); diff --git a/regsub.c b/regsub.c index 4039b9be..61a9c324 100644 --- a/regsub.c +++ b/regsub.c @@ -24,7 +24,7 @@ #include "tmux.h" static void -regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) +regsub_copy(char **buf, ssize_t *len, const char *text, size_t start, size_t end) { size_t add = end - start; @@ -34,7 +34,7 @@ regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) } static void -regsub_expand(char **buf, size_t *len, const char *with, const char *text, +regsub_expand(char **buf, ssize_t *len, const char *with, const char *text, regmatch_t *m, u_int n) { const char *cp; diff --git a/screen-write.c b/screen-write.c index 91750d93..02261a32 100644 --- a/screen-write.c +++ b/screen-write.c @@ -326,7 +326,9 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_reset_tabs(s); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); - s->mode = MODE_CURSOR | MODE_WRAP; + s->mode = MODE_CURSOR|MODE_WRAP; + if (options_get_number(global_options, "extended-keys") == 2) + s->mode |= MODE_KEXTENDED; screen_write_clearscreen(ctx, 8); screen_write_set_cursor(ctx, 0, 0); diff --git a/screen.c b/screen.c index a51b17bd..a7c6195b 100644 --- a/screen.c +++ b/screen.c @@ -642,7 +642,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) * before copying back. */ if (s->saved_grid != NULL) - screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1); + screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0); /* * Restore the cursor position and cell. This happens even if not diff --git a/session.c b/session.c index 71435cdf..8432b6ae 100644 --- a/session.c +++ b/session.c @@ -737,9 +737,12 @@ session_renumber_windows(struct session *s) memcpy(&old_lastw, &s->lastw, sizeof old_lastw); TAILQ_INIT(&s->lastw); TAILQ_FOREACH(wl, &old_lastw, sentry) { + wl->flags &= ~WINLINK_VISITED; wl_new = winlink_find_by_window(&s->windows, wl->window); - if (wl_new != NULL) + if (wl_new != NULL) { TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); + wl_new->flags |= WINLINK_VISITED; + } } /* Set the current window. */ diff --git a/spawn.c b/spawn.c index 10604028..d321dba4 100644 --- a/spawn.c +++ b/spawn.c @@ -113,6 +113,7 @@ spawn_window(struct spawn_context *sc, char **cause) window_pane_resize(sc->wp0, w->sx, w->sy); layout_init(w, sc->wp0); + w->active = NULL; window_set_active_pane(w, sc->wp0, 0); } @@ -428,8 +429,8 @@ spawn_pane(struct spawn_context *sc, char **cause) _exit(1); /* Clean up file descriptors and signals and update the environment. */ - closefrom(STDERR_FILENO + 1); proc_clear_signals(server_proc, 1); + closefrom(STDERR_FILENO + 1); sigprocmask(SIG_SETMASK, &oldset, NULL); log_close(); environ_push(child); diff --git a/style.c b/style.c index 8407dc68..3d9d317d 100644 --- a/style.c +++ b/style.c @@ -77,6 +77,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) if (strcasecmp(tmp, "default") == 0) { sy->gc.fg = base->fg; sy->gc.bg = base->bg; + sy->gc.us = base->us; sy->gc.attr = base->attr; sy->gc.flags = base->flags; } else if (strcasecmp(tmp, "ignore") == 0) @@ -162,6 +163,13 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.bg = base->bg; } else goto error; + } else if (end > 3 && strncasecmp(tmp, "us=", 3) == 0) { + if ((value = colour_fromstring(tmp + 3)) == -1) + goto error; + if (value != 8) + sy->gc.us = value; + else + sy->gc.us = base->us; } else if (strcasecmp(tmp, "none") == 0) sy->gc.attr = 0; else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { @@ -258,6 +266,11 @@ style_tostring(struct style *sy) colour_tostring(gc->bg)); comma = ","; } + if (gc->us != 8) { + off += xsnprintf(s + off, sizeof s - off, "%sus=%s", comma, + colour_tostring(gc->us)); + comma = ","; + } if (gc->attr != 0) { xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); @@ -287,6 +300,8 @@ style_add(struct grid_cell *gc, struct options *oo, const char *name, gc->fg = sy->gc.fg; if (sy->gc.bg != 8) gc->bg = sy->gc.bg; + if (sy->gc.us != 8) + gc->us = sy->gc.us; gc->attr |= sy->gc.attr; if (ft0 != NULL) diff --git a/tmux.1 b/tmux.1 index 7bf2bc4d..7b53740c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1745,91 +1745,266 @@ Key tables may be viewed with the command. .Pp The following commands are supported in copy mode: -.Bl -column "CommandXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent -.It Sy "Command" Ta Sy "vi" Ta Sy "emacs" -.It Li "append-selection" Ta "" Ta "" -.It Li "append-selection-and-cancel" Ta "A" Ta "" -.It Li "back-to-indentation" Ta "^" Ta "M-m" -.It Li "begin-selection" Ta "Space" Ta "C-Space" -.It Li "bottom-line" Ta "L" Ta "" -.It Li "cancel" Ta "q" Ta "Escape" -.It Li "clear-selection" Ta "Escape" Ta "C-g" -.It Li "copy-end-of-line []" Ta "" Ta "" -.It Li "copy-end-of-line-and-cancel []" Ta "" Ta "" -.It Li "copy-pipe-end-of-line [] []" Ta "" Ta "" -.It Li "copy-pipe-end-of-line-and-cancel [] []" Ta "D" Ta "C-k" -.It Li "copy-line []" Ta "" Ta "" -.It Li "copy-line-and-cancel []" Ta "" Ta "" -.It Li "copy-pipe-line [] []" Ta "" Ta "" -.It Li "copy-pipe-line-and-cancel [] []" Ta "" Ta "" -.It Li "copy-pipe [] []" Ta "" Ta "" -.It Li "copy-pipe-no-clear [] []" Ta "" Ta "" -.It Li "copy-pipe-and-cancel [] []" Ta "" Ta "" -.It Li "copy-selection []" Ta "" Ta "" -.It Li "copy-selection-no-clear []" Ta "" Ta "" -.It Li "copy-selection-and-cancel []" Ta "Enter" Ta "M-w" -.It Li "cursor-down" Ta "j" Ta "Down" -.It Li "cursor-down-and-cancel" Ta "" Ta "" -.It Li "cursor-left" Ta "h" Ta "Left" -.It Li "cursor-right" Ta "l" Ta "Right" -.It Li "cursor-up" Ta "k" Ta "Up" -.It Li "end-of-line" Ta "$" Ta "C-e" -.It Li "goto-line " Ta ":" Ta "g" -.It Li "halfpage-down" Ta "C-d" Ta "M-Down" -.It Li "halfpage-down-and-cancel" Ta "" Ta "" -.It Li "halfpage-up" Ta "C-u" Ta "M-Up" -.It Li "history-bottom" Ta "G" Ta "M->" -.It Li "history-top" Ta "g" Ta "M-<" -.It Li "jump-again" Ta ";" Ta ";" -.It Li "jump-backward " Ta "F" Ta "F" -.It Li "jump-forward " Ta "f" Ta "f" -.It Li "jump-reverse" Ta "," Ta "," -.It Li "jump-to-backward " Ta "T" Ta "" -.It Li "jump-to-forward " Ta "t" Ta "" -.It Li "jump-to-mark" Ta "M-x" Ta "M-x" -.It Li "middle-line" Ta "M" Ta "M-r" -.It Li "next-matching-bracket" Ta "%" Ta "M-C-f" -.It Li "next-paragraph" Ta "}" Ta "M-}" -.It Li "next-space" Ta "W" Ta "" -.It Li "next-space-end" Ta "E" Ta "" -.It Li "next-word" Ta "w" Ta "" -.It Li "next-word-end" Ta "e" Ta "M-f" -.It Li "other-end" Ta "o" Ta "" -.It Li "page-down" Ta "C-f" Ta "PageDown" -.It Li "page-down-and-cancel" Ta "" Ta "" -.It Li "page-up" Ta "C-b" Ta "PageUp" -.It Li "pipe [] []" Ta "" Ta "" -.It Li "pipe-no-clear [] []" Ta "" Ta "" -.It Li "pipe-and-cancel [] []" Ta "" Ta "" -.It Li "previous-matching-bracket" Ta "" Ta "M-C-b" -.It Li "previous-paragraph" Ta "{" Ta "M-{" -.It Li "previous-space" Ta "B" Ta "" -.It Li "previous-word" Ta "b" Ta "M-b" -.It Li "rectangle-on" Ta "" Ta "" -.It Li "rectangle-off" Ta "" Ta "" -.It Li "rectangle-toggle" Ta "v" Ta "R" -.It Li "refresh-from-pane" Ta "r" Ta "r" -.It Li "scroll-down" Ta "C-e" Ta "C-Down" -.It Li "scroll-down-and-cancel" Ta "" Ta "" -.It Li "scroll-up" Ta "C-y" Ta "C-Up" -.It Li "search-again" Ta "n" Ta "n" -.It Li "search-backward " Ta "?" Ta "" -.It Li "search-backward-incremental " Ta "" Ta "C-r" -.It Li "search-backward-text " Ta "" Ta "" -.It Li "search-forward " Ta "/" Ta "" -.It Li "search-forward-incremental " Ta "" Ta "C-s" -.It Li "search-forward-text " Ta "" Ta "" -.It Li "scroll-bottom" Ta "" Ta "" -.It Li "scroll-middle" Ta "z" Ta "" -.It Li "scroll-top" Ta "" Ta "" -.It Li "search-reverse" Ta "N" Ta "N" -.It Li "select-line" Ta "V" Ta "" -.It Li "select-word" Ta "" Ta "" -.It Li "set-mark" Ta "X" Ta "X" -.It Li "start-of-line" Ta "0" Ta "C-a" -.It Li "stop-selection" Ta "" Ta "" -.It Li "toggle-position" Ta "P" Ta "P" -.It Li "top-line" Ta "H" Ta "M-R" +.Bl -tag -width Ds +.It Xo +.Ic append-selection +.Xc +Append the selection to the top paste buffer. +.It Xo +.Ic append-selection-and-cancel +(vi: A) +.Xc +Append the selection to the top paste buffer and exit copy mode. +.It Xo +.Ic back-to-indentation +(vi: ^) +(emacs: M-m) +.Xc +Move the cursor back to the indentation. +.It Xo +.Ic begin-selection +(vi: Space) +(emacs: C-Space) +.Xc +Begin selection. +.It Xo +.Ic bottom-line +(vi: L) +.Xc +Move to the bottom line. +.It Xo +.Ic cancel +(vi: q) +(emacs: Escape) +.Xc +Exit copy mode. +.It Xo +.Ic clear-selection +(vi: Escape) +(emacs: C-g) +.Xc +Clear the current selection. +.It Xo +.Ic copy-end-of-line [] +.Xc +Copy from the cursor position to the end of the line. +.Ar prefix +is used to name the new paste buffer. +.It Xo +.Ic copy-end-of-line-and-cancel [] +.Xc +Copy from the cursor position and exit copy mode. +.It Xo +.Ic copy-line [] +.Xc +Copy the entire line. +.It Xo +.Ic copy-line-and-cancel [] +.Xc +Copy the entire line and exit copy mode. +.It Xo +.Ic copy-selection [] +.Xc +Copies the current selection. +.It Xo +.Ic copy-selection-and-cancel [] +(vi: Enter) +(emacs: M-w) +.Xc +Copy the current selection and exit copy mode. +.It Xo +.Ic cursor-down +(vi: j) +(emacs: Down) +.Xc +Move the cursor down. +.It Xo +.Ic cursor-left +(vi: h) +(emacs: Left) +.Xc +Move the cursor left. +.It Xo +.Ic cursor-right +(vi: l) +(emacs: Right) +.Xc +Move the cursor right. +.It Xo +.Ic cursor-up +(vi: k) +(emacs: Up) +.Xc +Move the cursor up. +.It Xo +.Ic end-of-line +(vi: $) +(emacs: C-e) +.Xc +Move the cursor to the end of the line. +.It Xo +.Ic goto-line +(vi: :) +(emacs: g) +.Xc +Move the cursor to a specific line. +.It Xo +.Ic history-bottom +(vi: G) +(emacs: M->) +.Xc +Scroll to the bottom of the history. +.It Xo +.Ic history-top +(vi: g) +(emacs: M-<) +.Xc +Scroll to the top of the history. +.It Xo +.Ic jump-again +(vi: ;) +(emacs: ;) +.Xc +Repeat the last jump. +.It Xo +.Ic jump-backward +(vi: F) +(emacs: F) +.Xc +Jump backwards to the specified text. +.It Xo +.Ic jump-forward +(vi: f) +(emacs: f) +.Xc +Jump forward to the specified text. +.It Xo +.Ic jump-to-mark +(vi: M-x) +(emacs: M-x) +.Xc +Jump to the last mark. +.It Xo +.Ic middle-line +(vi: M) +(emacs: M-r) +.Xc +Move to the middle line. +.It Xo +.Ic next-matching-bracket +(vi: %) +(emacs: M-C-f) +.Xc +Move to the next matching bracket. +.It Xo +.Ic next-paragraph +(vi: }) +(emacs: M-}) +.Xc +Move to the next paragraph. +.It Xo +.Ic next-prompt +.Xc +Move to the next prompt. +.It Xo +.Ic next-word +(vi: w) +.Xc +Move to the next word. +.It Xo +.Ic page-down +(vi: C-f) +(emacs: PageDown) +.Xc +Scroll down by one page. +.It Xo +.Ic page-up +(vi: C-b) +(emacs: PageUp) +.Xc +Scroll up by one page. +.It Xo +.Ic previous-matching-bracket +(emacs: M-C-b) +.Xc +Move to the previous matching bracket. +.It Xo +.Ic previous-paragraph +(vi: {) +(emacs: M-{) +.Xc +Move to the previous paragraph. +.It Xo +.Ic previous-prompt +.Xc +Move to the previous prompt. +.It Xo +.Ic previous-word +(vi: b) +(emacs: M-b) +.Xc +Move to the previous word. +.It Xo +.Ic rectangle-toggle +(vi: v) +(emacs: R) +.Xc +Toggle rectangle selection mode. +.It Xo +.Ic refresh-from-pane +(vi: r) +(emacs: r) +.Xc +Refresh the content from the pane. +.It Xo +.Ic search-again +(vi: n) +(emacs: n) +.Xc +Repeat the last search. +.It Xo +.Ic search-backward +(vi: ?) +.Xc +Search backwards for the specified text. +.It Xo +.Ic search-forward +(vi: /) +.Xc +Search forward for the specified text. +.It Xo +.Ic select-line +(vi: V) +.Xc +Select the current line. +.It Xo +.Ic select-word +.Xc +Select the current word. +.It Xo +.Ic start-of-line +(vi: 0) +(emacs: C-a) +.Xc +Move the cursor to the start of the line. +.It Xo +.Ic top-line +(vi: H) +(emacs: M-R) +.Xc +Move to the top line. +.It Xo +.Ic next-prompt +(vi: C-n) +(emacs: C-n) +.Xc +Move to the next prompt. +.It Xo +.Ic previous-prompt +(vi: C-p) +(emacs: C-p) +.Xc +Move to the previous prompt. .El .Pp The search commands come in several varieties: @@ -1852,6 +2027,16 @@ repeats the last search and does the same but reverses the direction (forward becomes backward and backward becomes forward). .Pp +The +.Ql next-prompt +and +.Ql previous-prompt +move between shell prompts, but require the shell to emit an escape sequence +(\e033]133;A\e033\e\e) to tell +.Nm +where the prompts are located; if the shell does not do this, these commands +will do nothing. +.Pp Copy commands may take an optional buffer prefix argument which is used to generate the buffer name (the default is .Ql buffer @@ -5387,6 +5572,8 @@ for the terminal default colour; or a hexadecimal RGB string such as .Ql #ffffff . .It Ic bg=colour Set the background colour. +.It Ic us=colour +Set the underscore colour. .It Ic none Set no attributes (turn off any active attributes). .It Xo Ic acs , diff --git a/tmux.h b/tmux.h index 602bbd89..c22aa2d6 100644 --- a/tmux.h +++ b/tmux.h @@ -680,6 +680,7 @@ struct colour_palette { #define GRID_LINE_WRAPPED 0x1 #define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_DEAD 0x4 +#define GRID_LINE_START_PROMPT 0x8 /* Grid string flags. */ #define GRID_STRING_WITH_SEQUENCES 0x1 @@ -1068,7 +1069,7 @@ struct window_pane { #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 -/* 0x8 unused */ +#define PANE_VISITED 0x8 /* 0x10 unused */ /* 0x20 unused */ #define PANE_INPUTOFF 0x40 @@ -1123,7 +1124,8 @@ struct window_pane { int border_gc_set; struct grid_cell border_gc; - TAILQ_ENTRY(window_pane) entry; + TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */ + TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */ RB_ENTRY(window_pane) tree_entry; }; TAILQ_HEAD(window_panes, window_pane); @@ -1144,7 +1146,7 @@ struct window { struct timeval activity_time; struct window_pane *active; - struct window_pane *last; + struct window_panes last_panes; struct window_panes panes; int lastlayout; @@ -1197,6 +1199,7 @@ struct winlink { #define WINLINK_ACTIVITY 0x2 #define WINLINK_SILENCE 0x4 #define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE) +#define WINLINK_VISITED 0x8 RB_ENTRY(winlink) entry; TAILQ_ENTRY(winlink) wentry; @@ -3090,6 +3093,10 @@ struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_left(struct window_pane *); struct window_pane *window_pane_find_right(struct window_pane *); +void window_pane_stack_push(struct window_panes *, + struct window_pane *); +void window_pane_stack_remove(struct window_panes *, + struct window_pane *); void window_set_name(struct window *, const char *); void window_add_ref(struct window *, const char *); void window_remove_ref(struct window *, const char *); diff --git a/tty-keys.c b/tty-keys.c index 25956c4d..e3000e5a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1160,7 +1160,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; struct window_pane *wp; - size_t end, terminator, needed; + size_t end, terminator = 0, needed; char *copy, *out; int outlen; u_int i; diff --git a/tty-term.c b/tty-term.c index 7dbcfee7..e2242269 100644 --- a/tty-term.c +++ b/tty-term.c @@ -718,7 +718,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, s = tmp; break; case TTYCODE_FLAG: - n = tigetflag((char *) ent->name); + n = tigetflag((char *)ent->name); if (n == -1) continue; if (n) @@ -726,6 +726,8 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, else s = "0"; break; + default: + fatalx("unknown capability type"); } *caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps); xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s); diff --git a/tty.c b/tty.c index a602a950..032bb967 100644 --- a/tty.c +++ b/tty.c @@ -488,10 +488,11 @@ tty_update_features(struct tty *tty) if (tty->term->flags & TERM_VT100LIKE) tty_puts(tty, "\033[?7727h"); - /* tty features might have changed since the first draw during attach. - * For example, this happens when DA responses are received. + /* + * Features might have changed since the first draw during attach. For + * example, this happens when DA responses are received. */ - c->flags |= CLIENT_REDRAWWINDOW; + server_redraw_client(c); tty_invalidate(tty); } @@ -2935,9 +2936,11 @@ tty_check_us(__unused struct tty *tty, struct colour_palette *palette, gc->us = c; } - /* Underscore colour is set as RGB so convert a 256 colour to RGB. */ - if (gc->us & COLOUR_FLAG_256) - gc->us = colour_256toRGB (gc->us); + /* Underscore colour is set as RGB so convert. */ + if ((c = colour_force_rgb (gc->us)) == -1) + gc->us = 8; + else + gc->us = c; } static void @@ -3012,7 +3015,7 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc) u_char r, g, b; /* Clear underline colour. */ - if (gc->us == 0) { + if (COLOUR_DEFAULT(gc->us)) { tty_putcode(tty, TTYC_OL); goto save; } diff --git a/utf8.c b/utf8.c index 042ddf89..38f1a89a 100644 --- a/utf8.c +++ b/utf8.c @@ -71,7 +71,7 @@ static u_int utf8_next_index; /* Get a UTF-8 item from data. */ static struct utf8_item * -utf8_item_by_data(const char *data, size_t size) +utf8_item_by_data(const u_char *data, size_t size) { struct utf8_item ui; @@ -94,7 +94,7 @@ utf8_item_by_index(u_int index) /* Add a UTF-8 item. */ static int -utf8_put_item(const char *data, size_t size, u_int *index) +utf8_put_item(const u_char *data, size_t size, u_int *index) { struct utf8_item *ui; diff --git a/window-copy.c b/window-copy.c index ed481d70..b0f14098 100644 --- a/window-copy.c +++ b/window-copy.c @@ -131,6 +131,7 @@ static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, const char *, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); +static void window_copy_cursor_prompt(struct window_mode_entry *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); static void window_copy_scroll_down(struct window_mode_entry *, u_int); static void window_copy_rectangle_set(struct window_mode_entry *, int); @@ -2240,6 +2241,24 @@ window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cursor_prompt(wme, 1); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cursor_prompt(wme, 0); + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) { @@ -2694,6 +2713,18 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_jump_to_mark }, + { .command = "next-prompt", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_next_prompt + }, + { .command = "previous-prompt", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_previous_prompt + }, { .command = "middle-line", .minargs = 0, .maxargs = 0, @@ -5357,6 +5388,48 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } +static void +window_copy_cursor_prompt(struct window_mode_entry *wme, int direction) +{ + struct window_copy_mode_data *data = wme->data; + struct screen *s = data->backing; + struct grid *gd = s->grid; + u_int end_line; + u_int line = gd->hsize - data->oy + data->cy; + int add; + + if (direction == 0) { /* up */ + add = -1; + end_line = 0; + } else { /* down */ + add = 1; + end_line = gd->hsize + gd->sy - 1; + } + + if (line == end_line) + return; + for (;;) { + if (line == end_line) + return; + line += add; + + if (grid_get_line(gd, line)->flags & GRID_LINE_START_PROMPT) + break; + } + + data->cx = 0; + if (line > gd->hsize) { + data->cy = line - gd->hsize; + data->oy = 0; + } else { + data->cy = 0; + data->oy = gd->hsize - line; + } + + window_copy_update_selection(wme, 1, 0); + window_copy_redraw_screen(wme); +} + static void window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) { diff --git a/window-tree.c b/window-tree.c index 1be47017..b2f397f5 100644 --- a/window-tree.c +++ b/window-tree.c @@ -671,9 +671,9 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, struct window_pane *wp; u_int cx = ctx->s->cx, cy = ctx->s->cy; u_int loop, total, visible, each, width, offset; - u_int current, start, end, remaining, i; + u_int current, start, end, remaining, i, pane_idx; struct grid_cell gc; - int colour, active_colour, left, right, pane_idx; + int colour, active_colour, left, right; char *label; total = window_count_panes(w); diff --git a/window.c b/window.c index 3af71d66..84a32393 100644 --- a/window.c +++ b/window.c @@ -246,21 +246,15 @@ winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) winlink_stack_remove(stack, wl); TAILQ_INSERT_HEAD(stack, wl, sentry); + wl->flags |= WINLINK_VISITED; } void winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) { - struct winlink *wl2; - - if (wl == NULL) - return; - - TAILQ_FOREACH(wl2, stack, sentry) { - if (wl2 == wl) { - TAILQ_REMOVE(stack, wl, sentry); - return; - } + if (wl != NULL && (wl->flags & WINLINK_VISITED)) { + TAILQ_REMOVE(stack, wl, sentry); + wl->flags &= ~WINLINK_VISITED; } } @@ -310,6 +304,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->flags = 0; TAILQ_INIT(&w->panes); + TAILQ_INIT(&w->last_panes); w->active = NULL; w->lastlayout = -1; @@ -519,18 +514,23 @@ window_pane_update_focus(struct window_pane *wp) int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { + struct window_pane *lastwp; + log_debug("%s: pane %%%u", __func__, wp->id); if (wp == w->active) return (0); - w->last = w->active; + lastwp = w->active; + + window_pane_stack_remove(&w->last_panes, wp); + window_pane_stack_push(&w->last_panes, lastwp); w->active = wp; w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; if (options_get_number(global_options, "focus-events")) { - window_pane_update_focus(w->last); + window_pane_update_focus(lastwp); window_pane_update_focus(w->active); } @@ -753,21 +753,21 @@ window_lost_pane(struct window *w, struct window_pane *wp) if (wp == marked_pane.wp) server_clear_marked(); + window_pane_stack_remove(&w->last_panes, wp); if (wp == w->active) { - w->active = w->last; - w->last = NULL; + w->active = TAILQ_FIRST(&w->last_panes); if (w->active == NULL) { w->active = TAILQ_PREV(wp, window_panes, entry); if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } if (w->active != NULL) { + window_pane_stack_remove(&w->last_panes, w->active); w->active->flags |= PANE_CHANGED; notify_window("window-pane-changed", w); window_update_focus(w); } - } else if (wp == w->last) - w->last = NULL; + } } void @@ -851,6 +851,11 @@ window_destroy_panes(struct window *w) { struct window_pane *wp; + while (!TAILQ_EMPTY(&w->last_panes)) { + wp = TAILQ_FIRST(&w->last_panes); + window_pane_stack_remove(&w->last_panes, wp); + } + while (!TAILQ_EMPTY(&w->panes)) { wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); @@ -1488,6 +1493,25 @@ window_pane_find_right(struct window_pane *wp) return (best); } +void +window_pane_stack_push(struct window_panes *stack, struct window_pane *wp) +{ + if (wp != NULL) { + window_pane_stack_remove(stack, wp); + TAILQ_INSERT_HEAD(stack, wp, sentry); + wp->flags |= PANE_VISITED; + } +} + +void +window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp) +{ + if (wp != NULL && (wp->flags & PANE_VISITED)) { + TAILQ_REMOVE(stack, wp, sentry); + wp->flags &= ~PANE_VISITED; + } +} + /* Clear alert flags for a winlink */ void winlink_clear_flags(struct winlink *wl)