diff --git a/CHANGES b/CHANGES index cf125fd9..cb92e2ff 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,332 @@ +CHANGES FROM 3.6b TO 3.7 + +* Add floating panes. These are panes which sit above the layout ("tiled + panes") like popups but unlike popups are not modal and behave like panes (so + the same escape sequence support). Floating panes are created with the + new-pane command, bound to * by default. + + Currently floating panes can only be moved and resized using the mouse. The + default second status line (if status-format is set to 2) has changed to show + a list of panes. Many obvious features are not yet available for floating + panes (notably the ability to swap floating panes, resize them using + resize-pane, change them between floating and tiles, and restore custom + layouts with floating panes). + + Mostly written by Michael Grant with help from Dane Jensen; testing and fixes + from others. + +* Allow run-shell arguments after a shell command to be expanded as #{1}, #{2} + and so on (from Rasmus Thystrup Karstensen in issue 5121). + +* Add -g to kill-session to kill all sessions in a session group (issue 5157 + from github at jiku dot jp). + +* Tighten up read-only checks on attach-session, detach-client and + switch-client so that a user should be able to only detach their own client + (reported by John Walker). + +* Increase escape delay if the buffer contains a partial paste end, fixes + issues with at least Windows Terminal (from jing dot empty at gmail.com issue + 5088). + +* When mode-keys is set to vi, do not allow the cursor to go into the invisible + extra cell to the right of the visible text; this is closer to what vi(1) + does (from Max Vim in issue 5070). + +* Add a five second limit on pasting for terminals which mysteriously lose the + end sequence if the paste is too big (that is, Terminal.app) (reported by + Garri Djavadyan in issue 4527). + +* Show file open errors more sensibly (reported by Meriel Luna Mittelbach in + issue 5081). + +* Update supported features list for Foot terminal (from Meriel Luna Mittelbach + in issue 5079). + +* Turn off the "is this a paste" guessing if the terminal supports bracket + pasting instead (issue 5031). + +* Check FIONREAD for all panes not just piped panes. + +* Add emacs-style recentre-top-bottom command to copy mode (issue 5053 from + sinyax75 at gmail dot com). + +* Allow the indicator in tree mode to be customized by two new options: + tree-mode-preview-format and tree-mode-preview-style. + +* Fix control client hang on exit after toggling no-output (issue 5049 from + Aaron Campbell). Also various other control mode fixes. + +* Add support for line numbers in copy mode. There is a new + copy-mode-line-numbers option which may be set to off, default (tmux's normal + line numbering where 0 is the top visible line), absolute (first line in + history is 1), relative (relative to the cursor) and hybrid (current line is + absolute, others relative). Also adds copy-mode-line-number-style and + copy-mode-current-line-number-style to set the style of the line numbers. + When copy mode is entered with the mouse, line numbers stay off. From Leo + Henon in issue 5025. + +* Make C-[ have the same bindings as Escape for terminals with extended + keys where they are different (issue 5035 from Eric Nicolas). + +* Sanitize paste buffer names in paste_set and paste_rename (issue 5032 from + Barrett Ruth). + +* Do not hang in run-shell when job_run fails (from Barrett Ruth in issue + 5037). + +* Add ability to forward progress bar to outside terminal (issue 4972 + from Eric Dorland). + +* Translate keypad keys to text in prompt input (from Barrett Ruth in issue + 4996). + +* Sanitize pane titles and window and session names more consistently and + strictly, prevents C0 characters and other invisible characters causing + problems (reported by Chris Monardo in issue 4999). + +* Make clock visible on terminals without colours (from Manuel Einfalt in issue + 5001). + +* Add detach to default session menu (suggested by Przemyslaw Sztoch). + +* Include window format variables for pane notifications (issue 5007 from Saul + Nogueras). + +* Limit precision to 100 for formats to stop silly formats from running out of + memory, reported by z1281552865 at gmail dot com. Also various other similar + changes, mostly found by OSS-Fuzz. + +* Add WAYLAND_DISPLAY to default update-environment (issue 4965). Also some + additional XDG_* variables (issue 5169). + +* Add -C flag to command-prompt to match display-message -C (do not freeze + panes) (from Barrett Ruth in issue 4978). + +* Cache user from getpwuid because it can be very expensive on some + platforms (from Ben Maurer in issue 4973). + +* Add remain-on-exit key to keep pane around until a key is pressed (from + Michael Grant). + +* Add some new mouse ranges called "control0" to "control9" and use to add some + mouse controls to the pane state line (from Dane Jensen with some bits from + Michael Grant). + +* Handle OSC 9;4 progress bar sequence and store in format variables (from Eric + Dorland in issue 4954). + +* Correctly size buffer used for parsing clipboard sequences (from Michal + Majchrowicz). + +* Limit MSG_COMMAND argument to between 0 and 1000 to prevent a misbehaving + client from crashing the server (from Michal Majchrowicz). + +* Reorganize host keys are represented internally so they can be built more + easily (from Dane Jensen in issue 4953). + +* Add new fuzzers for command parsing, formats and styles (from David + Korczynski in issue 4957). Also fix various issues shown from these. + +* Add bracket_paste_flag format flag (from George Nachman in issue 4951). + +* Use \- for hyphens in tmux.1 to cause newer groff versions to render them + correctly (from Keith Thompson in issue 4948). + +* Various minor code improvements and fixes from Pavel Lavrukhin (issue 4936 + and others). + +* Use window options for cursor-style to avoid crash when no pane (from Arden + Packeer in issue 4942). + +* Fix issue where popup window gets overwritten by background updates (from + Conor Taylor in issue 4920). + +* Protect against overflow when scrollbar is off screen (from san65384 at gmail + dot com in issue 4933). + +* Copy hyperlinks when redrawing popup so they do not vanish (from Antoine + Gaudreau Simard in issue 4925). + +* Work around systemd killing panes early during system shutdown by creating + dependencies from the panes to the service which started tmux (issue 4926 + from Dmitry Torokhov). + +* Allow codepoint-widths to accept ranges (from san65384 at gmail dot com in + issue 4930). + +* Add a short builtin help text for each mode accessible with C-h (based on + code from Patrick Motard in issue 4751). + +* Draw message as one format, allowing prompts and messages to occupy only a + portion of the status bar, overlaying the normal status content rather than + replacing the entire line. A new message-format option now controls the + entire message (like status-format). The message-style option now need to + include "fill" in order to cover the whole width (the default has + "fill=yellow"). From Conor Taylor in issue 4861. + +* Add next/previous variables for windows in W: loop (from Conor Taylor in + issue 4856). + +* Various bug and memory leak fixes from Renaud Allard (issue 4916). + +* Add pane_pipe_pid with pipe file descriptor. + +* Make -c work with new-session -A (from Jody Frankowski in issue 4906). + +* Allow copy mode to work for readonly clients, except for copy commands (from + Dane Jensen). + +* Pass paste buffer through vis(3) when pasting to prevent buffers containing + for example the bracket end sequence causing issues, a new -S flag disables + (reported by Mason Davis). + +* Add sorting (-O flag) and a custom format (-F) to list-keys (from Dane Jensen + in issue 4845). + +* Add scroll-exit-on, scroll-exit-off, scroll-exit-toggle commands to copy mode + (from xcdnlgd at hotmail dot com in issue 4884). + +* Respond to DECRQM 2026 (from David Turnbull in issue 4887) and various others + (from Ayman Bagabas in issue 5118). + +* Fix various memory leaks reported by Huihui Huang (issue 4872). + +* Pass which clipboard is set through to the terminal (from Axel Lindskog in + issue 4858). + +* Reuse extended entry when clearing RGB cell, to prevent memory growth when + cells are repeatedly cleared (from Michael K Darling in issue 4862). + +* Do not write before buffer when parsing empty clipboard or palette replies, + or try to allocate zero bytes with an empty clipboard sequence (reported by + DongHan Kim). + +* Various bug fixes and code improvements from Conor Taylor (issue 4848). + +* Clear search counts when clearing marks in case of repeated search (reported + by Daniel Pereira in issue 4817). + +* Make OSC 52 work in popups (from gogongxt at 163 dot com in issue 4797). + +* Refresh copy mode when style changes (from Josh Cooper in issue 4830). + +* Make sorting code common and add -O for sorting to the list commands (from + Dane Jensen in issue 4813). + +* Do not treat cells as empty unless the background colour stays the same, + fixes invisible clock in clock mode (reported by Theo Buehler). + +* When history-limit is changed, apply to existing panes, not just new + ones (issue 4705). + +* Reevaluate menu and popup styles on each draw to allow them to change when + options change (from Josh Cooper in issues 4828 and 4829). + +* Handle theme keys earlier so they are processed even if a popup is open (from + Josh Cooper in issue 4827). + +* Fix window-size=latest not resizing on switch-client in session groups (from + Ilya Grigoriev in issue 4818). + +* Add -e flag to command-prompt to close if empty (from Dane Jensen in issue + 4812). + +* Correctly draw indicators when pane-border-indicators is set to both + (reported by Ilya Grigoriev in issue 4780). + +* Remember last pane or type of location for double and triple clicks and + correctly handle it changes between first and second or second and third + (issue 4795 from Shaobo Song). + +* Add paste to the default pane menu (issue 4763). + +* Reduce request timeout to 500 milliseconds to match the extended escape-time, + and discard palette requests if receiving a reply for a different index. + +* Extend escape timeout if there are active forwarded requests not just + tmux's own requests (issue 4793). + +* Correct redrawing of wide characters when overwritten (reported by Jake + Stewart in issue 4737). + +* If cannot find a terminator for palette responses, treat as a partial key not + complete (issue 4749). + +* Do not send theme unless it has changed, and do not send immediately when + updates are enabled (issue 5787). + +* Do not use ;;s in list-keys output as it is confusing and cannot be + parsed on input (from Patrick Motard in issue 4750). + +* Redraw pane borders when entering or leaving alternate screen (from Mike + Jonkmans in issue 4788). + +* Add focus-follows-mouse option (from Barry Wasdell in issue 4771). + +* Add selection_mode format variable for copy mode (from Mike Jonkmans in issue + 4773). + +* Add prompt-command-cursor-style (from Joshua Cooper in issue 4765). + +* With status-keys vi, move the cursor left by one when pressing Escape to + enter command mode, like vi (issue 4767 from Joshua Cooper). + +* Add {current}/{active} for -t for current window or active pane (from Manuel + Einfalt, issue 4766). + +* Add support for applications to use synchronized output mode (DECSET 2026) to + prevent screen tearing during rapid updates (from Chris Lloyd in issue 4744). + +* Do not set a default prompt cursor colour because some terminals (urxvt, st) + do not support the reset sequence (issue 4759). + +* Add a scroll-to-mouse command for copy mode to scroll to the mouse position + and bind to the scrollbar, brings the scrollbar keys into line with the other + mouse keys (from Michael Grant in issue 4731). + +* Bump the maximum number of SIXEL images to 20. + +* Fix key code for M-BSpace (issue 4717). + +* Fix calculation of scaled SIXEL size (from nincsnevem662 at gmail dot com in + issue 4739). Also various other fixes and improvements for SIXEL. + +* Fix a race between fork and pane_current_path, most noticeable on systems + where starting processes is slow (issue 4719). + +* Fix mouse position calculation on scrollbar with pane status line at the top + (issue 4738 from Michael Grant). + +* Do not read outside buffer if format is a single #, and do not loop forever + if UTF-8 is unfinished in a format (reported by Giorgi Kobakhia in issue + 4735). + +* Add a missing skin tone character (from Jake Stewart in issue 4736). + +* Allow UTF-8 characters to be combined in either order (reported by Jake + Stewart in issue 4726). + +* Do not show scrollbar when entering copy mode from a pane in the alternate + screen (issue 4728 from Michael Grant). + +* Fix the size calculation for left-right windows used to spread out cells + horizontally evenly (from Michael Grant in issue 4724). + +* Add a get-clipboard option which when enabled (the default is off) and a + clipboard is requested from a pane, requests it from the terminal and + forwards to the requesting pane; also remove the now-redundant + forward-to-pane ability from "refresh-client -l" (issue 4275). + +* Fix the noattr attribute in styles, used by the default mode-style (issue + 4713). + +* Do not remove TERM for commands run from config file (regression reported by + Dennis Eriksen). + +* Add seconds options for clock mode (from augustus7613 dot mail at pm dot me, + issue 4697; later improved by Joao Pedro in issue 4760). + CHANGES FROM 3.6a TO 3.6b * Remove images from the correct list when they are removed while in the diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index d42adf88..8f537b98 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -29,18 +29,21 @@ static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *); -static char *cmd_capture_pane_append(char *, size_t *, char *, size_t); +static char *cmd_capture_pane_append(char *, size_t *, const char *, + size_t); static char *cmd_capture_pane_pending(struct args *, struct window_pane *, size_t *); static char *cmd_capture_pane_history(struct args *, struct cmdq_item *, struct window_pane *, size_t *); +static char *cmd_capture_pane_hyperlinks(struct grid *, struct screen *, + u_int, u_int *, u_int *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", - .args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL }, - .usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " + .args = { "ab:CeE:FHJLMNpPqS:Tt:", 0, 0, NULL }, + .usage = "[-aCeFHJLMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -63,7 +66,8 @@ const struct cmd_entry cmd_clear_history_entry = { }; static char * -cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) +cmd_capture_pane_append(char *buf, size_t *len, const char *line, + size_t linelen) { buf = xrealloc(buf, *len + linelen + 1); memcpy(buf + *len, line, linelen); @@ -103,6 +107,46 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp, return (buf); } +static char * +cmd_capture_pane_hyperlinks(struct grid *gd, struct screen *s, u_int py, + u_int *links, u_int *nlinks, size_t *len) +{ + const struct grid_line *gl = grid_peek_line(gd, py); + struct grid_cell gc; + const char *uri; + char *line = xstrdup(""); + u_int i, j; + + *len = 0; + + if (s->hyperlinks == NULL || (~gl->flags & GRID_LINE_HYPERLINK)) + return (line); + + for (i = 0; i < gl->cellused; i++) { + grid_get_cell(gd, i, py, &gc); + if (gc.link == 0) + continue; + for (j = 0; j < *nlinks; j++) { + if (links[j] == gc.link) + break; + } + if (j != *nlinks) + continue; + + if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL)) + continue; + + if (*nlinks == gd->sx) + break; + links[(*nlinks)++] = gc.link; + + if (*len != 0) + line = cmd_capture_pane_append(line, len, " ", 1); + line = cmd_capture_pane_append(line, len, uri, strlen(uri)); + } + return (line); +} + static char * cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct window_pane *wp, size_t *len) @@ -112,9 +156,11 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct screen *s; struct grid_cell *gc = NULL; struct window_mode_entry *wme; - int n, join_lines, flags = 0; + int n, join_lines, number_lines, flags = 0; + int show_flags, hyperlinks; + u_int *links = NULL, nlinks = 0; u_int i, sx, top, bottom, tmp; - char *cause, *buf, *line; + char *cause, *buf = NULL, *line, b[64], *cp; const char *Sflag, *Eflag; size_t linelen; @@ -152,7 +198,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, if (cause != NULL) { top = gd->hsize; free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) + } else if (n < 0 && (u_int)-n > gd->hsize) top = 0; else top = gd->hsize + n; @@ -169,7 +215,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) + } else if (n < 0 && (u_int)-n > gd->hsize) bottom = 0; else bottom = gd->hsize + n; @@ -192,12 +238,57 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, flags |= GRID_STRING_EMPTY_CELLS; if (!join_lines && !args_has(args, 'N')) flags |= GRID_STRING_TRIM_SPACES; + number_lines = args_has(args, 'L'); + show_flags = args_has(args, 'F'); + hyperlinks = args_has(args, 'H'); + if (hyperlinks) + links = xreallocarray(NULL, gd->sx, sizeof *links); - buf = NULL; for (i = top; i <= bottom; i++) { - line = grid_string_cells(gd, 0, i, sx, &gc, flags, s); - linelen = strlen(line); + if (hyperlinks) { + line = cmd_capture_pane_hyperlinks(gd, s, i, links, + &nlinks, &linelen); + } else { + line = grid_string_cells(gd, 0, i, sx, &gc, flags, s); + linelen = strlen(line); + } + if (hyperlinks && linelen == 0) { + free(line); + continue; + } + if (number_lines) { + if (i >= gd->hsize) + n = i - gd->hsize; + else + n = (int)i - (int)gd->hsize; + n = snprintf(b, sizeof b, "%d ", n); + if (n >= 0) + buf = cmd_capture_pane_append(buf, len, b, n); + } + if (show_flags) { + cp = b; + *cp = '\0'; + + gl = grid_peek_line(gd, i); + if (gl->flags & GRID_LINE_DEAD) + *cp++ = 'D'; + if (gl->flags & GRID_LINE_HYPERLINK) + *cp++ = 'H'; + if (gl->flags & GRID_LINE_START_OUTPUT) + *cp++ = 'O'; + if (gl->flags & GRID_LINE_START_PROMPT) + *cp++ = 'P'; + if (gl->flags & GRID_LINE_WRAPPED) + *cp++ = 'W'; + if (gl->flags & GRID_LINE_EXTENDED) + *cp++ = 'X'; + if (b == cp) + *cp++ = '-'; + *cp++ = ' '; + *cp = '\0'; + buf = cmd_capture_pane_append(buf, len, b, strlen (b)); + } buf = cmd_capture_pane_append(buf, len, line, linelen); gl = grid_peek_line(gd, i); @@ -206,6 +297,9 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, free(line); } + free(links); + if (buf == NULL) + buf = xstrdup(""); return (buf); } @@ -228,7 +322,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) } len = 0; - if (args_has(args, 'P')) + if (args_has(args, 'P') && !args_has(args, 'H')) buf = cmd_capture_pane_pending(args, wp, &len); else buf = cmd_capture_pane_history(args, item, wp, &len); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 186672f4..6bb17fc2 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-GhkNrswZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -47,8 +47,8 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:hK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-hkNrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -61,8 +61,8 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -75,8 +75,8 @@ const struct cmd_entry cmd_customize_mode_entry = { .name = "customize-mode", .alias = NULL, - .args = { "F:f:Nt:yZ", 0, 0, NULL }, - .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, + .args = { "F:f:kNt:yZ", 0, 0, NULL }, + .usage = "[-kNZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-display-panes.c b/cmd-display-panes.c index efa7c7e4..5a1782ff 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -76,6 +76,40 @@ cmd_display_panes_put(struct screen_redraw_ctx *ctx, } } +static void +cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, + struct window_pane *wp, u_int xoff, u_int yoff, u_int sx, + const struct grid_cell *gc) +{ + struct client *c = ctx->c; + struct tty *tty = &c->tty; + struct session *s = c->session; + struct screen screen; + struct screen_write_ctx sctx; + struct visible_ranges *r; + struct visible_range *ri; + const char *format; + char *expanded; + u_int i, px = ctx->ox + xoff; + + format = options_get_string(s->options, "display-panes-format"); + expanded = format_single(NULL, format, c, s, s->curw, wp); + + screen_init(&screen, sx, 1, 0); + screen_write_start(&sctx, &screen); + format_draw(&sctx, gc, sx, expanded, NULL, 0); + screen_write_stop(&sctx); + free(expanded); + + r = screen_redraw_get_visible_ranges(wp, px, wp->yoff, sx, NULL); + for (i = 0; i < r->used; i++) { + ri = &r->ranges[i]; + tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx, + ri->px - ctx->ox, yoff, gc, NULL); + } + screen_free(&screen); +} + static void cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) @@ -89,8 +123,8 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; u_int cx, cy; int colour, active_colour; - char buf[16], lbuf[16], rbuf[16], *ptr; - size_t len, llen, rlen; + char buf[16], lbuf[16], *ptr; + size_t len, llen; if (wp->xoff + (int)wp->sx <= ctx->ox || wp->xoff >= ctx->ox + (int)ctx->sx || @@ -159,7 +193,6 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, bgc.bg = colour; } - rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy); if (pane > 9 && pane < 35) llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); else @@ -207,13 +240,9 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, if (sy <= 6) goto out; - tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); - if (rlen != 0 && sx >= rlen) { - cx = xoff + sx - rlen; - cy = yoff; - cmd_display_panes_put(ctx, wp, cx, cy, rbuf, rlen); - } + cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc); if (llen != 0) { + tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); cx = xoff + sx / 2 + len * 3 - llen - 1; cy = yoff + py + 5; cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index c1e879b6..070f2d6f 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -93,7 +93,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause); if (cause != NULL) { - cmdq_error(item, "%s", cause); + cmdq_error(item, "size or position %s", cause); free(cause); return (CMD_RETURN_ERROR); } diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 19a8d495..7d3ae984 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_kill_session_entry = { .name = "kill-session", .alias = NULL, - .args = { "aCt:", 0, 0, NULL }, - .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, + .args = { "aCgt:", 0, 0, NULL }, + .usage = "[-aCg] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -48,6 +48,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct session *s = target->s, *sloop, *stmp; + struct session_group *sg; struct winlink *wl; if (args_has(args, 'C')) { @@ -63,6 +64,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) session_destroy(sloop, 1, __func__); } } + } else if (args_has(args, 'g') && + (sg = session_group_contains(s)) != NULL) { + TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) { + server_destroy_session(sloop); + session_destroy(sloop, 1, __func__); + } } else { server_destroy_session(s); session_destroy(s, 1, __func__); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 405c03c7..d8065259 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -35,7 +35,7 @@ "," \ "bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \ "-T #{p|#{key_table_width}:key_table} " \ - "#{p|#{key_string_width}:key_string} " \ + "#{p|#{key_string_width}:#{q|a:key_string}} " \ "#{key_command}}" static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); diff --git a/cmd-split-window.c b/cmd-split-window.c index 2f0c9a46..77576901 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -38,11 +38,11 @@ const struct cmd_entry cmd_new_pane_entry = { .name = "new-pane", .alias = "newp", - .args = { "bc:de:fF:hIkl:Lm:p:PR:s:S:t:vx:X:y:Y:Z", 0, -1, NULL }, + .args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vx:X:y:Y:Z", 0, -1, NULL }, .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " - "[-R inactive-border-style] [-x width] [-y height] " + "[-R inactive-border-style] [-T title] [-x width] [-y height] " "[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " " "[shell-command [argument ...]]", @@ -56,11 +56,11 @@ const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", - .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vZ", 0, -1, NULL }, .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " - "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " "[shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -83,9 +83,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp, *new_wp; struct layout_cell *lc = NULL; struct cmd_find_state fs; - int input, is_floating, flags = 0; + int input, empty, is_floating, flags = 0; const char *template, *style; - char *cause = NULL, *cp; + char *cause = NULL, *cp, *title; struct args_value *av; u_int count = args_count(args); @@ -93,22 +93,33 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) is_floating = !args_has(args, 'L'); else is_floating = 0; - input = (args_has(args, 'I') && count == 0); flags = is_floating ? SPAWN_FLOATING : 0; if (args_has(args, 'b')) flags |= SPAWN_BEFORE; if (args_has(args, 'f')) flags |= SPAWN_FULLSIZE; - if (input || (count == 1 && *args_string(args, 0) == '\0')) + + input = args_has(args, 'I'); + if (input) + empty = 1; + else + empty = args_has(args, 'E'); + if (empty && + count != 0 && + (count != 1 || *args_string(args, 0) != '\0')) { + cmdq_error(item, "command cannot be given for empty pane"); + return (CMD_RETURN_ERROR); + } + if (empty) flags |= SPAWN_EMPTY; if (is_floating) - lc = layout_get_floating_cell(item, args, w, wp, lc, &cause); + lc = layout_get_floating_cell(item, args, w, wp, &cause); else lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause); if (cause != NULL) { - cmdq_error(item, "%s", cause); + cmdq_error(item, "size or position %s", cause); free(cause); return (CMD_RETURN_ERROR); } @@ -183,6 +194,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) "remain-on-exit-format", 0, "%s", args_get(args, 'm')); } + if (args_has(args, 'T')) { + title = format_single_from_target(item, args_get(args, 'T')); + screen_set_title(&new_wp->base, title); + notify_pane("pane-title-changed", new_wp); + free(title); + } if (input) { switch (window_pane_start_input(new_wp, item, &cause)) { @@ -209,7 +226,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) window_pop_zoom(wp->window); server_redraw_window(wp->window); } - server_status_session(s); + server_redraw_session(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 24ed0b9d..48785c92 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -79,7 +79,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (src_wp == dst_wp) goto out; - if (window_pane_is_floating(src_wp) && + if (window_pane_is_floating(src_wp) || window_pane_is_floating(dst_wp)) { cmdq_error(item, "cannot swap floating panes"); return (CMD_RETURN_ERROR); diff --git a/format.c b/format.c index eb5546f4..af251c3a 100644 --- a/format.c +++ b/format.c @@ -116,6 +116,8 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_NOT 0x80000 #define FORMAT_NOT_NOT 0x100000 #define FORMAT_REPEAT 0x200000 +#define FORMAT_QUOTE_ARGUMENTS 0x400000 +#define FORMAT_RELATIVE 0x800000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 100 @@ -4079,6 +4081,50 @@ format_pretty_time(time_t t, int seconds) return (xstrdup(s)); } +/* Make a relative time. */ +static char * +format_relative_time(time_t t) +{ + time_t now, age; + u_int d, h, m, s; + char out[32], sign; + + time(&now); + if (t == now) + return (xstrdup("0s")); + if (t > now) { + sign = '+'; + age = t - now; + } else { + sign = '-'; + age = now - t; + } + + d = age / 86400; + h = (age % 86400) / 3600; + m = (age % 3600) / 60; + s = age % 60; + + if (d != 0) { + if (h != 0) + xsnprintf(out, sizeof out, "%c%ud%uh", sign, d, h); + else + xsnprintf(out, sizeof out, "%c%ud", sign, d); + } else if (h != 0) { + if (m != 0) + xsnprintf(out, sizeof out, "%c%uh%um", sign, h, m); + else + xsnprintf(out, sizeof out, "%c%uh", sign, h); + } else if (m != 0) { + if (s != 0) + xsnprintf(out, sizeof out, "%c%um%us", sign, m, s); + else + xsnprintf(out, sizeof out, "%c%um", sign, m); + } else + xsnprintf(out, sizeof out, "%c%us", sign, s); + return (xstrdup(out)); +} + /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers, @@ -4160,7 +4206,9 @@ found: } if (t == 0) return (NULL); - if (modifiers & FORMAT_PRETTY) + if (modifiers & FORMAT_RELATIVE) + found = format_relative_time(t); + else if (modifiers & FORMAT_PRETTY) found = format_pretty_time(t, 0); else { if (time_format != NULL) { @@ -4199,6 +4247,11 @@ found: found = format_quote_style(saved); free(saved); } + if (modifiers & FORMAT_QUOTE_ARGUMENTS) { + saved = found; + found = args_escape(saved); + free(saved); + } return (found); } @@ -5160,6 +5213,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, break; if (strchr(fm->argv[0], 'p') != NULL) modifiers |= FORMAT_PRETTY; + else if (strchr(fm->argv[0], 'r') != NULL) + modifiers |= FORMAT_RELATIVE; else if (fm->argc >= 2 && strchr(fm->argv[0], 'f') != NULL) { free(time_format); @@ -5172,6 +5227,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, else if (strchr(fm->argv[0], 'e') != NULL || strchr(fm->argv[0], 'h') != NULL) modifiers |= FORMAT_QUOTE_STYLE; + else if (strchr(fm->argv[0], 'a') != NULL) + modifiers |= FORMAT_QUOTE_ARGUMENTS; break; case 'E': modifiers |= FORMAT_EXPAND; diff --git a/grid.c b/grid.c index 0d1db844..36c99184 100644 --- a/grid.c +++ b/grid.c @@ -125,6 +125,8 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, else if (gce->offset >= gl->extdsize) fatalx("offset too big"); gl->flags |= GRID_LINE_EXTENDED; + if (gc->link != 0) + gl->flags |= GRID_LINE_HYPERLINK; if (gc->flags & GRID_FLAG_TAB) uc = gc->data.width; @@ -285,9 +287,8 @@ static void grid_free_line(struct grid *gd, u_int py) { free(gd->linedata[py].celldata); - gd->linedata[py].celldata = NULL; free(gd->linedata[py].extddata); - gd->linedata[py].extddata = NULL; + memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); } /* Free several lines. */ @@ -332,9 +333,7 @@ void grid_destroy(struct grid *gd) { grid_free_lines(gd, 0, gd->hsize + gd->sy); - free(gd->linedata); - free(gd); } @@ -414,12 +413,14 @@ grid_collect_history(struct grid *gd, int all) void grid_remove_history(struct grid *gd, u_int ny) { - u_int yy; + u_int yy, start; if (ny > gd->hsize) return; + start = gd->hsize + gd->sy - ny; for (yy = 0; yy < ny; yy++) - grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); + grid_free_line(gd, start + yy); + memset(&gd->linedata[start], 0, ny * sizeof *gd->linedata); gd->hsize -= ny; } diff --git a/layout.c b/layout.c index 41b8fee2..d6b6ca86 100644 --- a/layout.c +++ b/layout.c @@ -46,9 +46,6 @@ static int layout_set_size_check(struct window *, struct layout_cell *, enum layout_type, int); static void layout_resize_child_cells(struct window *, struct layout_cell *); -static struct layout_cell *layout_active_neighbour(struct layout_cell *, int); -void layout_redistribute_cells(struct window *, struct layout_cell *, - enum layout_type); /* Create a new layout cell. */ struct layout_cell * @@ -253,9 +250,10 @@ layout_fix_offsets1(struct layout_cell *lc) if (lc->type == LAYOUT_LEFTRIGHT) { xoff = lc->xoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->type == LAYOUT_WINDOWPANE && + if (lcchild->flags & LAYOUT_CELL_FLOATING || + (lcchild->type == LAYOUT_WINDOWPANE && lcchild->wp != NULL && - lcchild->wp->flags & PANE_HIDDEN) + (lcchild->wp->flags & PANE_HIDDEN))) continue; lcchild->xoff = xoff; lcchild->yoff = lc->yoff; @@ -266,9 +264,10 @@ layout_fix_offsets1(struct layout_cell *lc) } else { yoff = lc->yoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { - if (lcchild->type == LAYOUT_WINDOWPANE && + if (lcchild->flags & LAYOUT_CELL_FLOATING || + (lcchild->type == LAYOUT_WINDOWPANE && lcchild->wp != NULL && - lcchild->wp->flags & PANE_HIDDEN) + (lcchild->wp->flags & PANE_HIDDEN))) continue; lcchild->xoff = lc->xoff; lcchild->yoff = yoff; @@ -285,6 +284,10 @@ layout_fix_offsets(struct window *w) { struct layout_cell *lc = w->layout_root; + /* Root consists of a single floating cell */ + if (lc->flags & LAYOUT_CELL_FLOATING) + return; + lc->xoff = 0; lc->yoff = 0; @@ -493,8 +496,11 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, /* Child cell runs in a different direction. */ if (lc->type != type) { - TAILQ_FOREACH(lcchild, &lc->cells, entry) + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; layout_resize_adjust(w, lcchild, type, change); + } return; } @@ -506,6 +512,8 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (change == 0) break; + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; if (change > 0) { layout_resize_adjust(w, lcchild, type, 1); change--; @@ -610,30 +618,33 @@ void layout_destroy_cell(struct window *w, struct layout_cell *lc, struct layout_cell **lcroot) { - struct layout_cell *lcother, *lcparent; + struct layout_cell *lcother = NULL, *lcparent; - /* - * If no parent, this is either a floating pane or the last - * pane so window close is imminent and there is no need to - * resize anything. - */ + /* If no parent, this is the last pane in a window. */ lcparent = lc->parent; if (lcparent == NULL) { - if (lc->wp != NULL && !window_pane_is_floating(lc->wp)) + if (lc->wp != NULL) *lcroot = NULL; layout_free_cell(lc); return; } - /* Merge the space into the previous or next cell. */ - if (lc == TAILQ_FIRST(&lcparent->cells)) - lcother = TAILQ_NEXT(lc, entry); - else - lcother = TAILQ_PREV(lc, layout_cells, entry); - if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) - layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); - else if (lcother != NULL) - layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); + if (~lc->flags & LAYOUT_CELL_FLOATING) { + /* Merge the space into the previous or next cell. */ + if (lc == TAILQ_FIRST(&lcparent->cells)) + lcother = TAILQ_NEXT(lc, entry); + else + lcother = TAILQ_PREV(lc, layout_cells, entry); + } + if (lcother != NULL && (~lcother->flags & LAYOUT_CELL_FLOATING)) { + if (lcparent->type == LAYOUT_LEFTRIGHT) { + layout_resize_adjust(w, lcother, lcparent->type, + lc->sx + 1); + } else { + layout_resize_adjust(w, lcother, lcparent->type, + lc->sy + 1); + } + } /* Remove this from the parent's list. */ TAILQ_REMOVE(&lcparent->cells, lc, entry); @@ -649,8 +660,10 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, lc->parent = lcparent->parent; if (lc->parent == NULL) { - lc->xoff = 0; - lc->yoff = 0; + if (~lc->flags & LAYOUT_CELL_FLOATING) { + lc->xoff = 0; + lc->yoff = 0; + } /* * If the sole remaining child is a hidden @@ -774,6 +787,8 @@ layout_resize(struct window *w, u_int sx, u_int sy) * out proportionately - this should leave the layout fitting the new * window size. */ + if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING)) + return; xchange = sx - lc->sx; xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); if (xchange < 0 && xchange < -xlimit) @@ -1086,6 +1101,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) count = 0; previous = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; count++; if (lc->type == LAYOUT_LEFTRIGHT) previous += lcchild->sx; @@ -1104,6 +1121,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) /* Resize children into the new size. */ idx = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (lcchild->flags & LAYOUT_CELL_FLOATING) + continue; if (lc->type == LAYOUT_TOPBOTTOM) { lcchild->sx = lc->sx; lcchild->xoff = lc->xoff; @@ -1112,9 +1131,10 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) lc->type, lc->sx, count - idx, available); available -= (lcchild->sx + 1); } - if (lc->type == LAYOUT_LEFTRIGHT) + if (lc->type == LAYOUT_LEFTRIGHT) { lcchild->sy = lc->sy; - else { + lcchild->yoff = lc->yoff; + } else { lcchild->sy = layout_new_pane_size(w, previous, lcchild, lc->type, lc->sy, count - idx, available); available -= (lcchild->sy + 1); @@ -1303,6 +1323,39 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, return (lcnew); } +/* + * Creates a cell for a new floating pane. This must be followed by + * layout_assign_pane before much else happens! + */ +struct layout_cell * +layout_floating_pane(struct window *w, u_int sx, u_int sy, int ox, int oy) +{ + struct layout_cell *lc = w->layout_root, *lcnew, *lcparent; + + if (lc->type == LAYOUT_WINDOWPANE) { + /* + * Adding a pane to a root that doesn't have a container. Must + * create and insert a new root. + */ + lcparent = layout_create_cell(NULL); + layout_make_node(lcparent, LAYOUT_TOPBOTTOM); + layout_set_size(lcparent, w->sx, w->sy, 0, 0); + w->layout_root = lcparent; + + /* Insert the old cell. */ + lc->parent = lcparent; + TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); + } else + lcparent = w->layout_root; + + lcnew = layout_create_cell(lcparent); + TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); + lcnew->flags |= LAYOUT_CELL_FLOATING; + layout_set_size(lcnew, sx, sy, ox, oy); + + return (lcnew); +} + /* Destroy the cell associated with a pane. */ void layout_close_pane(struct window_pane *wp) @@ -1314,6 +1367,7 @@ layout_close_pane(struct window_pane *wp) /* Remove the cell. */ layout_destroy_cell(w, wp->layout_cell, &w->layout_root); + wp->layout_cell = NULL; /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { @@ -1470,49 +1524,37 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args, /* Get a new floating cell. */ struct layout_cell * layout_get_floating_cell(struct cmdq_item *item, struct args *args, - struct window *w, __unused struct window_pane *wp, struct layout_cell *lc, - char **cause) + struct window *w, __unused struct window_pane *wp, char **cause) { - u_int sx, sy, ox, oy; - int new = 0; - - if (lc == NULL) { - lc = layout_create_cell(NULL); - new = 1; - } - - sx = lc->sx; sy = lc->sy; - ox = lc->xoff; oy = lc->yoff; + struct layout_cell *lcnew; + int sx = w->sx / 2, sy = w->sy / 4; + int ox = INT_MAX, oy = INT_MAX; if (args_has(args, 'x')) { - sx = args_percentage_and_expand(args, 'x', 0, USHRT_MAX, w->sx, + sx = args_percentage_and_expand(args, 'x', 0, w->sx - 1, w->sx, item, cause); if (*cause != NULL) - goto error; + return (NULL); } if (args_has(args, 'y')) { - sy = args_percentage_and_expand(args, 'y', 0, USHRT_MAX, w->sy, + sy = args_percentage_and_expand(args, 'y', 0, w->sy - 1, w->sy, item, cause); if (*cause != NULL) - goto error; + return (NULL); } if (args_has(args, 'X')) { - ox = args_percentage_and_expand(args, 'X', 0, USHRT_MAX, w->sx, - item, cause); + ox = args_percentage_and_expand(args, 'X', -sx, w->sx, + w->sx, item, cause); if (*cause != NULL) - goto error; + return (NULL); } if (args_has(args, 'Y')) { - oy = args_percentage_and_expand(args, 'Y', 0, USHRT_MAX, w->sy, - item, cause); + oy = args_percentage_and_expand(args, 'Y', -sy, w->sy, + w->sy, item, cause); if (*cause != NULL) - goto error; + return (NULL); } - if (sx == UINT_MAX) - sx = w->sx / 2; - if (sy == UINT_MAX) - sy = w->sy / 4; if (ox == INT_MAX) { if (w->last_new_pane_x == 0) ox = 4; @@ -1533,13 +1575,7 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args, } w->last_new_pane_y = oy; } - layout_set_size(lc, sx, sy, ox, oy); - lc->flags |= LAYOUT_CELL_FLOATING; - return (lc); - -error: - if (new) - layout_destroy_cell(w, lc, &w->layout_root); - return (NULL); + lcnew = layout_floating_pane(w, sx, sy, ox, oy); + return (lcnew); } diff --git a/mode-tree.c b/mode-tree.c index 6479d78d..82535497 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1224,6 +1224,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, u_int i, x, y; int choice, preview; + if (mtd->line_size == 0) { + *key = KEYC_NONE; + return (1); + } + if (KEYC_IS_MOUSE(*key) && m != NULL) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { *key = KEYC_NONE; diff --git a/options-table.c b/options-table.c index 6fa41750..f78f047e 100644 --- a/options-table.c +++ b/options-table.c @@ -647,6 +647,14 @@ const struct options_table_entry options_table[] = { .text = "Colour of not active panes for 'display-panes'." }, + { .name = "display-panes-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "#[align=right]#{pane_width}x#{pane_height}", + .text = "Format of text shown by 'display-panes', expanded for each " + "pane." + }, + { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, @@ -1028,7 +1036,8 @@ const struct options_table_entry options_table[] = { .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK " "SSH_AGENT_PID SSH_CONNECTION WAYLAND_DISPLAY " - "WINDOWID XAUTHORITY", + "WINDOWID XAUTHORITY XDG_CURRENT_DESKTOP " + "XDG_SESSION_DESKTOP XDG_SESSION_TYPE", .text = "List of environment variables to update in the session " "environment when a client is attached." }, diff --git a/regress/am-terminal.sh b/regress/am-terminal.sh index 94033468..bf1cabfe 100644 --- a/regress/am-terminal.sh +++ b/regress/am-terminal.sh @@ -4,9 +4,9 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null -TMUX2="$TEST_TMUX -Ltest2" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" $TMUX2 kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/border-arrows.sh b/regress/border-arrows.sh index 7e7bffd0..ea571b05 100644 --- a/regress/border-arrows.sh +++ b/regress/border-arrows.sh @@ -11,9 +11,9 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null -TMUX_OUTER="$TEST_TMUX -Ltest2" +TMUX_OUTER="$TEST_TMUX -Ltest2 -f/dev/null" $TMUX_OUTER kill-server 2>/dev/null trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15 diff --git a/regress/capture-pane-hyperlink.sh b/regress/capture-pane-hyperlink.sh index bcd053b4..80092e7f 100644 --- a/regress/capture-pane-hyperlink.sh +++ b/regress/capture-pane-hyperlink.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" TMP=$(mktemp) TMP2=$(mktemp) trap "rm -f $TMP $TMP2" 0 1 15 diff --git a/regress/capture-pane-sgr0.sh b/regress/capture-pane-sgr0.sh index 5d21ccc4..7c9c3231 100644 --- a/regress/capture-pane-sgr0.sh +++ b/regress/capture-pane-sgr0.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/combine-test.sh b/regress/combine-test.sh index 14fa709b..a4d4f368 100644 --- a/regress/combine-test.sh +++ b/regress/combine-test.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/command-order.sh b/regress/command-order.sh index 04046f0d..775768a6 100644 --- a/regress/command-order.sh +++ b/regress/command-order.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/conf-syntax.sh b/regress/conf-syntax.sh index 96d35140..49b9710b 100644 --- a/regress/conf-syntax.sh +++ b/regress/conf-syntax.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null for i in conf/*.conf; do diff --git a/regress/control-client-sanity.sh b/regress/control-client-sanity.sh index 48ffd0ee..f6c36e1b 100644 --- a/regress/control-client-sanity.sh +++ b/regress/control-client-sanity.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/control-client-size.sh b/regress/control-client-size.sh index dc275e52..7d5e1dc2 100644 --- a/regress/control-client-size.sh +++ b/regress/control-client-size.sh @@ -8,7 +8,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/copy-mode-test-emacs.sh b/regress/copy-mode-test-emacs.sh index ad863030..a5b69c67 100644 --- a/regress/copy-mode-test-emacs.sh +++ b/regress/copy-mode-test-emacs.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -f/dev/null -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null $TMUX new -d -x40 -y10 \ diff --git a/regress/copy-mode-test-vi.sh b/regress/copy-mode-test-vi.sh index 911a1f1b..2d6bfc28 100644 --- a/regress/copy-mode-test-vi.sh +++ b/regress/copy-mode-test-vi.sh @@ -34,14 +34,17 @@ $TMUX send-keys -X previous-word $TMUX send-keys -X copy-selection [ "$($TMUX show-buffer)" = "line" ] || exit 1 -# Test that `next-word-end` wraps around indented line breaks. +# Test that `next-word-end` stops at the end of the line. $TMUX send-keys -X next-word $TMUX send-keys -X next-word $TMUX send-keys -X begin-selection $TMUX send-keys -X next-word-end $TMUX send-keys -X next-word-end $TMUX send-keys -X copy-selection -[ "$($TMUX show-buffer)" = "$(printf "words\n\tIndented")" ] || exit 1 +[ "$($TMUX show-buffer)" = "words" ] || exit 1 + +# Move to the next word for the following tests. +$TMUX send-keys -X next-word # Test that `next-word` wraps around un-indented line breaks. $TMUX send-keys -X next-word diff --git a/regress/cursor-test1.sh b/regress/cursor-test1.sh index 2dc20539..73a336be 100644 --- a/regress/cursor-test1.sh +++ b/regress/cursor-test1.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -f/dev/null -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/cursor-test2.sh b/regress/cursor-test2.sh index 9791f567..3de80fda 100644 --- a/regress/cursor-test2.sh +++ b/regress/cursor-test2.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/cursor-test3.sh b/regress/cursor-test3.sh index 8bb4bd6f..e4881c79 100644 --- a/regress/cursor-test3.sh +++ b/regress/cursor-test3.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/cursor-test4.sh b/regress/cursor-test4.sh index 2bf1bb0e..8f4dfa5c 100644 --- a/regress/cursor-test4.sh +++ b/regress/cursor-test4.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/decrqm-sync.sh b/regress/decrqm-sync.sh index bca57dca..7f8c3f61 100644 --- a/regress/decrqm-sync.sh +++ b/regress/decrqm-sync.sh @@ -9,16 +9,12 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null sleep 1 TMP=$(mktemp) -TMP2=$(mktemp) -TMP3=$(mktemp) -TMP4=$(mktemp) -TMP5=$(mktemp) -trap "rm -f $TMP $TMP2 $TMP3 $TMP4 $TMP5; $TMUX kill-server 2>/dev/null" 0 1 15 +trap "rm -f $TMP; $TMUX kill-server 2>/dev/null" 0 1 15 $TMUX -f/dev/null new -d -x80 -y24 || exit 1 sleep 1 @@ -35,13 +31,14 @@ query_decrpm() { _outfile=$1 _mode=$2 _setup=$3 + _n=$4 $TMUX respawnw -k -t:0 -- sh -c " exec 2>/dev/null stty raw -echo ${_setup:+printf '$_setup'; sleep 0.2} printf '\033[%s\$p' "$_mode" - dd bs=1 count=11 2>/dev/null | cat -v > $_outfile + dd bs=1 count=$_n 2>/dev/null | cat -v > $_outfile sleep 0.2 " || exit 1 sleep 2 @@ -50,7 +47,7 @@ query_decrpm() { # ------------------------------------------------------------------ # Test 1: mode 2026 should be reset by default (Ps=2) # ------------------------------------------------------------------ -query_decrpm "$TMP" "?2026" +query_decrpm "$TMP" "?2026" '' 11 actual=$(cat "$TMP") expected='^[[?2026;2$y' @@ -67,9 +64,9 @@ fi # ------------------------------------------------------------------ # Test 2: set mode 2026 (SM ?2026), then query (expect Ps=1) # ------------------------------------------------------------------ -query_decrpm "$TMP2" "?2026" '\033[?2026h' +query_decrpm "$TMP" "?2026" '\033[?2026h' 11 -actual=$(cat "$TMP2") +actual=$(cat "$TMP") expected='^[[?2026;1$y' if [ "$actual" = "$expected" ]; then @@ -84,9 +81,9 @@ fi # ------------------------------------------------------------------ # Test 3: mode 25 should return current value # ------------------------------------------------------------------ -query_decrpm "$TMP3" "?25" '\033[?25l' +query_decrpm "$TMP" "?25" '\033[?25l' 9 -actual=$(cat "$TMP3") +actual=$(cat "$TMP") expected='^[[?25;2$y' if [ "$actual" = "$expected" ]; then @@ -101,9 +98,9 @@ fi # ------------------------------------------------------------------ # Test 4: mode ?9999 should return not recognized # ------------------------------------------------------------------ -query_decrpm "$TMP4" "?9999" '\033[?9999h' +query_decrpm "$TMP" "?9999" '\033[?9999h' 11 -actual=$(cat "$TMP4") +actual=$(cat "$TMP") expected='^[[?9999;0$y' if [ "$actual" = "$expected" ]; then @@ -122,9 +119,9 @@ exit $exit_status # ------------------------------------------------------------------ # Test 5: mode 4 is reset by default # ------------------------------------------------------------------ -query_decrpm "$TMP5" "4" '\033[4h' +query_decrpm "$TMP" "4" '\033[4h' 11 -actual=$(cat "$TMP5") +actual=$(cat "$TMP") expected='^[[4;1$y' if [ "$actual" = "$expected" ]; then diff --git a/regress/format-strings.sh b/regress/format-strings.sh index e9bf8b0f..8d192ed2 100644 --- a/regress/format-strings.sh +++ b/regress/format-strings.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" # test_format $format $expected_result test_format() diff --git a/regress/has-session-return.sh b/regress/has-session-return.sh index 5cc30f05..a7d76ad8 100644 --- a/regress/has-session-return.sh +++ b/regress/has-session-return.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null $TMUX -f/dev/null has -tfoo /dev/null && exit 1 diff --git a/regress/if-shell-TERM.sh b/regress/if-shell-TERM.sh index 5967a58f..21846fd1 100644 --- a/regress/if-shell-TERM.sh +++ b/regress/if-shell-TERM.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/if-shell-error.sh b/regress/if-shell-error.sh index 271e160b..2eab8f7c 100644 --- a/regress/if-shell-error.sh +++ b/regress/if-shell-error.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/if-shell-nested.sh b/regress/if-shell-nested.sh index 976c6738..434775c4 100644 --- a/regress/if-shell-nested.sh +++ b/regress/if-shell-nested.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/input-keys.sh b/regress/input-keys.sh index 96d2d5e8..e2fc41f5 100644 --- a/regress/input-keys.sh +++ b/regress/input-keys.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null sleep 1 $TMUX -f/dev/null new -x20 -y2 -d \; set -g escape-time 0 || exit 1 diff --git a/regress/kill-session-process-exit.sh b/regress/kill-session-process-exit.sh index 82cd7412..04617ca2 100644 --- a/regress/kill-session-process-exit.sh +++ b/regress/kill-session-process-exit.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null sleep 1 diff --git a/regress/new-session-base-index.sh b/regress/new-session-base-index.sh index 337037c3..bb6dd594 100644 --- a/regress/new-session-base-index.sh +++ b/regress/new-session-base-index.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/new-session-command.sh b/regress/new-session-command.sh index 8dec322a..b2fc91db 100644 --- a/regress/new-session-command.sh +++ b/regress/new-session-command.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/new-session-environment.sh b/regress/new-session-environment.sh index 84062aba..e5404e67 100644 --- a/regress/new-session-environment.sh +++ b/regress/new-session-environment.sh @@ -5,14 +5,14 @@ PATH=/bin:/usr/bin [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TERM=$($TMUX start \; show -gv default-terminal) TMP=$(mktemp) OUT=$(mktemp) SCRIPT=$(mktemp) -#trap "rm -f $TMP $OUT $SCRIPT" 0 1 15 +trap "rm -f $TMP $OUT $SCRIPT" 0 1 15 cat <$SCRIPT ( diff --git a/regress/new-session-no-client.sh b/regress/new-session-no-client.sh index 88c064cd..84fddaec 100644 --- a/regress/new-session-no-client.sh +++ b/regress/new-session-no-client.sh @@ -8,7 +8,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/new-session-size.sh b/regress/new-session-size.sh index 89fc580d..029bd6ee 100644 --- a/regress/new-session-size.sh +++ b/regress/new-session-size.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/new-window-command.sh b/regress/new-window-command.sh index b83376e4..18349115 100644 --- a/regress/new-window-command.sh +++ b/regress/new-window-command.sh @@ -6,7 +6,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/osc-11colours.sh b/regress/osc-11colours.sh index a049a49a..2fba76d8 100644 --- a/regress/osc-11colours.sh +++ b/regress/osc-11colours.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null $TMUX new -d diff --git a/regress/run-shell-output.sh b/regress/run-shell-output.sh index fd9634d6..3b44504c 100644 --- a/regress/run-shell-output.sh +++ b/regress/run-shell-output.sh @@ -7,7 +7,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/session-group-resize.sh b/regress/session-group-resize.sh index 84eb31a4..28e7a85e 100755 --- a/regress/session-group-resize.sh +++ b/regress/session-group-resize.sh @@ -10,7 +10,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null TMP1=$(mktemp) diff --git a/regress/style-trim.sh b/regress/style-trim.sh index c90d647d..5b03482d 100644 --- a/regress/style-trim.sh +++ b/regress/style-trim.sh @@ -17,9 +17,9 @@ if command -v bash >/dev/null 2>&1; then fi [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null -TMUX2="$TEST_TMUX -Ltest2" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" $TMUX2 kill-server 2>/dev/null $TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new -- $shell" diff --git a/regress/tty-keys.sh b/regress/tty-keys.sh index 0a1fe6bf..849f6f33 100644 --- a/regress/tty-keys.sh +++ b/regress/tty-keys.sh @@ -4,9 +4,9 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" $TMUX kill-server 2>/dev/null -TMUX2="$TEST_TMUX -Ltest2" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" $TMUX2 kill-server 2>/dev/null TMP=$(mktemp) diff --git a/regress/utf8-test.result b/regress/utf8-test.result index f15d049d..e700cb17 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/regress/utf8-test.sh b/regress/utf8-test.sh index 3b2b22c5..b3cf41bd 100644 --- a/regress/utf8-test.sh +++ b/regress/utf8-test.sh @@ -4,7 +4,7 @@ PATH=/bin:/usr/bin TERM=screen [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) -TMUX="$TEST_TMUX -Ltest" +TMUX="$TEST_TMUX -Ltest -f/dev/null" TMP=$(mktemp) trap "rm -f $TMP" 0 1 15 $TMUX kill-server 2>/dev/null diff --git a/screen-write.c b/screen-write.c index 6a327bc1..6c4543dc 100644 --- a/screen-write.c +++ b/screen-write.c @@ -627,21 +627,23 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, struct grid_line *gl, *sgl; struct grid_cell gc; u_int xx, yy, cx = s->cx, cy = s->cy; - int yoff = 0; + int xoff = 0, yoff = 0; struct visible_ranges *r; if (nx == 0 || ny == 0) return; - if (wp != NULL) + if (wp != NULL) { + xoff = wp->xoff; yoff = wp->yoff; + } for (yy = py; yy < py + ny; yy++) { if (yy >= gd->hsize + gd->sy) break; s->cx = cx; screen_write_initctx(ctx, &ttyctx, 0, 0); - r = screen_redraw_get_visible_ranges(wp, px, s->cy + yoff, nx, - NULL); + r = screen_redraw_get_visible_ranges(wp, xoff + s->cx, + s->cy + yoff, nx, NULL); for (xx = px; xx < px + nx; xx++) { gl = grid_get_line(gd, yy); sgl = grid_get_line(s->grid, s->cy); @@ -653,7 +655,7 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, break; grid_view_set_cell(s->grid, s->cx, s->cy, &gc); - if (!screen_redraw_is_visible(r, px)) + if (!screen_redraw_is_visible(r, xoff + s->cx)) break; ttyctx.cell = &gc; ttyctx.flags &= (TTY_CTX_OVERLAY_SYNC|TTY_CTX_SYNC); @@ -2925,16 +2927,22 @@ void screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, int cursor) { - struct tty_ctx ttyctx; - struct window_pane *wp = ctx->wp; + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + struct window_pane_resize *r, *r1; if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) return; screen_write_collect_flush(ctx, 0, __func__); - screen_alternate_on(ctx->s, gc, cursor); + if (!screen_alternate_on(ctx->s, gc, cursor)) + return; if (wp != NULL) { + TAILQ_FOREACH_SAFE (r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } layout_fix_panes(wp->window, NULL); server_redraw_window_borders(wp->window); } @@ -2956,7 +2964,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, return; screen_write_collect_flush(ctx, 0, __func__); - screen_alternate_off(ctx->s, gc, cursor); + if (!screen_alternate_off(ctx->s, gc, cursor)) + return; if (wp != NULL) { layout_fix_panes(wp->window, NULL); diff --git a/screen.c b/screen.c index 2c78c5ac..233f2d06 100644 --- a/screen.c +++ b/screen.c @@ -672,7 +672,7 @@ screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) * Enter alternative screen mode. A copy of the visible screen is saved and the * history is not updated. */ -void +int screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx, sy; @@ -681,7 +681,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) #endif if (SCREEN_IS_ALTERNATE(s)) - return; + return 0; sx = screen_size_x(s); sy = screen_size_y(s); @@ -703,10 +703,12 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) s->saved_flags = s->grid->flags; s->grid->flags &= ~GRID_HISTORY; + + return 1; } /* Exit alternate screen mode and restore the copied grid. */ -void +int screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx = screen_size_x(s), sy = screen_size_y(s); @@ -738,7 +740,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) s->cx = screen_size_x(s) - 1; if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; - return; + return 0; } /* Restore the saved grid. */ @@ -767,6 +769,8 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) s->cx = screen_size_x(s) - 1; if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; + + return 1; } /* Get mode as a string. */ diff --git a/server-client.c b/server-client.c index 1d2e430b..12be8f39 100644 --- a/server-client.c +++ b/server-client.c @@ -1623,10 +1623,7 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) static void server_client_check_pane_resize(struct window_pane *wp) { - struct window_pane_resize *r; - struct window_pane_resize *r1; - struct window_pane_resize *first; - struct window_pane_resize *last; + struct window_pane_resize *r, *r1, *first, *last; struct timeval tv = { .tv_usec = 250000 }; if (TAILQ_EMPTY(&wp->resize_queue)) @@ -1852,12 +1849,13 @@ server_client_reset_state(struct client *c) cx = wp->xoff + (int)s->cx - (int)ox; cy = wp->yoff + (int)s->cy - (int)oy; + r = screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL); + if (!screen_redraw_is_visible(r, cx)) + cursor = 0; + if (status_at_line(c) == 0) cy += status_line_size(c); } - r = screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL); - if (!screen_redraw_is_visible(r, cx)) - cursor = 0; if (!cursor) mode &= ~MODE_CURSOR; diff --git a/tmux.1 b/tmux.1 index ed91170f..baab6696 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1206,7 +1206,7 @@ Kill the .Nm server and clients and destroy all sessions. .It Xo Ic kill\-session -.Op Fl aC +.Op Fl aCg .Op Fl t Ar target\-session .Xc Destroy the given session, closing any windows linked to it and no other @@ -1214,6 +1214,10 @@ sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. +If +.Fl g +is given and the session is in a session group, all sessions in the group are +killed. The .Fl C flag clears alerts (bell, activity, or silence) in all windows linked to the @@ -2310,7 +2314,6 @@ cursor on that line. Scroll pane in copy\-mode when bound to a mouse drag event. .Fl e causes copy mode to exit when at the bottom. -.Pp .It Xo .Ic scroll\-top .Xc @@ -2666,7 +2669,7 @@ but a different format may be specified with .Fl F . .Tg capturep .It Xo Ic capture\-pane -.Op Fl aepPqCJMN +.Op Fl aeFHLpPqCJMN .Op Fl b Ar buffer\-name .Op Fl E Ar end\-line .Op Fl S Ar start\-line @@ -2706,6 +2709,26 @@ implies .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. +.Fl L +includes the line number at the start of each line and +.Fl F +includes the flags (where +.Ql - +is no flags, +.Ql D +is an unused line, +.Ql O +is a line marked as output, +.Ql P +is a line marked as a prompt, +.Ql X +is a line containing extended cells and +.Ql H +is a line with hyperlinks). +With +.Fl H , +only hyperlinks in the specified lines are captured. +Multiple hyperlinks on the same line are separated by spaces. .Pp .Fl S and @@ -2721,7 +2744,7 @@ the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose\-client -.Op Fl NryZ +.Op Fl hkNryZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -2792,12 +2815,20 @@ If a filter would lead to an empty list, it is ignored. specifies the format for each item in the list and .Fl K a format for each shortcut key; both are evaluated once for each line. +.Pp .Fl N starts without the preview or if given twice with the larger preview. -This command works only if at least one client is attached. +.Fl h +hides the pane containing the mode. +.Fl k +kills the pane when the mode is exited. +.Pp +The +.Ic choose-client +command works only if at least one client is attached. .It Xo .Ic choose\-tree -.Op Fl GNrswyZ +.Op Fl GhkNrswyZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -2882,15 +2913,30 @@ If a filter would lead to an empty list, it is ignored. specifies the format for each item in the tree and .Fl K a format for each shortcut key; both are evaluated once for each line. -.Fl N -starts without the preview or if given twice with the larger preview. .Fl G includes all sessions in any session groups in the tree rather than only the first. -This command works only if at least one client is attached. +.Pp +.Fl N +starts without the preview or if given twice with the larger preview. +.Fl h +hides the pane containing the mode. +.Fl k +kills the pane when the mode is exited. +.Fl h +and +.Fl k +are intended to ease use of the mode in a floating pane; for example: +.Bd -literal -offset indent +bind s 'new-pane -x50% -y50% -E; choose-tree -hks' +.Ed +.Pp +The +.Ic choose-tree +command works only if at least one client is attached. .It Xo .Ic customize\-mode -.Op Fl NZ +.Op Fl kNZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl t Ar target\-pane @@ -2936,9 +2982,15 @@ the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F specifies the format for each item in the tree. +.Pp .Fl N starts without the option information. -This command works only if at least one client is attached. +.Fl k +kills the pane when the mode is exited. +.Pp +The +.Ic customize-mode +command works only if at least one client is attached. .It Xo .Tg displayp .Ic display\-panes @@ -3009,7 +3061,9 @@ The default is .Fl Z zooms the pane. .Pp -This command works only if at least one client is attached. +The +.Ic find-window +command works only if at least one client is attached. .Tg floatp .It Xo Ic float\-pane .Op Fl h Ar height @@ -3371,6 +3425,7 @@ but a different format may be specified with .Op Fl s Ar style .Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane +.Op Fl T Ar title .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic newp @@ -3389,6 +3444,8 @@ sets the border style when the pane is active and .Fl R sets the border style when the pane is inactive (see .Sx STYLES ) . +.Fl T +sets the pane title. .Pp .Fl h does a horizontal split and @@ -3433,15 +3490,16 @@ but also sets the option for this pane to .Ar message . .Pp -An empty -.Ar shell\-command -(\[aq]\[aq]) will create a pane with no command running in it. +.Fl E , +or an empty +.Ar shell\-command , +(\[aq]\[aq]) will create an empty pane with no command running in it; +.Ic display-message +.Fl I +can write to an empty pane. The .Fl I -flag (if -.Ar shell\-command -is not specified or empty) -will create an empty pane and forward any output from stdin to it. +flag will create an empty pane and forward any output from stdin to it. For example: .Bd -literal -offset indent $ make 2>&1|tmux splitw \-dI & @@ -3785,6 +3843,7 @@ the command behaves like .Op Fl s Ar style .Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane +.Op Fl T Ar title .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic splitw @@ -4848,6 +4907,12 @@ command to show the indicator for the active pane. Set the colour used by the .Ic display\-panes command to show the indicators for inactive panes. +.It Ic display\-panes\-format Ar format +Set the +.Ar format +of the text shown by the +.Ic display\-panes +command, expanded for each pane. .It Ic display\-panes\-time Ar time Set the time in milliseconds for which the indicators shown by the .Ic display\-panes @@ -6375,9 +6440,15 @@ gives gives .Ql Sun Oct 25 09:25:02 2015 . Adding -.Ql p ( -.Ql `t/p` ) +.Ql p +.Pq Ql t/p will use shorter but less accurate time format for times in the past. +.Ql r +.Pq Ql t/r +will show the time relative to the current time, for example +.Ql \-1m +or +.Ql +2m23s . A custom format may be given using an .Ql f suffix (note that @@ -6405,12 +6476,17 @@ of the variable respectively. .Ql q:\& will escape .Xr sh 1 -special characters or with a +special characters; with a .Ql h suffix, escape hash characters (so .Ql # becomes -.Ql ## ) . +.Ql ## ) ; +or with +.Ql a +escape +.Nm +command arguments. .Ql E:\& will expand the format twice, for example .Ql #{E:status\-left} @@ -7703,7 +7779,7 @@ The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose\-buffer -.Op Fl NryZ +.Op Fl kNryZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -7774,7 +7850,11 @@ specifies the format for each item in the list and a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. -This command works only if at least one client is attached. +.Fl k +kills the pane when the mode is exited. +The +.Ic choose-buffer +command works only if at least one client is attached. .Tg clearhist .It Xo Ic clear\-history .Op Fl H diff --git a/tmux.h b/tmux.h index d64eaa5a..d5ffbdef 100644 --- a/tmux.h +++ b/tmux.h @@ -780,6 +780,7 @@ struct colour_palette { #define GRID_LINE_DEAD 0x4 #define GRID_LINE_START_PROMPT 0x8 #define GRID_LINE_START_OUTPUT 0x10 +#define GRID_LINE_HYPERLINK 0x20 /* Grid string flags. */ #define GRID_STRING_WITH_SEQUENCES 0x1 @@ -1180,6 +1181,8 @@ struct window_mode_entry { struct screen *screen; u_int prefix; + int kill; + TAILQ_ENTRY(window_mode_entry) entry; }; @@ -1270,7 +1273,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 #define PANE_VISITED 0x8 #define PANE_ZOOMED 0x10 -/* unused 0x20 */ +/* 0x20 unused */ #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 @@ -1482,8 +1485,7 @@ TAILQ_HEAD(layout_cells, layout_cell); struct layout_cell { enum layout_type type; -/* unused 0x1 */ -#define LAYOUT_CELL_FLOATING 0x2 +#define LAYOUT_CELL_FLOATING 0x1 int flags; struct layout_cell *parent; @@ -3397,8 +3399,8 @@ void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); int screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); -void screen_alternate_on(struct screen *, struct grid_cell *, int); -void screen_alternate_off(struct screen *, struct grid_cell *, int); +int screen_alternate_on(struct screen *, struct grid_cell *, int); +int screen_alternate_off(struct screen *, struct grid_cell *, int); const char *screen_mode_to_string(int); const char *screen_print(struct screen *, int); @@ -3547,12 +3549,13 @@ void layout_assign_pane(struct layout_cell *, struct window_pane *, int); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, int, int); +struct layout_cell *layout_floating_pane(struct window *, u_int, u_int, int, + int); void layout_close_pane(struct window_pane *); int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *, - struct window *, struct window_pane *, struct layout_cell *, - char **); + struct window *, struct window_pane *, char **); struct layout_cell *layout_get_tiled_cell(struct cmdq_item *, struct args *, struct window *, struct window_pane *, int, char **); diff --git a/window-client.c b/window-client.c index be1fd5fa..519222d9 100644 --- a/window-client.c +++ b/window-client.c @@ -82,6 +82,7 @@ struct window_client_modedata { char *format; char *key_format; char *command; + int hide_preview_this_pane; struct window_client_itemdata **item_list; u_int item_size; @@ -162,9 +163,10 @@ window_client_build(void *modedata, struct sort_criteria *sort_crit, } static void -window_client_draw(__unused void *modedata, void *itemdata, +window_client_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, u_int sx, u_int sy) { + struct window_client_modedata *data = modedata; struct window_client_itemdata *item = itemdata; struct client *c = item->c; struct screen *s = ctx->s; @@ -174,6 +176,12 @@ window_client_draw(__unused void *modedata, void *itemdata, if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return; wp = c->session->curw->window->active; + if (data->hide_preview_this_pane && wp == data->wp) { + if (!TAILQ_EMPTY(&c->session->curw->window->last_panes)) + wp = TAILQ_FIRST(&c->session->curw->window->last_panes); + else + wp = NULL; + } lines = status_line_size(c); if (lines >= sy) @@ -184,7 +192,8 @@ window_client_draw(__unused void *modedata, void *itemdata, at = 0; screen_write_cursormove(ctx, cx, cy + at, 0); - screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines); + if (wp != NULL) + screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines); if (at != 0) screen_write_cursormove(ctx, cx, cy + 2, 0); @@ -270,6 +279,7 @@ window_client_init(struct window_mode_entry *wme, wme->data = data = xcalloc(1, sizeof *data); data->wp = wp; + data->hide_preview_this_pane = args != NULL && args_has(args, 'h'); if (args == NULL || !args_has(args, 'F')) data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT); diff --git a/window-tree.c b/window-tree.c index b8681069..7acb477c 100644 --- a/window-tree.c +++ b/window-tree.c @@ -113,6 +113,7 @@ struct window_tree_modedata { char *key_format; char *command; int squash_groups; + int hide_preview_this_pane; int prompt_flags; struct window_tree_itemdata **item_list; @@ -289,6 +290,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, if (n == 0) goto empty; for (i = 0; i < n; i++) { + if (data->hide_preview_this_pane && l[i] == data->wp) + continue; if (window_tree_filter_pane(s, wl, l[i], filter)) window_tree_build_pane(s, wl, l[i], modedata, mti); } @@ -580,6 +583,10 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, struct options *oo; total = window_count_panes(w, 1); + if (data->hide_preview_this_pane && data->wp->window == w) + total--; + if (total == 0) + return; if (sx / total < 24) { visible = sx / 24; @@ -590,6 +597,8 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, current = 0; TAILQ_FOREACH(wp, &w->panes, entry) { + if (data->hide_preview_this_pane && wp == data->wp) + continue; if (wp == w->active) break; current++; @@ -653,6 +662,8 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, i = loop = 0; TAILQ_FOREACH(wp, &w->panes, entry) { + if (data->hide_preview_this_pane && wp == data->wp) + continue; if (loop == end) break; if (loop < start) { @@ -704,6 +715,7 @@ static void window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, u_int sx, u_int sy) { + struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item = itemdata; struct session *sp; struct winlink *wl; @@ -723,7 +735,8 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, window_tree_draw_window(modedata, sp, wl, ctx, sx, sy); break; case WINDOW_TREE_PANE: - screen_write_preview(ctx, &wp->base, sx, sy); + if (!data->hide_preview_this_pane || wp != data->wp) + screen_write_preview(ctx, &wp->base, sx, sy); break; } } @@ -932,6 +945,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, else data->command = xstrdup(args_string(args, 0)); data->squash_groups = !args_has(args, 'G'); + data->hide_preview_this_pane = args_has(args, 'h'); if (args_has(args, 'y')) data->prompt_flags = PROMPT_ACCEPT; diff --git a/window.c b/window.c index e0417ec9..24699748 100644 --- a/window.c +++ b/window.c @@ -1280,6 +1280,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, TAILQ_INSERT_HEAD(&wp->modes, wme, entry); wme->screen = wme->mode->init(wme, fs, args); } + wme->kill = args_has(args, 'k'); wp->screen = wme->screen; wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED); @@ -1297,11 +1298,13 @@ window_pane_reset_mode(struct window_pane *wp) { struct window_mode_entry *wme, *next; struct window *w = wp->window; + int kill; if (TAILQ_EMPTY(&wp->modes)) return; wme = TAILQ_FIRST(&wp->modes); + kill = wme->kill; TAILQ_REMOVE(&wp->modes, wme, entry); wme->mode->free(wme); free(wme); @@ -1324,6 +1327,9 @@ window_pane_reset_mode(struct window_pane *wp) server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); + + if (kill) + server_kill_pane(wp); } void