diff --git a/Makefile.am b/Makefile.am index 999e9909..fc054091 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,10 @@ endif AM_CPPFLAGS += -DDEBUG endif AM_CPPFLAGS += -iquote. +if IS_ASAN +AM_CFLAGS += -fsanitize=address +AM_LDFLAGS += -fsanitize=address +endif endif # Set flags for Solaris. diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 5ac0192f..fce6d3fb 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -64,6 +64,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) struct session *s; struct window_pane *wp = target->wp, *swp; u_int tty_ox, tty_oy, tty_sx, tty_sy; + int line_numbers; if (args_has(args, 'q')) { window_pane_reset_mode_all(wp); @@ -86,10 +87,15 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) swp = source->wp; else swp = wp; + line_numbers = 1; + if (event != NULL && KEYC_IS_MOUSE(event->key)) + line_numbers = 0; if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { + window_copy_set_line_numbers(wp, line_numbers); if (args_has(args, 'M')) window_copy_start_drag(c, &event->m); - } + } else + window_copy_set_line_numbers(wp, line_numbers); if (args_has(args, 'u')) window_copy_pageup(wp, 0); if (args_has(args, 'd')) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2dfbda83..8444a230 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -118,7 +118,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, item, &cause); } else if (args_has(args, 'p')) { - size = args_strtonum_and_expand(args, 'l', 0, 100, item, + size = args_strtonum_and_expand(args, 'p', 0, 100, item, &cause); if (cause == NULL) size = curval * size / 100; diff --git a/cmd-new-session.c b/cmd-new-session.c index a3e74888..a397ccf9 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -102,7 +102,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) tmp = args_get(args, 's'); if (tmp != NULL) { name = format_single(item, tmp, c, NULL, NULL, NULL); - newname = session_check_name(name); + newname = clean_name(name, "#:."); if (newname == NULL) { cmdq_error(item, "invalid session: %s", name); free(name); @@ -142,7 +142,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) else if (groupwith != NULL) prefix = xstrdup(groupwith->name); else { - prefix = session_check_name(group); + prefix = clean_name(group, "#:."); if (prefix == NULL) { cmdq_error(item, "invalid session group: %s", group); diff --git a/cmd-parse.y b/cmd-parse.y index 542f37d1..0b32f5d3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -37,6 +37,8 @@ static void printflike(1,2) yyerror(const char *, ...); static char *yylex_token(int); static char *yylex_format(void); +#define CMD_PARSE_MAX_ENVIRON_LEN 16384 + struct cmd_parse_scope { int flag; TAILQ_ENTRY (cmd_parse_scope) entry; @@ -232,6 +234,10 @@ assignment : EQUALS flag = flag && scope->flag; } + if (strlen($1) > CMD_PARSE_MAX_ENVIRON_LEN) { + yyerror("environment variable is too long"); + YYABORT; + } if ((~flags & CMD_PARSE_PARSEONLY) && flag) environ_put(global_environ, $1, 0); free($1); @@ -250,6 +256,10 @@ hidden_assignment : HIDDEN EQUALS flag = flag && scope->flag; } + if (strlen($2) > CMD_PARSE_MAX_ENVIRON_LEN) { + yyerror("environment variable is too long"); + YYABORT; + } if ((~flags & CMD_PARSE_PARSEONLY) && flag) environ_put(global_environ, $2, ENVIRON_HIDDEN); free($2); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index a2f8df1b..844b8b85 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -128,6 +128,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) sigprocmask(SIG_SETMASK, &oldset, NULL); cmdq_error(item, "fork error: %s", strerror(errno)); + close(pipe_fd[0]); + close(pipe_fd[1]); free(cmd); return (CMD_RETURN_ERROR); case 0: diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 694f3c97..37b26100 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -52,7 +52,7 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) char *newname, *tmp; tmp = format_single_from_target(item, args_string(args, 0)); - newname = session_check_name(tmp); + newname = clean_name(tmp, "#:."); if (newname == NULL) { cmdq_error(item, "invalid session: %s", tmp); free(tmp); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 045f551e..a9f1c1fe 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -198,8 +198,17 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) } if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, - cdata->flags, -1, -1) == NULL) + cdata->flags, -1, -1) == NULL) { + if (cdata->item == NULL) + status_message_set(c, -1, 1, 0, 0, + "failed to run command: %s", cmd); + else { + cmdq_error(cdata->item, + "failed to run command: %s", cmd); + cmdq_continue(cdata->item); + } cmd_run_shell_free(cdata); + } return; } diff --git a/compat.h b/compat.h index 55fbad48..8d71a9cf 100644 --- a/compat.h +++ b/compat.h @@ -463,6 +463,9 @@ int utf8proc_wctomb(char *, wchar_t); #ifdef NEED_FUZZING /* tmux.c */ #define main __weak main +#define regcomp(preg, pattern, cflags) (0) +#define regexec(preg, string, nmatch, pmatch, eflags) (REG_NOMATCH) +#define regfree(preg) ((void)0) #endif /* getopt.c */ diff --git a/configure.ac b/configure.ac index fa474825..093d855b 100644 --- a/configure.ac +++ b/configure.ac @@ -73,6 +73,15 @@ AC_ARG_ENABLE( ) AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes) +# Is this --enable-asan? +AC_ARG_ENABLE( + asan, + AS_HELP_STRING(--enable-asan, enable ASAN build flags), + , + enable_asan=no +) +AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes) + # Is this a static build? AC_ARG_ENABLE( static, diff --git a/control.c b/control.c index 2fc97b5e..6b7c3b26 100644 --- a/control.c +++ b/control.c @@ -207,10 +207,12 @@ control_free_sub(struct control_state *cs, struct control_sub *csub) RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) { RB_REMOVE(control_sub_panes, &csub->panes, csp); + free(csp->last); free(csp); } RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) { RB_REMOVE(control_sub_windows, &csub->windows, csw); + free(csw->last); free(csw); } free(csub->last); @@ -298,6 +300,7 @@ control_reset_offsets(struct client *c) struct control_pane *cp, *cp1; RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { + control_discard_pane(c, cp); RB_REMOVE(control_panes, &cs->panes, cp); free(cp); } @@ -352,6 +355,9 @@ control_set_pane_off(struct client *c, struct window_pane *wp) struct control_pane *cp; cp = control_add_pane(c, wp); + control_discard_pane(c, cp); + memcpy(&cp->offset, &wp->offset, sizeof cp->offset); + memcpy(&cp->queued, &wp->offset, sizeof cp->queued); cp->flags |= CONTROL_PANE_OFF; } diff --git a/file.c b/file.c index 1f1be060..2bf7a570 100644 --- a/file.c +++ b/file.c @@ -804,7 +804,7 @@ file_read_cancel(struct client_files *files, struct imsg *imsg) } /* Handle a write ready message (server). */ -void +int file_write_ready(struct client_files *files, struct imsg *imsg) { struct msg_write_ready *msg = imsg->data; @@ -812,19 +812,20 @@ file_write_ready(struct client_files *files, struct imsg *imsg) struct client_file find, *cf; if (msglen != sizeof *msg) - fatalx("bad MSG_WRITE_READY size"); + return (-1); find.stream = msg->stream; if ((cf = RB_FIND(client_files, files, &find)) == NULL) - return; + return (0); if (msg->error != 0) { cf->error = msg->error; file_fire_done(cf); } else file_push(cf); + return (0); } /* Handle read data message (server). */ -void +int file_read_data(struct client_files *files, struct imsg *imsg) { struct msg_read_data *msg = imsg->data; @@ -834,10 +835,10 @@ file_read_data(struct client_files *files, struct imsg *imsg) size_t bsize = msglen - sizeof *msg; if (msglen < sizeof *msg) - fatalx("bad MSG_READ_DATA size"); + return (-1); find.stream = msg->stream; if ((cf = RB_FIND(client_files, files, &find)) == NULL) - return; + return (0); log_debug("file %d read %zu bytes", cf->stream, bsize); if (cf->error == 0 && !cf->closed) { @@ -847,10 +848,11 @@ file_read_data(struct client_files *files, struct imsg *imsg) } else file_fire_read(cf); } + return (0); } /* Handle a read done message (server). */ -void +int file_read_done(struct client_files *files, struct imsg *imsg) { struct msg_read_done *msg = imsg->data; @@ -858,12 +860,13 @@ file_read_done(struct client_files *files, struct imsg *imsg) struct client_file find, *cf; if (msglen != sizeof *msg) - fatalx("bad MSG_READ_DONE size"); + return (-1); find.stream = msg->stream; if ((cf = RB_FIND(client_files, files, &find)) == NULL) - return; + return (0); log_debug("file %d read done", cf->stream); cf->error = msg->error; file_fire_done(cf); + return (0); } diff --git a/format.c b/format.c index 74399665..0486d52e 100644 --- a/format.c +++ b/format.c @@ -120,6 +120,9 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 100 +/* Limit on time taken (milliseconds). */ +#define FORMAT_TIME_LIMIT 100 + /* Format expand flags. */ #define FORMAT_EXPAND_TIME 0x1 #define FORMAT_EXPAND_NOJOBS 0x2 @@ -169,9 +172,11 @@ RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); struct format_expand_state { struct format_tree *ft; u_int loop; + uint64_t start_time; + int flags; + time_t time; struct tm tm; - int flags; }; /* Format modifier. */ @@ -292,6 +297,7 @@ format_copy_state(struct format_expand_state *to, to->time = from->time; memcpy(&to->tm, &from->tm, sizeof to->tm); to->flags = from->flags|flags; + to->start_time = from->start_time; } /* Format job update callback. */ @@ -2319,7 +2325,7 @@ format_cb_pane_pipe_pid(struct format_tree *ft) static void * format_cb_pane_pb_progress(struct format_tree *ft) { - char *value = NULL; + char *value = NULL; if (ft->wp != NULL) xasprintf(&value, "%d", ft->wp->base.progress_bar.progress); @@ -4155,15 +4161,33 @@ found: return (found); } +/* Check if format has not taken too long. */ +static int +format_check_time(struct format_expand_state *es) +{ + uint64_t t = get_timer(); + + if (t - es->start_time < FORMAT_TIME_LIMIT) + return (1); + t -= es->start_time; + + format_log(es, "reached time limit (%llu)", (unsigned long long)t); + return (0); +} + /* Unescape escaped characters. */ static char * -format_unescape(const char *s) +format_unescape(struct format_expand_state *es, const char *s) { char *out, *cp; int brackets = 0; cp = out = xmalloc(strlen(s) + 1); for (; *s != '\0'; s++) { + if (!format_check_time(es)){ + free(out); + return (xstrdup("")); + } if (*s == '#' && s[1] == '{') brackets++; if (brackets == 0 && @@ -4182,13 +4206,17 @@ format_unescape(const char *s) /* Remove escaped characters. */ static char * -format_strip(const char *s) +format_strip(struct format_expand_state *es, const char *s) { char *out, *cp; int brackets = 0; cp = out = xmalloc(strlen(s) + 1); for (; *s != '\0'; s++) { + if (!format_check_time(es)){ + free(out); + return (xstrdup("")); + } if (*s == '#' && s[1] == '{') brackets++; if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { @@ -4204,13 +4232,16 @@ format_strip(const char *s) return (out); } + /* Skip until end. */ -const char * -format_skip(const char *s, const char *end) +static const char * +format_skip1(struct format_expand_state *es, const char *s, const char *end) { int brackets = 0; for (; *s != '\0'; s++) { + if (es != NULL && !format_check_time(es)) + return (NULL); if (*s == '#' && s[1] == '{') brackets++; if (*s == '#' && @@ -4229,6 +4260,13 @@ format_skip(const char *s, const char *end) return (s); } +/* Skip until end. */ +const char * +format_skip(const char *s, const char *end) +{ + return (format_skip1(NULL, s, end)); +} + /* Return left and right alternatives separated by commas. */ static int format_choose(struct format_expand_state *es, const char *s, char **left, @@ -4237,7 +4275,7 @@ format_choose(struct format_expand_state *es, const char *s, char **left, const char *cp; char *left0, *right0; - cp = format_skip(s, ","); + cp = format_skip1(es, s, ","); if (cp == NULL) return (-1); left0 = xstrndup(s, cp - s); @@ -4368,7 +4406,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* Single argument with no wrapper character. */ if (!ispunct((u_char)cp[1]) || cp[1] == '-') { - end = format_skip(cp + 1, ":;"); + end = format_skip1(es, cp + 1, ":;"); if (end == NULL) break; @@ -4391,7 +4429,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; break; } - end = format_skip(cp + 1, last); + end = format_skip1(es, cp + 1, last); if (end == NULL) break; cp++; @@ -4505,7 +4543,7 @@ format_bool_op_n(struct format_expand_state *es, const char *fmt, int and) cp1 = fmt; while (and ? result : !result) { - cp2 = format_skip(cp1, ","); + cp2 = format_skip1(es, cp1, ","); if (cp2 == NULL) raw = xstrdup(cp1); @@ -5085,7 +5123,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, else if (fm->argc >= 2 && strchr(fm->argv[0], 'f') != NULL) { free(time_format); - time_format = format_strip(fm->argv[1]); + time_format = format_strip(es, fm->argv[1]); } break; case 'q': @@ -5201,7 +5239,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, /* Is this a literal string? */ if (modifiers & FORMAT_LITERAL) { format_log(es, "literal string is '%s'", copy); - value = format_unescape(copy); + value = format_unescape(es, copy); goto done; } @@ -5265,7 +5303,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = format_search(search, wp, new); } free(new); - } else if (modifiers & FORMAT_REPEAT) { + } else if (modifiers & FORMAT_REPEAT) { /* Repeat multiple times. */ if (format_choose(es, copy, &left, &right, 1) != 0) { format_log(es, "repeat syntax error: %s", copy); @@ -5277,6 +5315,12 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, else { value = xstrdup(""); for (i = 0; i < nrep; i++) { + if (!format_check_time(es)) { + free(right); + free(left); + free(value); + goto fail; + } xasprintf(&new, "%s%s", value, left); free(value); value = new; @@ -5284,7 +5328,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, } free(right); free(left); - } else if (modifiers & FORMAT_NOT) { + } else if (modifiers & FORMAT_NOT) { value = format_bool_op_1(es, copy, 1); } else if (modifiers & FORMAT_NOT_NOT) { value = format_bool_op_1(es, copy, 0); @@ -5348,7 +5392,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, */ cp = copy + 1; while (1) { - cp2 = format_skip(cp, ","); + cp2 = format_skip1(es, cp, ","); if (cp2 == NULL) { format_log(es, "no condition matched in '%s'; using last " @@ -5383,7 +5427,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, } cp = cp2 + 1; - cp2 = format_skip(cp, ","); + cp2 = format_skip1(es, cp, ","); if (format_true(found)) { format_log(es, "condition '%s' is true", condition); @@ -5551,7 +5595,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) int ch, brackets; char expanded[8192]; - if (fmt == NULL || *fmt == '\0') + if (fmt == NULL || *fmt == '\0' || !format_check_time(es)) return (xstrdup("")); if (es->loop == FORMAT_LOOP_LIMIT) { @@ -5633,7 +5677,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) fmt += n + 1; continue; case '{': - ptr = format_skip((char *)fmt - 2, "}"); + ptr = format_skip1(es, (char *)fmt - 2, "}"); if (ptr == NULL) break; n = ptr - fmt; @@ -5656,7 +5700,7 @@ format_expand1(struct format_expand_state *es, const char *fmt) n++; } if (*ptr == '[') { - style_end = format_skip(fmt - 2, "]"); + style_end = format_skip1(es, fmt - 2, "]"); format_log(es, "found #*%zu[", n); while (len - off < n + 2) { buf = xreallocarray(buf, 2, len); @@ -5720,6 +5764,7 @@ format_expand_time(struct format_tree *ft, const char *fmt) memset(&es, 0, sizeof es); es.ft = ft; es.flags = FORMAT_EXPAND_TIME; + es.start_time = get_timer(); return (format_expand1(&es, fmt)); } @@ -5732,6 +5777,7 @@ format_expand(struct format_tree *ft, const char *fmt) memset(&es, 0, sizeof es); es.ft = ft; es.flags = 0; + es.start_time = get_timer(); return (format_expand1(&es, fmt)); } diff --git a/image-sixel.c b/image-sixel.c index c37e2cce..cc946cfe 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -122,19 +122,13 @@ sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c) static int sixel_parse_write(struct sixel_image *si, u_int ch) { - struct sixel_line *sl; u_int i; - if (sixel_parse_expand_lines(si, si->dy + 6) != 0) - return (1); - sl = &si->lines[si->dy]; - for (i = 0; i < 6; i++) { - if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0) - return (1); - if (ch & (1 << i)) - sl->data[si->dx] = si->dc; - sl++; + if (ch & (1 << i)) { + if (sixel_set_pixel(si, si->dx, si->dy + i, si->dc)) + return (1); + } } return (0); } diff --git a/image.c b/image.c index 0948925b..f0543dc4 100644 --- a/image.c +++ b/image.c @@ -53,14 +53,12 @@ image_log(struct image *im, const char* from, const char* fmt, ...) static void image_free(struct image *im) { - struct screen *s = im->s; - image_log(im, __func__, NULL); TAILQ_REMOVE(&all_images, im, all_entry); all_images_count--; - TAILQ_REMOVE(&s->images, im, entry); + TAILQ_REMOVE(im->list, im, entry); sixel_free(im->data); free(im->fallback); free(im); @@ -137,7 +135,8 @@ image_store(struct screen *s, struct sixel_image *si) image_fallback(&im->fallback, im->sx, im->sy); image_log(im, __func__, NULL); - TAILQ_INSERT_TAIL(&s->images, im, entry); + im->list = &s->images; + TAILQ_INSERT_TAIL(im->list, im, entry); TAILQ_INSERT_TAIL(&all_images, im, all_entry); if (++all_images_count == MAX_IMAGE_COUNT) diff --git a/input.c b/input.c index 671f5788..07f0ce76 100644 --- a/input.c +++ b/input.c @@ -2663,12 +2663,9 @@ input_exit_osc(struct input_ctx *ictx) input_osc_4(ictx, p); break; case 7: - if (utf8_isvalid(p)) { - screen_set_path(sctx->s, p); - if (wp != NULL) { - server_redraw_window_borders(wp->window); - server_status_window(wp->window); - } + if (screen_set_path(sctx->s, p) && wp != NULL) { + server_redraw_window_borders(wp->window); + server_status_window(wp->window); } break; case 8: diff --git a/key-bindings.c b/key-bindings.c index 4bb7d801..49454ed7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -474,6 +474,10 @@ key_bindings_init(void) "bind -n MouseDown1Status { if -F '#{&&:#{pane_active},#{!#{pane_minimised_flag}}}' { minimise-pane -t= } { switch-client -t= } }", "bind -n C-MouseDown1Status { swap-window -t@ }", + /* Mouse button 1 down on default pane-border-format */ + "bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}", + "bind -n MouseDown1Control8 { resize-pane -Z }", + /* Mouse wheel down on status line. */ "bind -n WheelDownStatus { next-window }", @@ -506,7 +510,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-b { send -X cursor-left }", "bind -Tcopy-mode C-g { send -X clear-selection }", "bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }", - "bind -Tcopy-mode C-l { send -X cursor-centre-vertical }", + "bind -Tcopy-mode C-l { send -X recentre-top-bottom }", "bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }", "bind -Tcopy-mode C-n { send -X cursor-down }", "bind -Tcopy-mode C-p { send -X cursor-up }", @@ -515,6 +519,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-v { send -X page-down }", "bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }", "bind -Tcopy-mode Escape { send -X cancel }", + "bind -Tcopy-mode C-[ { send -X cancel }", "bind -Tcopy-mode Space { send -X page-down }", "bind -Tcopy-mode , { send -X jump-reverse }", "bind -Tcopy-mode \\; { send -X jump-again }", @@ -588,6 +593,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi C-v { send -X rectangle-toggle }", "bind -Tcopy-mode-vi C-y { send -X scroll-up }", "bind -Tcopy-mode-vi Escape { send -X clear-selection }", + "bind -Tcopy-mode-vi C-[ { send -X clear-selection }", "bind -Tcopy-mode-vi Space { send -X begin-selection }", "bind -Tcopy-mode-vi '$' { send -X end-of-line }", "bind -Tcopy-mode-vi , { send -X jump-reverse }", diff --git a/layout-custom.c b/layout-custom.c index 7b91f9ac..050edb4b 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -183,13 +183,14 @@ layout_parse(struct window *w, const char *layout, char **cause) struct window_pane *wp; u_int npanes, ncells, sx = 0, sy = 0; u_short csum; + int n; /* Check validity. */ - if (sscanf(layout, "%hx,", &csum) != 1) { + if (sscanf(layout, "%hx,%n", &csum, &n) != 1 || n != 5) { *cause = xstrdup("invalid layout"); return (-1); } - layout += 5; + layout += n; if (csum != layout_checksum(layout)) { *cause = xstrdup("invalid layout"); return (-1); diff --git a/menu.c b/menu.c index 9ae300ed..4f122831 100644 --- a/menu.c +++ b/menu.c @@ -471,6 +471,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) case '\r': goto chosen; case '\033': /* Escape */ + case '['|KEYC_CTRL: case 'c'|KEYC_CTRL: case 'g'|KEYC_CTRL: case 'q': diff --git a/mode-tree.c b/mode-tree.c index 835f4219..6479d78d 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -301,6 +301,8 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl) void mode_tree_up(struct mode_tree_data *mtd, int wrap) { + if (mtd->line_size == 0) + return; if (mtd->current == 0) { if (wrap) { mtd->current = mtd->line_size - 1; @@ -317,6 +319,8 @@ mode_tree_up(struct mode_tree_data *mtd, int wrap) int mode_tree_down(struct mode_tree_data *mtd, int wrap) { + if (mtd->line_size == 0) + return (0); if (mtd->current == mtd->line_size - 1) { if (wrap) { mtd->current = 0; @@ -363,6 +367,8 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction) void * mode_tree_get_current(struct mode_tree_data *mtd) { + if (mtd->line_size == 0) + return (NULL); return (mtd->line_list[mtd->current].item->itemdata); } @@ -433,6 +439,8 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) return (1); } if (mtd->current >= mtd->line_size) { + if (mtd->line_size == 0) + return (0); mtd->current = mtd->line_size - 1; if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; @@ -1276,6 +1284,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, switch (*key) { case 'q': case '\033': /* Escape */ + case '['|KEYC_CTRL: case 'g'|KEYC_CTRL: return (1); case KEYC_F1: diff --git a/names.c b/names.c index aeb67338..8a04df73 100644 --- a/names.c +++ b/names.c @@ -166,7 +166,9 @@ parse_window_name(const char *in) if (*name == '/') name = basename(name); - name = xstrdup(name); + name = clean_name(name, "#"); free(copy); + if (name == NULL) + return (xstrdup("")); return (name); } diff --git a/options-table.c b/options-table.c index aefd2db9..021a29ce 100644 --- a/options-table.c +++ b/options-table.c @@ -108,6 +108,9 @@ static const char *options_table_extended_keys_format_list[] = { static const char *options_table_allow_passthrough_list[] = { "off", "on", "all", NULL }; +static const char *options_table_copy_mode_line_numbers_list[] = { + "off", "default", "absolute", "relative", "hybrid", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -1176,7 +1179,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "#[align=right]" "#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}" - "[#{scroll_position}/#{history_size}]" + "[#{copy_position}/#{copy_position_limit}]" "#{?search_timed_out, (timed out)," "#{?search_count, (#{search_count}" "#{?search_count_partial,+,} results),}}", @@ -1201,6 +1204,32 @@ const struct options_table_entry options_table[] = { .text = "Style of selection in copy mode." }, + { .name = "copy-mode-current-line-number-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "fg=yellow", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of current line number in copy mode." + }, + + { .name = "copy-mode-line-number-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "fg=white,dim", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of line numbers in copy mode." + }, + + { .name = "copy-mode-line-numbers", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_copy_mode_line_numbers_list, + .default_num = 0, + .text = "Line number mode in copy mode." + }, + { .name = "fill-character", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, @@ -1326,7 +1355,14 @@ const struct options_table_entry options_table[] = { .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " - "\"#{pane_title}\"", + "\"#{pane_title}\"" + "#{?#{mouse}," + "#[align=right]" + "#[range=control|8][" + "#{?#{window_zoomed_flag},u,z}" + "]#[norange]" + "#[range=control|9][x]#[norange]" + ",}", .text = "Format of text in the pane status lines." }, @@ -1473,6 +1509,28 @@ const struct options_table_entry options_table[] = { "A value of 0 means no limit." }, + { .name = "tree-mode-preview-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "#{?pane_format," + "#{pane_index}:#{pane_title}," + "#{window_index}:#{window_name}}", + .text = "Format of the preview indicator in tree mode." + }, + + { .name = "tree-mode-preview-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "fg=#{?#{||:" + "#{&&:#{pane_format},#{pane_active}}," + "#{&&:#{window_format},#{window_active}}}," + "#{display-panes-active-colour}," + "#{display-panes-colour}}", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of preview indicator in tree mode." + }, + { .name = "window-active-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/paste.c b/paste.c index 7eda6ada..6461d9ba 100644 --- a/paste.c +++ b/paste.c @@ -204,6 +204,7 @@ int paste_rename(const char *oldname, const char *newname, char **cause) { struct paste_buffer *pb, *pb_new; + char *name; if (cause != NULL) *cause = NULL; @@ -219,23 +220,33 @@ paste_rename(const char *oldname, const char *newname, char **cause) return (-1); } + name = clean_name(newname, ""); + if (name == NULL) { + if (cause != NULL) + xasprintf(cause, "invalid buffer name: %s", newname); + return (-1); + } + pb = paste_get_name(oldname); if (pb == NULL) { if (cause != NULL) xasprintf(cause, "no buffer %s", oldname); + free(name); return (-1); } - pb_new = paste_get_name(newname); - if (pb_new == pb) + pb_new = paste_get_name(name); + if (pb_new == pb) { + free(name); return (0); + } if (pb_new != NULL) paste_free(pb_new); RB_REMOVE(paste_name_tree, &paste_by_name, pb); free(pb->name); - pb->name = xstrdup(newname); + pb->name = name; if (pb->automatic) paste_num_automatic--; @@ -244,7 +255,7 @@ paste_rename(const char *oldname, const char *newname, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); notify_paste_buffer(oldname, 1); - notify_paste_buffer(newname, 0); + notify_paste_buffer(pb->name, 0); return (0); } @@ -257,6 +268,7 @@ int paste_set(char *data, size_t size, const char *name, char **cause) { struct paste_buffer *pb, *old; + char *newname; if (cause != NULL) *cause = NULL; @@ -276,9 +288,16 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (-1); } + newname = clean_name(name, ""); + if (newname == NULL) { + if (cause != NULL) + xasprintf(cause, "invalid buffer name: %s", name); + return (-1); + } + pb = xmalloc(sizeof *pb); - pb->name = xstrdup(name); + pb->name = newname; pb->data = data; pb->size = size; @@ -288,13 +307,13 @@ paste_set(char *data, size_t size, const char *name, char **cause) pb->created = time(NULL); - if ((old = paste_get_name(name)) != NULL) + if ((old = paste_get_name(pb->name)) != NULL) paste_free(old); RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); - notify_paste_buffer(name, 0); + notify_paste_buffer(pb->name, 0); return (0); } diff --git a/regress/utf8-test.result b/regress/utf8-test.result index e700cb17..f15d049d 100644 --- a/regress/utf8-test.result +++ b/regress/utf8-test.result @@ -90,7 +90,7 @@ You should see the Greek word 'kosme': "κόσμε" 2.3.2 U-0000E000 = ee 80 80 = "" | 2.3.3 U-0000FFFD = ef bf bd = "�" | 2.3.4 U-0010FFFF = f4 8f bf bf = "􏿿" | -2.3.5 U-00110000 = f4 90 80 80 = "�" | +2.3.5 U-00110000 = f4 90 80 80 = "" | | 3 Malformed sequences | | diff --git a/screen-redraw.c b/screen-redraw.c index 8fd9839e..7a15b158 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -739,7 +739,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) if (ctx->statustop) yoff += ctx->statuslines; - for (i=0; i < r->used; i++) { + for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) continue; diff --git a/screen.c b/screen.c index 63c6009c..2c78c5ac 100644 --- a/screen.c +++ b/screen.c @@ -36,6 +36,8 @@ struct screen_sel { u_int ex; u_int ey; + u_int clipx; + struct grid_cell cell; }; @@ -245,19 +247,28 @@ screen_set_cursor_colour(struct screen *s, int colour) int screen_set_title(struct screen *s, const char *title) { - if (!utf8_isvalid(title)) + char *new_title; + + new_title = clean_name(title, "#"); + if (new_title == NULL) return (0); free(s->title); - s->title = xstrdup(title); + s->title = new_title; return (1); } /* Set screen path. */ -void +int screen_set_path(struct screen *s, const char *path) { + char *new_path; + + new_path = clean_name(path, "#"); + if (new_path == NULL) + return (0); free(s->path); - utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + s->path = new_path; + return (1); } /* Push the current title onto the stack. */ @@ -266,6 +277,16 @@ screen_push_title(struct screen *s) { struct screen_title_entry *title_entry; + log_debug("%s: %u", __func__, s->ntitles); + + while (s->ntitles >= 10) { + title_entry = TAILQ_LAST(s->titles, screen_titles); + free(title_entry->text); + TAILQ_REMOVE(s->titles, title_entry, entry); + free(title_entry); + s->ntitles--; + } + if (s->titles == NULL) { s->titles = xmalloc(sizeof *s->titles); TAILQ_INIT(s->titles); @@ -273,6 +294,7 @@ screen_push_title(struct screen *s) title_entry = xmalloc(sizeof *title_entry); title_entry->text = xstrdup(s->title); TAILQ_INSERT_HEAD(s->titles, title_entry, entry); + s->ntitles++; } /* @@ -286,14 +308,15 @@ screen_pop_title(struct screen *s) if (s->titles == NULL) return; + log_debug("%s: %u", __func__, s->ntitles); title_entry = TAILQ_FIRST(s->titles); if (title_entry != NULL) { - screen_set_title(s, title_entry->text); - + free(s->title); + s->title = title_entry->text; TAILQ_REMOVE(s->titles, title_entry, entry); - free(title_entry->text); free(title_entry); + s->ntitles--; } } @@ -456,7 +479,8 @@ screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) /* Set selection. */ void screen_set_selection(struct screen *s, u_int sx, u_int sy, - u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc) + u_int ex, u_int ey, u_int rectangle, u_int clipx, int modekeys, + struct grid_cell *gc) { if (s->sel == NULL) s->sel = xcalloc(1, sizeof *s->sel); @@ -470,6 +494,7 @@ screen_set_selection(struct screen *s, u_int sx, u_int sy, s->sel->sy = sy; s->sel->ex = ex; s->sel->ey = ey; + s->sel->clipx = clipx; } /* Clear selection. */ @@ -497,6 +522,8 @@ screen_check_selection(struct screen *s, u_int px, u_int py) if (sel == NULL || sel->hidden) return (0); + if (px < sel->clipx) + return (0); if (sel->rectangle) { if (sel->sy < sel->ey) { @@ -648,7 +675,10 @@ screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) void screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) { - u_int sx, sy; + u_int sx, sy; +#ifdef ENABLE_SIXEL + struct image *im; +#endif if (SCREEN_IS_ALTERNATE(s)) return; @@ -665,6 +695,8 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) #ifdef ENABLE_SIXEL TAILQ_CONCAT(&s->saved_images, &s->images, entry); + TAILQ_FOREACH(im, &s->saved_images, entry) + im->list = &s->saved_images; #endif grid_view_clear(s->grid, 0, 0, sx, sy, 8); @@ -677,7 +709,10 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) void screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) { - u_int sx = screen_size_x(s), sy = screen_size_y(s); + u_int sx = screen_size_x(s), sy = screen_size_y(s); +#ifdef ENABLE_SIXEL + struct image *im; +#endif /* * If the current size is different, temporarily resize to the old size @@ -724,6 +759,8 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) #ifdef ENABLE_SIXEL image_free_all(s); TAILQ_CONCAT(&s->images, &s->saved_images, entry); + TAILQ_FOREACH(im, &s->images, entry) + im->list = &s->images; #endif if (s->cx > screen_size_x(s) - 1) @@ -762,6 +799,8 @@ screen_mode_to_string(int mode) strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp); if (mode & MODE_CURSOR_VERY_VISIBLE) strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); + if (mode & MODE_CURSOR_BLINKING_SET) + strlcat(tmp, "CURSOR_BLINKING_SET,", sizeof tmp); if (mode & MODE_MOUSE_UTF8) strlcat(tmp, "MOUSE_UTF8,", sizeof tmp); if (mode & MODE_MOUSE_SGR) @@ -782,7 +821,10 @@ screen_mode_to_string(int mode) strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp); if (mode & MODE_THEME_UPDATES) strlcat(tmp, "THEME_UPDATES,", sizeof tmp); - tmp[strlen(tmp) - 1] = '\0'; + if (mode & MODE_SYNC) + strlcat(tmp, "SYNC,", sizeof tmp); + if (*tmp != '\0') + tmp[strlen(tmp) - 1] = '\0'; return (tmp); } diff --git a/server-client.c b/server-client.c index 83dfbcef..9ca4b289 100644 --- a/server-client.c +++ b/server-client.c @@ -41,6 +41,7 @@ static void server_client_check_redraw(struct client *); static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_set_path(struct client *); +static void server_client_set_progress_bar(struct client *); static void server_client_reset_state(struct client *); static void server_client_update_latest(struct client *); static void server_client_dispatch(struct imsg *, void *); @@ -2142,6 +2143,7 @@ server_client_check_redraw(struct client *c) server_client_set_title(c); server_client_set_path(c); } + server_client_set_progress_bar(c); screen_redraw_screen(c); } @@ -2208,6 +2210,23 @@ server_client_set_path(struct client *c) } } +/* Set client progress bar. */ +static void +server_client_set_progress_bar(struct client *c) +{ + struct session *s = c->session; + struct progress_bar *pane_pb; + + if (s->curw == NULL) + return; + pane_pb = &s->curw->window->active->base.progress_bar; + if (pane_pb->state == c->progress_bar.state && + pane_pb->progress == c->progress_bar.progress) + return; + memcpy(&c->progress_bar, pane_pb, sizeof c->progress_bar); + tty_set_progress_bar(&c->tty, &c->progress_bar); +} + /* Dispatch message from client. */ static void server_client_dispatch(struct imsg *imsg, void *arg) @@ -2302,13 +2321,16 @@ server_client_dispatch(struct imsg *imsg, void *arg) goto bad; break; case MSG_WRITE_READY: - file_write_ready(&c->files, imsg); + if (file_write_ready(&c->files, imsg) != 0) + goto bad; break; case MSG_READ: - file_read_data(&c->files, imsg); + if (file_read_data(&c->files, imsg) != 0) + goto bad; break; case MSG_READ_DONE: - file_read_done(&c->files, imsg); + if (file_read_done(&c->files, imsg) != 0) + goto bad; break; } diff --git a/session.c b/session.c index f58ec3f6..1e558977 100644 --- a/session.c +++ b/session.c @@ -229,24 +229,6 @@ session_destroy(struct session *s, int notify, const char *from) session_remove_ref(s, __func__); } -/* Sanitize session name. */ -char * -session_check_name(const char *name) -{ - char *copy, *cp, *new_name; - - if (*name == '\0') - return (NULL); - copy = xstrdup(name); - for (cp = copy; *cp != '\0'; cp++) { - if (*cp == ':' || *cp == '.') - *cp = '_'; - } - utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); - free(copy); - return (new_name); -} - /* Lock session if it has timed out. */ static void session_lock_timer(__unused int fd, __unused short events, void *arg) diff --git a/spawn.c b/spawn.c index 38816f39..24617bdd 100644 --- a/spawn.c +++ b/spawn.c @@ -82,6 +82,7 @@ spawn_window(struct spawn_context *sc, char **cause) struct winlink *wl; int idx = sc->idx; u_int sx, sy, xpixel, ypixel; + char *name; spawn_log(__func__, sc); @@ -180,8 +181,11 @@ spawn_window(struct spawn_context *sc, char **cause) if (~sc->flags & SPAWN_RESPAWN) { free(w->name); if (sc->name != NULL) { - w->name = format_single(item, sc->name, c, s, NULL, - NULL); + name = format_single(item, sc->name, c, s, NULL, NULL); + w->name = clean_name(name, "#"); + free(name); + if (w->name == NULL) + w->name = xstrdup(""); options_set_number(w->options, "automatic-rename", 0); } else w->name = default_window_name(w); diff --git a/status.c b/status.c index 5ecf7bf9..2dfad519 100644 --- a/status.c +++ b/status.c @@ -689,10 +689,13 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, server_client_clear_overlay(c); - if (fs != NULL) + if (fs != NULL) { ft = format_create_from_state(NULL, c, fs); - else + cmd_find_copy_state(&c->prompt_state, fs); + } else { ft = format_create_defaults(NULL, c, NULL, NULL, NULL); + cmd_find_clear_state(&c->prompt_state, 0); + } if (input == NULL) input = ""; @@ -701,7 +704,6 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, status_prompt_clear(c); status_push_screen(c); - c->prompt_formats = ft; c->prompt_string = xstrdup (msg); if (flags & PROMPT_NOFORMAT) @@ -737,6 +739,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT)) cmdq_append(c, cmdq_get_callback(status_prompt_accept, c)); + format_free(ft); } /* Remove status line prompt. */ @@ -752,9 +755,6 @@ status_prompt_clear(struct client *c) free(c->prompt_last); c->prompt_last = NULL; - format_free(c->prompt_formats); - c->prompt_formats = NULL; - free(c->prompt_string); c->prompt_string = NULL; @@ -774,13 +774,19 @@ status_prompt_clear(struct client *c) void status_prompt_update(struct client *c, const char *msg, const char *input) { - char *tmp; + struct format_tree *ft; + char *tmp; + + if (cmd_find_valid_state(&c->prompt_state)) + ft = format_create_from_state(NULL, c, &c->prompt_state); + else + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); free(c->prompt_string); c->prompt_string = xstrdup(msg); free(c->prompt_buffer); - tmp = format_expand_time(c->prompt_formats, input); + tmp = format_expand_time(ft, input); c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); free(tmp); @@ -788,6 +794,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->flags |= CLIENT_REDRAWSTATUS; + format_free(ft); } /* Redraw character. Return 1 if can continue redrawing, 0 otherwise. */ @@ -846,11 +853,13 @@ status_prompt_redraw(struct client *c) struct status_line *sl = &c->status; struct screen_write_ctx ctx; struct session *s = c->session; + struct options *oo = s->options; struct screen old_screen; u_int i, lines, offset, left, start, width, n; u_int pcursor, pwidth, promptline; u_int ax, aw; struct grid_cell gc; + struct format_tree *ft; const char *msgfmt; char *expanded, *prompt, *tmp; @@ -863,12 +872,17 @@ status_prompt_redraw(struct client *c) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); + if (cmd_find_valid_state(&c->prompt_state)) + ft = format_create_from_state(NULL, c, &c->prompt_state); + else + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); + n = options_get_number(s->options, "prompt-cursor-colour"); sl->active->default_ccolour = n; if (c->prompt_mode == PROMPT_COMMAND) - n = options_get_number(s->options, "prompt-command-cursor-style"); + n = options_get_number(oo, "prompt-command-cursor-style"); else - n = options_get_number(s->options, "prompt-cursor-style"); + n = options_get_number(oo, "prompt-cursor-style"); screen_set_cursor_style(n, &sl->active->default_cstyle, &sl->active->default_mode); @@ -877,28 +891,29 @@ status_prompt_redraw(struct client *c) promptline = lines - 1; if (c->prompt_mode == PROMPT_COMMAND) - style_apply(&gc, s->options, "message-command-style", NULL); + style_apply(&gc, oo, "message-command-style", NULL); else - style_apply(&gc, s->options, "message-style", NULL); + style_apply(&gc, oo, "message-style", NULL); status_prompt_area(c, &ax, &aw); tmp = utf8_tocstr(c->prompt_buffer); - format_add(c->prompt_formats, "prompt-input", "%s", tmp); - prompt = format_expand_time(c->prompt_formats, c->prompt_string); + format_add(ft, "prompt-input", "%s", tmp); + prompt = format_expand_time(ft, c->prompt_string); free(tmp); /* * Set #{message} to the prompt string and expand message-format. * format_draw handles fill, alignment, and decorations in one call. */ - format_add(c->prompt_formats, "message", "%s", prompt); - format_add(c->prompt_formats, "command_prompt", "%d", + format_add(ft, "message", "%s", prompt); + format_add(ft, "command_prompt", "%d", c->prompt_mode == PROMPT_COMMAND); - msgfmt = options_get_string(s->options, "message-format"); - expanded = format_expand_time(c->prompt_formats, msgfmt); + msgfmt = options_get_string(oo, "message-format"); + expanded = format_expand_time(ft, msgfmt); + free(prompt); - start = format_width(prompt); + start = format_width(expanded); if (start > aw) start = aw; @@ -972,6 +987,49 @@ status_prompt_space(const struct utf8_data *ud) return (*ud->data == ' '); } +static key_code +status_prompt_keypad_key(key_code key) +{ + if (key & KEYC_MASK_MODIFIERS) + return (key); + + switch (key) { + case KEYC_KP_SLASH: + return ('/'); + case KEYC_KP_STAR: + return ('*'); + case KEYC_KP_MINUS: + return ('-'); + case KEYC_KP_SEVEN: + return ('7'); + case KEYC_KP_EIGHT: + return ('8'); + case KEYC_KP_NINE: + return ('9'); + case KEYC_KP_PLUS: + return ('+'); + case KEYC_KP_FOUR: + return ('4'); + case KEYC_KP_FIVE: + return ('5'); + case KEYC_KP_SIX: + return ('6'); + case KEYC_KP_ONE: + return ('1'); + case KEYC_KP_TWO: + return ('2'); + case KEYC_KP_THREE: + return ('3'); + case KEYC_KP_ENTER: + return ('\r'); + case KEYC_KP_ZERO: + return ('0'); + case KEYC_KP_PERIOD: + return ('.'); + } + return (key); +} + /* * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key * as an emacs key; return 2 to append to the buffer. @@ -1010,6 +1068,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) *new_key = key; return (1); case '\033': /* Escape */ + case '['|KEYC_CTRL: c->prompt_mode = PROMPT_COMMAND; if (c->prompt_index != 0) c->prompt_index--; @@ -1042,6 +1101,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) c->flags |= CLIENT_REDRAWSTATUS; return (0); case '\033': /* Escape */ + case '['|KEYC_CTRL: return (0); } @@ -1383,6 +1443,9 @@ status_prompt_key(struct client *c, key_code key) } size = utf8_strlen(c->prompt_buffer); + key &= ~KEYC_MASK_FLAGS; + key = status_prompt_keypad_key(key); + if (c->prompt_flags & PROMPT_NUMERIC) { if (key >= '0' && key <= '9') goto append_key; @@ -1392,7 +1455,6 @@ status_prompt_key(struct client *c, key_code key) free(s); return (1); } - key &= ~KEYC_MASK_FLAGS; if (c->prompt_flags & (PROMPT_SINGLE|PROMPT_QUOTENEXT)) { if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) @@ -1607,6 +1669,7 @@ process_key: free(s); break; case '\033': /* Escape */ + case '['|KEYC_CTRL: case 'c'|KEYC_CTRL: case 'g'|KEYC_CTRL: if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0) diff --git a/tmux.1 b/tmux.1 index 764a9f4b..1eee1e8f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2228,6 +2228,11 @@ Turn off rectangle selection mode. .Xc Toggle rectangle selection mode. .It Xo +.Ic recentre\-top\-bottom +(emacs : C-l) +.Xc +Cycles the current line between centre, top, and bottom. +.It Xo .Ic refresh\-from\-pane (vi: r) (emacs: r) @@ -4664,6 +4669,8 @@ mouse sequences. Supports the OSC 7 working directory extension. .It overline Supports the overline SGR attribute. +.It progressbar +Supports the OSC 9;4 progress bar extension. .It rectfill Supports the DECFRA rectangle fill escape sequence. .It RGB @@ -5357,17 +5364,6 @@ section. .It Ic copy\-mode\-position\-format Ar format Format of the position indicator in copy mode. .Pp -.It Xo Ic mode\-keys -.Op Ic vi | emacs -.Xc -Use vi or emacs-style key bindings in copy mode. -The default is emacs, unless -.Ev VISUAL -or -.Ev EDITOR -contains -.Ql vi . -.Pp .It Ic copy\-mode\-position\-style Ar style Set the style of the position indicator in copy mode. For how to specify @@ -5384,6 +5380,63 @@ see the .Sx STYLES section. .Pp +.It Ic copy\-mode\-current\-line\-number\-style Ar style +Set style of current line number in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Ic copy\-mode\-line\-number\-style Ar style +Set style of line numbers in copy mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp +.It Xo Ic copy\-mode\-line\-numbers +.Op Ic off | default | absolute | relative | hybrid +.Xc +Show line numbers in copy mode. +.Ic off +hides line numbers, +.Ic default +shows line numbers matching the copy mode position indicator and +.Ic goto\-line , +.Ic absolute +shows absolute line numbers, +.Ic relative +shows line numbers relative to the cursor, and +.Ic hybrid +shows the current line number as absolute and other line numbers as +relative. +With +.Ic off +or +.Ic default , +the position indicator and +.Ic goto\-line +use the same numbering; +with +.Ic absolute , +.Ic relative +and +.Ic hybrid , +they use absolute line numbers. +.Pp +.It Xo Ic mode\-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style key bindings in copy mode. +The default is emacs, unless +.Ev VISUAL +or +.Ev EDITOR +contains +.Ql vi . +.Pp .It Ic mode\-style Ar style Set window modes style. For how to specify @@ -5628,6 +5681,17 @@ A value of 0 (the default) means no limit. When a limit is set, panes are arranged to not exceed this number of columns, with additional panes stacked in extra rows. .Pp +.It Ic tree\-mode\-preview\-format Ar format +Format of the preview indicator in tree mode. +.Pp +.It Ic tree\-mode\-preview\-style Ar style +Set the style of the preview indicator in tree mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Ic window\-status\-activity\-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -6533,6 +6597,8 @@ The following variables are available, where appropriate: .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" .It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" +.It Li "copy_position" Ta "" Ta "Position shown in the copy mode indicator" +.It Li "copy_position_limit" Ta "" Ta "Limit shown in the copy mode indicator" .It Li "current_file" Ta "" Ta "Current configuration file" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_colour" Ta "" Ta "Cursor colour in pane" @@ -7493,7 +7559,6 @@ Display a popup running when omitted) on .Ar target\-client . A popup is a rectangular box drawn over the top of any panes. -Panes are not updated while a popup is present. If the command is run inside an existing popup, that popup is modified. Only the .Fl b , @@ -8081,6 +8146,8 @@ $ printf \[aq]\e033[4 q\[aq] If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Spb +Set the state and progress for the OSC9;4 progress bar. .It Em \&Swd Set the opening sequence for the working directory notification. The sequence is terminated using the standard diff --git a/tmux.c b/tmux.c index 8d390203..251b0336 100644 --- a/tmux.c +++ b/tmux.c @@ -281,6 +281,23 @@ get_timer(void) return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL)); } +char * +clean_name(const char *name, const char* forbid) +{ + char *copy, *cp, *new_name; + + if (*name == '\0' || !utf8_isvalid(name)) + return (NULL); + copy = xstrdup(name); + for (cp = copy; *cp != '\0'; cp++) { + if (strchr(forbid, *cp) != NULL) + *cp = '_'; + } + utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + free(copy); + return (new_name); +} + const char * sig2name(int signo) { diff --git a/tmux.h b/tmux.h index 08d6d305..02e010fd 100644 --- a/tmux.h +++ b/tmux.h @@ -638,6 +638,7 @@ enum tty_code_code { TTYC_SMUL, TTYC_SMULX, TTYC_SMXX, + TTYC_SPB, TTYC_SXL, TTYC_SS, TTYC_SWD, @@ -970,8 +971,10 @@ struct image { u_int sx; u_int sy; - TAILQ_ENTRY (image) all_entry; + struct images *list; TAILQ_ENTRY (image) entry; + + TAILQ_ENTRY (image) all_entry; }; TAILQ_HEAD(images, image); #endif @@ -1005,6 +1008,7 @@ struct screen { char *title; char *path; struct screen_titles *titles; + u_int ntitles; struct grid *grid; /* grid data */ @@ -2035,6 +2039,7 @@ struct client { char *title; char *path; const char *cwd; + struct progress_bar progress_bar; char *term_name; int term_features; @@ -2144,8 +2149,8 @@ struct client { struct event message_timer; char *prompt_string; - struct format_tree *prompt_formats; struct utf8_data *prompt_buffer; + struct cmd_find_state prompt_state; char *prompt_last; size_t prompt_index; prompt_input_cb prompt_inputcb; @@ -2380,6 +2385,7 @@ int checkshell(const char *); void setblocking(int, int); char *shell_argv0(const char *, int); uint64_t get_timer(void); +char *clean_name(const char *, const char *); const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); @@ -2674,6 +2680,7 @@ void tty_repeat_requests(struct tty *, int); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_set_path(struct tty *, const char *); +void tty_set_progress_bar(struct tty *, struct progress_bar *); void tty_default_attributes(struct tty *, const struct grid_cell *, struct colour_palette *, u_int, struct hyperlinks *); void tty_update_mode(struct tty *, int, struct screen *); @@ -3009,9 +3016,9 @@ void file_write_data(struct client_files *, struct imsg *); void file_write_close(struct client_files *, struct imsg *); void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *, int, int, client_file_cb, void *); -void file_write_ready(struct client_files *, struct imsg *); -void file_read_data(struct client_files *, struct imsg *); -void file_read_done(struct client_files *, struct imsg *); +int file_write_ready(struct client_files *, struct imsg *); +int file_read_data(struct client_files *, struct imsg *); +int file_read_done(struct client_files *, struct imsg *); void file_read_cancel(struct client_files *, struct imsg *); /* server.c */ @@ -3353,14 +3360,14 @@ void screen_set_default_cursor(struct screen *, struct options *); void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *); void screen_set_cursor_colour(struct screen *, int); int screen_set_title(struct screen *, const char *); -void screen_set_path(struct screen *, const char *); +int screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_set_progress_bar(struct screen *, enum progress_bar_state, int); void screen_resize(struct screen *, u_int, u_int, int); void screen_resize_cursor(struct screen *, u_int, u_int, int, int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, - u_int, int, struct grid_cell *); + u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); @@ -3605,6 +3612,7 @@ char *window_copy_get_line(struct window_pane *, u_int); int window_copy_get_current_offset(struct window_pane *, u_int *, u_int *); char *window_copy_get_hyperlink(struct window_pane *, u_int, u_int); +void window_copy_set_line_numbers(struct window_pane *, int); /* window-option.c */ extern const struct window_mode window_customize_mode; @@ -3666,7 +3674,6 @@ struct session *session_create(const char *, const char *, const char *, void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); -char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *, struct sort_criteria *); struct session *session_previous_session(struct session *, diff --git a/tty-features.c b/tty-features.c index c620c383..74e7922f 100644 --- a/tty-features.c +++ b/tty-features.c @@ -357,6 +357,17 @@ static const struct tty_feature tty_feature_sixel = { TERM_SIXEL }; +/* Terminal supports the OSC 9;4 progress bar. */ +static const char *const tty_feature_progressbar_capabilities[] = { + "Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\", + NULL +}; +static const struct tty_feature tty_feature_progressbar = { + "progressbar", + tty_feature_progressbar_capabilities, + 0 +}; + /* Available terminal features. */ static const struct tty_feature *const tty_features[] = { &tty_feature_256, @@ -372,6 +383,7 @@ static const struct tty_feature *const tty_features[] = { &tty_feature_mouse, &tty_feature_osc7, &tty_feature_overline, + &tty_feature_progressbar, &tty_feature_rectfill, &tty_feature_rgb, &tty_feature_sixel, @@ -475,45 +487,56 @@ tty_default_features(int *feat, const char *name, u_int version) "256,RGB,bpaste,clipboard,mouse,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM "," - "ccolour," - "cstyle," - "extkeys," - "margins," - "overline," - "usstyle" + "ccolour," + "cstyle," + "extkeys," + "margins," + "overline," + "usstyle" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM "," - "ccolour," - "cstyle," - "extkeys," - "focus," - "overline," - "usstyle," - "hyperlinks" + "ccolour," + "cstyle," + "extkeys," + "focus," + "overline," + "usstyle," + "hyperlinks," + "progressbar" }, { .name = "rxvt-unicode", .features = "256," - "bpaste," - "ccolour," - "cstyle," - "mouse," - "title," - "ignorefkeys" + "bpaste," + "ccolour," + "cstyle," + "mouse," + "title," + "ignorefkeys" }, { .name = "iTerm2", .features = TTY_FEATURES_BASE_MODERN_XTERM "," - "cstyle," - "extkeys," - "margins," - "usstyle," - "sync," - "osc7,hyperlinks" + "cstyle," + "extkeys," + "margins," + "usstyle," + "sync," + "osc7," + "hyperlinks," + "progressbar" }, { .name = "foot", .features = TTY_FEATURES_BASE_MODERN_XTERM "," - "cstyle," - "extkeys" + "cstyle," + "extkeys" + }, + { .name = "WezTerm", + .features = TTY_FEATURES_BASE_MODERN_XTERM "," + "ccolour," + "cstyle," + "extkeys," + "focus," + "usstyle" }, { .name = "XTerm", /* @@ -522,10 +545,10 @@ tty_default_features(int *feat, const char *name, u_int version) * secondary DA shows VT420. */ .features = TTY_FEATURES_BASE_MODERN_XTERM "," - "ccolour," - "cstyle," - "extkeys," - "focus" + "ccolour," + "cstyle," + "extkeys," + "focus" } }; u_int i; diff --git a/tty-keys.c b/tty-keys.c index 05ec63c8..1336188d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1640,6 +1640,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, tty_default_features(features, "mintty", 0); else if (strncmp(tmp, "foot(", 5) == 0) tty_default_features(features, "foot", 0); + else if (strncmp(tmp, "WezTerm ", 7) == 0) + tty_default_features(features, "WezTerm", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); free(c->term_type); diff --git a/tty-term.c b/tty-term.c index 29dbaf59..39bfd9d6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -280,6 +280,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMULX] = { TTYCODE_STRING, "Smulx" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, + [TTYC_SPB] = { TTYCODE_STRING, "Spb" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, [TTYC_SWD] = { TTYCODE_STRING, "Swd" }, [TTYC_SYNC] = { TTYCODE_STRING, "Sync" }, diff --git a/tty.c b/tty.c index 30436e92..2079bffd 100644 --- a/tty.c +++ b/tty.c @@ -3315,3 +3315,10 @@ tty_clipboard_query(struct tty *tty) evtimer_add(&tty->clipboard_timer, &tv); } } + +void +tty_set_progress_bar(struct tty *tty, struct progress_bar *pb) +{ + if (tty_term_has(tty->term, TTYC_SPB)) + tty_putcode_ii(tty, TTYC_SPB, pb->state, pb->progress); +} diff --git a/window-clock.c b/window-clock.c index ecac43ec..27bde027 100644 --- a/window-clock.c +++ b/window-clock.c @@ -279,6 +279,7 @@ window_clock_draw_screen(struct window_mode_entry *wme) memcpy(&gc, &grid_default_cell, sizeof gc); gc.flags |= GRID_FLAG_NOPALETTE; gc.bg = colour; + gc.fg = colour; for (ptr = tim; *ptr != '\0'; ptr++) { if (*ptr >= '0' && *ptr <= '9') idx = *ptr - '0'; @@ -299,7 +300,7 @@ window_clock_draw_screen(struct window_mode_entry *wme) for (i = 0; i < 5; i++) { screen_write_cursormove(&ctx, x + i, y + j, 0); if (window_clock_table[idx][j][i]) - screen_write_putc(&ctx, &gc, ' '); + screen_write_putc(&ctx, &gc, '#'); } } x += 6; diff --git a/window-copy.c b/window-copy.c index d5d2b0de..65c14aae 100644 --- a/window-copy.c +++ b/window-copy.c @@ -52,6 +52,12 @@ static void window_copy_redraw_lines(struct window_mode_entry *, u_int, u_int); static void window_copy_redraw_screen(struct window_mode_entry *); static void window_copy_style_changed(struct window_mode_entry *); +static int window_copy_line_number_mode(struct window_mode_entry *); +static int window_copy_line_number_is_absolute(struct window_mode_entry *); +static int window_copy_line_numbers_active(struct window_mode_entry *); +static u_int window_copy_line_number_width(struct window_mode_entry *); +static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int, u_int); +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_lines(struct window_mode_entry *, @@ -207,6 +213,14 @@ enum window_copy_cmd_clear { WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, }; +enum window_copy_line_numbers { + WINDOW_COPY_LINE_NUMBERS_OFF, + WINDOW_COPY_LINE_NUMBERS_DEFAULT, + WINDOW_COPY_LINE_NUMBERS_ABSOLUTE, + WINDOW_COPY_LINE_NUMBERS_RELATIVE, + WINDOW_COPY_LINE_NUMBERS_HYBRID, +}; + struct window_copy_cmd_state { struct window_mode_entry *wme; struct args *args; @@ -264,6 +278,7 @@ struct window_copy_mode_data { int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ int hide_position; /* hide position marker */ + int line_numbers; enum { SEL_CHAR, /* select one char at a time */ @@ -271,6 +286,13 @@ struct window_copy_mode_data { SEL_LINE, /* select one line at a time */ } selflag; + enum { + RECENTRE_TOP, + RECENTRE_MIDDLE, + RECENTRE_BOTTOM, + } recentre_state; + u_int recentre_line; + const char *separators; /* word separators */ u_int dx; /* drag start position */ @@ -427,6 +449,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->jumptype = WINDOW_COPY_OFF; data->jumpchar = NULL; + data->line_numbers = 1; screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); screen_set_default_cursor(&data->screen, global_w_options); @@ -463,9 +486,12 @@ window_copy_init(struct window_mode_entry *wme, data->scroll_exit = args_has(args, 'e'); data->hide_position = args_has(args, 'H'); - if (base->hyperlinks != NULL) + if (base->hyperlinks != NULL) { + hyperlinks_free(data->screen.hyperlinks); data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks); - data->screen.cx = data->cx; + } + data->screen.cx = window_copy_cursor_offset(wme, data->cx, + screen_size_x(&data->screen)); data->screen.cy = data->cy; data->mx = data->cx; data->my = screen_hsize(data->backing) + data->cy - data->oy; @@ -474,9 +500,13 @@ window_copy_init(struct window_mode_entry *wme, screen_write_start(&ctx, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) window_copy_write_line(wme, &ctx, i); - screen_write_cursormove(&ctx, data->cx, data->cy, 0); + screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx, + screen_size_x(&data->screen)), data->cy, 0); screen_write_stop(&ctx); + data->recentre_state = RECENTRE_MIDDLE; + data->recentre_line = 0; + return (&data->screen); } @@ -491,6 +521,7 @@ window_copy_view_init(struct window_mode_entry *wme, data = window_copy_common_init(wme); data->viewmode = 1; + data->line_numbers = 0; data->backing = xmalloc(sizeof *data->backing); screen_init(data->backing, sx, screen_size_y(base), UINT_MAX); @@ -937,12 +968,22 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; u_int hsize = screen_hsize(data->backing); + u_int position, limit; struct grid_line *gl; gl = grid_get_line(data->backing->grid, hsize - data->oy); format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time); format_add(ft, "scroll_position", "%d", data->oy); + if (window_copy_line_number_is_absolute(wme)) { + position = hsize - data->oy + 1; + limit = hsize + screen_size_y(data->backing); + } else { + position = data->oy; + limit = hsize; + } + format_add(ft, "copy_position", "%u", position); + format_add(ft, "copy_position_limit", "%u", limit); format_add(ft, "rectangle_toggle", "%d", data->rectflag); format_add(ft, "copy_cursor_x", "%d", data->cx); @@ -2769,6 +2810,62 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_REDRAW); } +static enum window_copy_cmd_action +window_copy_cmd_recentre_top_bottom(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int cy = data->cy, oy = data->oy; + u_int sy = screen_size_y(&data->screen) - 1; + u_int sm = sy / 2, backing_row; + enum { MIDDLE, TOP, BOTTOM } target; + + backing_row = screen_hsize(data->backing) + cy - data->oy; + if (data->recentre_line != backing_row) { + data->recentre_state = RECENTRE_MIDDLE; + data->recentre_line = backing_row; + } + + switch (data->recentre_state) { + case RECENTRE_MIDDLE: + data->recentre_state = RECENTRE_TOP; + target = MIDDLE; + break; + case RECENTRE_TOP: + data->recentre_state = RECENTRE_BOTTOM; + target = TOP; + break; + case RECENTRE_BOTTOM: + default: + data->recentre_state = RECENTRE_MIDDLE; + target = BOTTOM; + break; + } + + oy = data->oy; + switch (target) { + case MIDDLE: + if (cy < sm) + window_copy_scroll_down(wme, sm - cy); + else if (cy > sm) + window_copy_scroll_up(wme, cy - sm); + if (data->oy != oy) + data->cy = cy + (data->oy - oy); + break; + case TOP: + window_copy_scroll_up(wme, cy); + data->cy = cy - (oy - data->oy); + break; + case BOTTOM: + window_copy_scroll_down(wme, sy - cy); + data->cy = cy + (data->oy - oy); + break; + } + window_copy_update_selection(wme, 0, 0); + + return (WINDOW_COPY_CMD_REDRAW); +} + static const struct { const char *command; u_int minargs; @@ -3153,6 +3250,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, .f = window_copy_cmd_previous_word }, + { .command = "recentre-top-bottom", + .args = { "", 0, 0, NULL }, + .flags = WINDOW_COPY_CMD_FLAG_READONLY, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_recentre_top_bottom + }, { .command = "rectangle-on", .args = { "", 0, 0, NULL }, .flags = 0, @@ -4471,15 +4574,28 @@ window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) { struct window_copy_mode_data *data = wme->data; const char *errstr; + u_int hsize = screen_hsize(data->backing); + u_int line; int lineno; lineno = strtonum(linestr, -1, INT_MAX, &errstr); if (errstr != NULL) return; - if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) - lineno = screen_hsize(data->backing); - data->oy = lineno; + if (window_copy_line_number_is_absolute(wme)) { + if (lineno <= 0) + line = 1; + else if ((u_int)lineno > hsize + 1) + line = hsize + 1; + else + line = lineno; + data->oy = hsize - (line - 1); + } else { + if (lineno < 0 || (u_int)lineno > hsize) + lineno = hsize; + data->oy = lineno; + } + window_copy_update_selection(wme, 1, 0); window_copy_redraw_screen(wme); } @@ -4628,7 +4744,7 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, static void window_copy_write_one(struct window_mode_entry *wme, - struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx, + struct screen_write_ctx *ctx, u_int px, u_int py, u_int fy, u_int nx, const struct grid_cell *mgc, const struct grid_cell *cgc, const struct grid_cell *mkgc) { @@ -4637,7 +4753,7 @@ window_copy_write_one(struct window_mode_entry *wme, struct grid_cell gc; u_int fx; - screen_write_cursormove(ctx, 0, py, 0); + screen_write_cursormove(ctx, px, py, 0); for (fx = 0; fx < nx; fx++) { grid_get_cell(gd, fx, fy, &gc); if (fx + gc.data.width <= nx) { @@ -4648,6 +4764,116 @@ window_copy_write_one(struct window_mode_entry *wme, } } +static int +window_copy_line_number_mode(struct window_mode_entry *wme) +{ + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + struct options *oo = wp->window->options; + + if (!data->line_numbers) + return (WINDOW_COPY_LINE_NUMBERS_OFF); + return (options_get_number(oo, "copy-mode-line-numbers")); +} + +static int +window_copy_line_number_is_absolute(struct window_mode_entry *wme) +{ + switch (window_copy_line_number_mode(wme)) { + case WINDOW_COPY_LINE_NUMBERS_ABSOLUTE: + case WINDOW_COPY_LINE_NUMBERS_RELATIVE: + case WINDOW_COPY_LINE_NUMBERS_HYBRID: + return (1); + case WINDOW_COPY_LINE_NUMBERS_OFF: + case WINDOW_COPY_LINE_NUMBERS_DEFAULT: + return (0); + } + fatalx("bad line number mode"); +} + +static int +window_copy_line_numbers_active(struct window_mode_entry *wme) +{ + return (window_copy_line_number_mode(wme) != + WINDOW_COPY_LINE_NUMBERS_OFF); +} + +static u_int +window_copy_line_number_width(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + u_int lines, digits; + + if (!window_copy_line_numbers_active(wme)) + return (0); + + lines = screen_hsize(data->backing) + screen_size_y(data->backing) + 1; + digits = 1; + while (lines >= 10) { + lines /= 10; + digits++; + } + if (digits < 3) + digits = 3; + return (digits + 1); +} + +static u_int +window_copy_cursor_offset(struct window_mode_entry *wme, u_int cx, u_int sx) +{ + u_int width = window_copy_line_number_width(wme); + u_int content; + + if (width == 0) + return (cx); + if (width >= sx) + content = 1; + else + content = sx - width; + if (cx >= content) + return (sx - 1); + return (width + cx); +} + +static u_int +window_copy_cursor_unoffset(struct window_mode_entry *wme, u_int vx, u_int sx) +{ + u_int width = window_copy_line_number_width(wme); + u_int content; + + if (width == 0) + return (vx); + if (width >= sx) + content = 1; + else + content = sx - width; + if (vx < width) + return (0); + vx -= width; + if (vx >= content) + return (content - 1); + return (vx); +} + +void +window_copy_set_line_numbers(struct window_pane *wp, int enabled) +{ + struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); + struct window_copy_mode_data *data; + + if (wme == NULL) + return; + if (wme->mode != &window_copy_mode) + return; + data = wme->data; + if (data == NULL) + return; + if (data->line_numbers == enabled) + return; + data->line_numbers = enabled; + window_copy_redraw_screen(wme); +} + int window_copy_get_current_offset(struct window_pane *wp, u_int *offset, u_int *size) @@ -4673,12 +4899,23 @@ window_copy_write_line(struct window_mode_entry *wme, struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; - struct grid_cell gc, mgc, cgc, mkgc; + struct grid_cell gc, mgc, cgc, mkgc, ln_gc, cur_ln_gc; u_int sx = screen_size_x(s); u_int hsize = screen_hsize(data->backing); + u_int width; + u_int absolute, line_number, content_sx; const char *value; char *expanded; struct format_tree *ft; + int current, mode; + + width = window_copy_line_number_width(wme); + if (width >= sx) + content_sx = 1; + else if (width != 0) + content_sx = sx - width; + else + content_sx = sx; ft = format_create_defaults(NULL, NULL, NULL, NULL, wp); @@ -4690,24 +4927,52 @@ window_copy_write_line(struct window_mode_entry *wme, cgc.flags |= GRID_FLAG_NOPALETTE; style_apply(&mkgc, oo, "copy-mode-mark-style", ft); mkgc.flags |= GRID_FLAG_NOPALETTE; + if (width != 0) { + style_apply(&ln_gc, oo, "copy-mode-line-number-style", ft); + ln_gc.flags |= GRID_FLAG_NOPALETTE; + style_apply(&cur_ln_gc, oo, + "copy-mode-current-line-number-style", ft); + cur_ln_gc.flags |= GRID_FLAG_NOPALETTE; + current = (py == data->cy); + absolute = hsize - data->oy + py + 1; + mode = window_copy_line_number_mode(wme); + if (mode == WINDOW_COPY_LINE_NUMBERS_DEFAULT) { + if (py < data->oy) + line_number = data->oy - py; + else + line_number = py - data->oy; + } else if (mode == WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) + line_number = absolute; + else if (mode == WINDOW_COPY_LINE_NUMBERS_HYBRID && current) + line_number = absolute; + else if (py > data->cy) + line_number = py - data->cy; + else + line_number = data->cy - py; + screen_write_cursormove(ctx, 0, py, 0); + screen_write_nputs(ctx, width, current ? &cur_ln_gc : &ln_gc, + "%*u ", (int)width - 1, line_number); + } - window_copy_write_one(wme, ctx, py, hsize - data->oy + py, - screen_size_x(s), &mgc, &cgc, &mkgc); + window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py, + content_sx, &mgc, &cgc, &mkgc); if (py == 0 && s->rupper < s->rlower && !data->hide_position) { value = options_get_string(oo, "copy-mode-position-format"); if (*value != '\0') { expanded = format_expand(ft, value); if (*expanded != '\0') { - screen_write_cursormove(ctx, 0, 0, 0); - format_draw(ctx, &gc, sx, expanded, NULL, 0); + screen_write_cursormove(ctx, width, 0, 0); + format_draw(ctx, &gc, content_sx, expanded, + NULL, 0); } free(expanded); } } - if (py == data->cy && data->cx == screen_size_x(s)) { - screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0); + if (py == data->cy && data->cx >= content_sx) { + screen_write_cursormove(ctx, window_copy_cursor_offset(wme, + data->cx, screen_size_x(s)), py, 0); screen_write_putc(ctx, &grid_default_cell, '$'); } @@ -4757,13 +5022,28 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; - struct screen_write_ctx ctx; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; u_int i; + 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); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), + data->cy, 0); + screen_write_stop(&ctx); + wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR); + return; + } + screen_write_start_pane(&ctx, wp, NULL); for (i = py; i < py + ny; i++) window_copy_write_line(wme, &ctx, i); - screen_write_cursormove(&ctx, data->cx, data->cy, 0); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy, + 0); screen_write_stop(&ctx); wp->flags |= PANE_REDRAWSCROLLBAR; @@ -4882,17 +5162,42 @@ window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct screen_write_ctx ctx; - u_int old_cx, old_cy; + u_int old_cx, old_cy, width, content_sx; old_cx = data->cx; old_cy = data->cy; data->cx = cx; data->cy = cy; + if (window_copy_line_numbers_active(wme)) { + width = window_copy_line_number_width(wme); + + if (s->sel != NULL || data->lineflag != LINE_SEL_NONE || + old_cy != data->cy) { + window_copy_redraw_screen(wme); + return; + } + if (width >= screen_size_x(s)) + content_sx = 1; + else + content_sx = screen_size_x(s) - width; + if (old_cx >= content_sx || data->cx >= content_sx) { + window_copy_redraw_screen(wme); + return; + } + screen_write_start_pane(&ctx, wp, NULL); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), + data->cy, 0); + screen_write_stop(&ctx); + return; + } if (old_cx == screen_size_x(s)) window_copy_redraw_lines(wme, old_cy, 1); if (data->cx == screen_size_x(s)) window_copy_redraw_lines(wme, data->cy, 1); else { screen_write_start_pane(&ctx, wp, NULL); - screen_write_cursormove(&ctx, data->cx, data->cy, 0); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), + data->cy, 0); screen_write_stop(&ctx); } } @@ -4967,7 +5272,7 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, struct screen *s = &data->screen; struct options *oo = wp->window->options; struct grid_cell gc; - u_int sx, sy, cy, endsx, endsy; + u_int sx, sy, cy, endsx, endsy, clipx; int startrelpos, endrelpos; struct format_tree *ft; @@ -4995,8 +5300,15 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw, style_apply(&gc, oo, "copy-mode-selection-style", ft); gc.flags |= GRID_FLAG_NOPALETTE; format_free(ft); + clipx = window_copy_line_number_width(wme); + if (clipx >= screen_size_x(s)) + clipx = screen_size_x(s) - 1; + if (window_copy_line_numbers_active(wme)) { + sx = window_copy_cursor_offset(wme, sx, screen_size_x(s)); + endsx = window_copy_cursor_offset(wme, endsx, screen_size_x(s)); + } screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, - data->modekeys, &gc); + clipx, data->modekeys, &gc); if (data->rectflag && may_redraw) { /* @@ -5152,12 +5464,20 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, { struct window_pane *wp = wme->wp; struct screen_write_ctx ctx; + int redraw = 0; if (set_clip && options_get_number(global_options, "set-clipboard") != 0) { + if (window_copy_line_numbers_active(wme) && + (wp->flags & PANE_REDRAW)) { + /* Clear PANE_REDRAW so clipboard write not skipped. */ + redraw = PANE_REDRAW; + wp->flags &= ~PANE_REDRAW; + } screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, "", buf, len); screen_write_stop(&ctx); + wp->flags |= redraw; notify_pane("pane-set-clipboard", wp); } @@ -5180,7 +5500,8 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s, if (cmd != NULL && *cmd != '\0') { job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT, -1, -1); - bufferevent_write(job_get_event(job), buf, *len); + if (job != NULL) + bufferevent_write(job_get_event(job), buf, *len); } return (buf); } @@ -5930,6 +6251,32 @@ 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); + if (window_copy_line_numbers_active(wme)) { + if (window_copy_line_number_mode(wme) != + WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) { + window_copy_redraw_screen(wme); + return; + } + screen_write_start(&ctx, &data->screen); + screen_write_cursormove(&ctx, 0, 0, 0); + screen_write_deleteline(&ctx, ny, 8); + window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); + window_copy_write_line(wme, &ctx, 0); + if (screen_size_y(s) > 1) + window_copy_write_line(wme, &ctx, 1); + if (screen_size_y(s) > 3) + window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); + if (s->sel != NULL && screen_size_y(s) > ny) { + window_copy_write_line(wme, &ctx, + screen_size_y(s) - ny - 1); + } + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), + data->cy, 0); + screen_write_stop(&ctx); + wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR); + return; + } screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -5942,7 +6289,9 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); if (s->sel != NULL && screen_size_y(s) > ny) window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); - screen_write_cursormove(&ctx, data->cx, data->cy, 0); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy, + 0); screen_write_stop(&ctx); wp->flags |= PANE_REDRAWSCROLLBAR; } @@ -5967,6 +6316,27 @@ 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); + if (window_copy_line_numbers_active(wme)) { + if (window_copy_line_number_mode(wme) != + WINDOW_COPY_LINE_NUMBERS_ABSOLUTE) { + window_copy_redraw_screen(wme); + return; + } + screen_write_start(&ctx, &data->screen); + screen_write_cursormove(&ctx, 0, 0, 0); + screen_write_insertline(&ctx, ny, 8); + window_copy_write_lines(wme, &ctx, 0, ny); + if (s->sel != NULL && screen_size_y(s) > ny) + window_copy_write_line(wme, &ctx, ny); + else if (ny == 1) + window_copy_write_line(wme, &ctx, 1); + screen_write_cursormove(&ctx, + window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), + data->cy, 0); + screen_write_stop(&ctx); + wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR); + return; + } screen_write_start_pane(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); @@ -5976,7 +6346,8 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) window_copy_write_line(wme, &ctx, ny); else if (ny == 1) /* nuke position */ window_copy_write_line(wme, &ctx, 1); - screen_write_cursormove(&ctx, data->cx, data->cy, 0); + screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx, + screen_size_x(s)), data->cy, 0); screen_write_stop(&ctx); wp->flags |= PANE_REDRAWSCROLLBAR; } @@ -6004,6 +6375,7 @@ window_copy_move_mouse(struct mouse_event *m) struct window_pane *wp; struct window_mode_entry *wme; u_int x, y; + struct window_copy_mode_data *data; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL) @@ -6017,6 +6389,8 @@ window_copy_move_mouse(struct mouse_event *m) if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; + data = wme->data; + x = window_copy_cursor_unoffset(wme, x, screen_size_x(&data->screen)); window_copy_update_cursor(wme, x, y); } @@ -6047,6 +6421,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) c->tty.mouse_drag_release = window_copy_drag_release; data = wme->data; + 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) data->selflag = SEL_CHAR; @@ -6104,6 +6479,7 @@ window_copy_drag_update(struct client *c, struct mouse_event *m) if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; + x = window_copy_cursor_unoffset(wme, x, screen_size_x(&data->screen)); old_cx = data->cx; old_cy = data->cy; @@ -6141,6 +6517,8 @@ window_copy_drag_release(struct client *c, struct mouse_event *m) return; data = wme->data; + if (window_copy_line_numbers_active(wme)) + window_copy_drag_update(c, m); evtimer_del(&data->dragtimer); } diff --git a/window-tree.c b/window-tree.c index ed3c9d69..0d6ca553 100644 --- a/window-tree.c +++ b/window-tree.c @@ -411,44 +411,49 @@ static void window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, u_int sx, u_int sy, const struct grid_cell *gc, const char *label) { - size_t len; - u_int ox, oy; + u_int width, ox, oy; + char *new_label = NULL; - len = strlen(label); - if (sx == 0 || sy == 1 || len > sx) + if (sx < 5 || sy < 3) return; - ox = (sx - len + 1) / 2; + width = format_width(label); + if (width > sx - 4) { + label = new_label = format_trim_left(label, sx - 4); + width = format_width(new_label); + } + if (width == 0) + return; + ox = (sx - width + 1) / 2; oy = (sy + 1) / 2; - if (ox > 1 && ox + len < sx - 1 && sy >= 3) { - screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); - screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL, - NULL); - } + screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0); + screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT, + NULL, NULL); + screen_write_cursormove(ctx, px + ox - 1, py + oy, 0); + screen_write_clearcharacter(ctx, width + 2, 8); screen_write_cursormove(ctx, px + ox, py + oy, 0); - screen_write_puts(ctx, gc, "%s", label); + format_draw(ctx, gc, width, label, NULL, 0); + free(new_label); } static void window_tree_draw_session(struct window_tree_modedata *data, struct session *s, struct screen_write_ctx *ctx, u_int sx, u_int sy) { - struct options *oo = s->options; struct winlink *wl; struct window *w; 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; struct grid_cell gc; - int colour, active_colour, left, right; + int left, right; char *label; + const char *format; + struct format_tree *ft; + struct options *oo; total = winlink_count(&s->windows); - memcpy(&gc, &grid_default_cell, sizeof gc); - colour = options_get_number(oo, "display-panes-colour"); - active_colour = options_get_number(oo, "display-panes-active-colour"); - if (sx / total < 24) { visible = sx / 24; if (visible == 0) @@ -528,11 +533,13 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, continue; } w = wl->window; + oo = w->options; - if (wl == s->curw) - gc.fg = active_colour; - else - gc.fg = colour; + ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0); + format_defaults(ft, NULL, s, wl, NULL); + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, oo, "tree-mode-preview-style", ft); if (left) offset = 3 + (i * each); @@ -546,17 +553,19 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &w->active->base, width, sy); - xasprintf(&label, " %u:%s ", wl->idx, w->name); - if (strlen(label) > width) { + format = options_get_string(oo, "tree-mode-preview-format"); + if (*format != '\0') { + label = format_expand(ft, format); + if (*label != '\0') { + window_tree_draw_label(ctx, cx + offset, cy, + width, sy, &gc, label); + } free(label); - xasprintf(&label, " %u ", wl->idx); } - window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, - label); - free(label); if (loop != end - 1) { - screen_write_cursormove(ctx, cx + offset + width, cy, 0); + screen_write_cursormove(ctx, cx + offset + width, cy, + 0); screen_write_vline(ctx, sy, 0, 0); } loop++; @@ -567,23 +576,22 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, static void window_tree_draw_window(struct window_tree_modedata *data, struct session *s, - struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) + struct winlink *wl, struct screen_write_ctx *ctx, u_int sx, u_int sy) { - struct options *oo = s->options; + struct window *w = wl->window; 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, pane_idx; + u_int current, start, end, remaining, i; struct grid_cell gc; - int colour, active_colour, left, right; + int left, right; char *label; + const char *format; + struct format_tree *ft; + struct options *oo; total = window_count_panes(w, 1); - memcpy(&gc, &grid_default_cell, sizeof gc); - colour = options_get_number(oo, "display-panes-colour"); - active_colour = options_get_number(oo, "display-panes-active-colour"); - if (sx / total < 24) { visible = sx / 24; if (visible == 0) @@ -662,11 +670,13 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, loop++; continue; } + oo = wp->options; - if (wp == w->active) - gc.fg = active_colour; - else - gc.fg = colour; + ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0); + format_defaults(ft, NULL, s, wl, wp); + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, oo, "tree-mode-preview-style", ft); if (left) offset = 3 + (i * each); @@ -680,15 +690,19 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &wp->base, width, sy); - if (window_pane_index(wp, &pane_idx) != 0) - pane_idx = loop; - xasprintf(&label, " %u ", pane_idx); - window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, - label); - free(label); + format = options_get_string(oo, "tree-mode-preview-format"); + if (*format != '\0') { + label = format_expand(ft, format); + if (*label != '\0') { + window_tree_draw_label(ctx, cx + offset, cy, + width, sy, &gc, label); + } + free(label); + } if (loop != end - 1) { - screen_write_cursormove(ctx, cx + offset + width, cy, 0); + screen_write_cursormove(ctx, cx + offset + width, cy, + 0); screen_write_vline(ctx, sy, 0, 0); } loop++; @@ -703,10 +717,10 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, { struct window_tree_itemdata *item = itemdata; struct session *sp; - struct winlink *wlp; + struct winlink *wl; struct window_pane *wp; - window_tree_pull_item(item, &sp, &wlp, &wp); + window_tree_pull_item(item, &sp, &wl, &wp); if (wp == NULL) return; @@ -717,7 +731,7 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, window_tree_draw_session(modedata, sp, ctx, sx, sy); break; case WINDOW_TREE_WINDOW: - window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); + window_tree_draw_window(modedata, sp, wl, ctx, sx, sy); break; case WINDOW_TREE_PANE: screen_write_preview(ctx, &wp->base, sx, sy); diff --git a/window.c b/window.c index 9a856b3f..f6141170 100644 --- a/window.c +++ b/window.c @@ -407,9 +407,14 @@ window_remove_ref(struct window *w, const char *from) void window_set_name(struct window *w, const char *new_name) { - free(w->name); - utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); - notify_window("window-renamed", w); + char *name; + + name = clean_name(new_name, "#"); + if (name != NULL) { + free(w->name); + w->name = name; + notify_window("window-renamed", w); + } } void @@ -2132,13 +2137,15 @@ struct style_range * window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) { struct style_ranges *srs; - struct window *w = wp->window; - struct options *wo = w->options; + struct window *w; + struct options *wo; u_int line; int pane_status; if (wp == NULL) return (NULL); + w = wp->window; + wo = w->options; srs = &wp->border_status_line.ranges; pane_status = options_get_number(wo, "pane-border-status");