Compare commits

..
Author SHA1 Message Date
Michael GrantandClaude Sonnet 5 a8f2f1101c Fix select-output losing a prior non-empty output to a later empty one
The output-commands.sh ":empty" test packed two full OSC133 C/D
cycles onto a single grid row with no newlines around them, which
does not reflect how real shell integrations behave (Enter always
echoes a newline). Give it the newline a real terminal would emit so
"echo" and the following no-output "true" land on separate rows,
matching realistic OSC133 usage (same idea as the earlier
scroll-on-clear fix for the ":clear" case).

With that in place, window_copy_find_previous_output_range still
failed: once cursor sits at/after a command with genuinely empty
output (e.g. "true"), the loop's cursor-row candidate unconditionally
overwrote an already-found earlier non-empty output ("echo" -> "one")
with the later, empty one. Keep the previously-found non-empty match
instead of downgrading it, so select-output/copy-output correctly
reach back to the last output that actually had content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 15:36:09 +01:00
Michael Grant ff2ffd0f20 Bugfix: OSC133 tracking can lose the C marker (start-of-output) for whatever was on screen at the time when screen and copy buffer gets cleared. 2026-09-20 15:23:04 +01:00
Michael Grant df134f38bd Merge updated OSC 133 output commands 2026-09-14 06:26:06 +01:00
Michael Grant a8b05dd7d7 Add pipe-selection copy mode command 2026-09-14 06:25:40 +01:00
Michael Grant 676ad44c96 Merge OSC 133 output commands with fold view 2026-09-14 05:43:59 +01:00
Michael Grant 71865a58d1 Add OSC 133 copy mode output commands 2026-09-14 05:35:34 +01:00
Michael Grant 9837766857 Allow callers to override editor command 2026-09-14 05:35:00 +01:00
tmux update bot e880cf63e0 Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'
* refs/remotes/tmux-openbsd/master:
  Add rounded borders option for panes like popups.
2026-09-11 17:00:20 +00:00
nicm 325b40e8c4 Add rounded borders option for panes like popups. 2026-09-11 17:00:18 +00:00
tmux update bot f95363aa83 Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'
* refs/remotes/tmux-openbsd/master:
  Rather than allowing floating panes to remain outside the window and invisible after resize, move them and resize them so they are fully inside the window. GitHub issue 5582 from Noam Stolero.
2026-09-11 12:58:31 +00:00
nicm f8fc53b6f8 Rather than allowing floating panes to remain outside the window and
invisible after resize, move them and resize them so they are fully
inside the window. GitHub issue 5582 from Noam Stolero.
2026-09-11 12:58:29 +00:00
Nicholas Marriott d215280b00 Add rounded border tests. 2026-09-11 11:17:35 +01:00
Nicholas Marriott d859a0f410 Test for resizing floating panes, from Noam Stolero. 2026-09-11 09:16:55 +01:00
Nicholas Marriott 10d00cc7cb Modal pane test changes. 2026-09-11 09:10:14 +01:00
Nicholas Marriott 14a01dd337 Note changes from Heon Jeong. 2026-09-11 09:05:06 +01:00
tmux update bot a958883625 Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'
* refs/remotes/tmux-openbsd/master:
  Add remain-on-exit failed-key and a -D flag to new-pane to have a modal pane wait for Escape/C-c. Both to allow better compatibility with popups.
2026-09-10 13:04:13 +00:00
nicm d202aa7ac1 Add remain-on-exit failed-key and a -D flag to new-pane to have a modal
pane wait for Escape/C-c. Both to allow better compatibility with
popups.
2026-09-10 13:04:11 +00:00
Nicholas Marriott d9a5e82678 Fix tests. 2026-09-10 12:00:01 +01:00
tmux update bot 13c10f672c Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'
* refs/remotes/tmux-openbsd/master:
  Add a nesting limit for v1 layouts, reported by M Khalilov, fix based on issue 5572 from Afonso Januário. Also tweak some language while here.
2026-09-09 19:23:09 +00:00
nicm b7c5030004 Add a nesting limit for v1 layouts, reported by M Khalilov, fix based on
issue 5572 from Afonso Januário. Also tweak some language while here.
2026-09-09 19:23:07 +00:00
Nicholas Marriott 4839123510 Tweak changes. 2026-09-09 13:50:42 +01:00
Nicholas Marriott 9b3268a2a0 Bump version. 2026-09-09 13:28:07 +01:00
Michael Grant 6294be4986 Merge branch 'master' into osc133_line_collapsible 2026-09-05 15:15:29 +01:00
Michael Grant c5a5b99788 Rename output to fold in places for consistency. 2026-08-01 11:59:18 +01:00
Michael Grant e23219ec04 Add comments during my review including one fix, separator text after D also expandable. 2026-08-01 11:47:25 +01:00
Michael Grant 0f29c47278 Cleanup. Rename things for consistency. Fix indenting. 2026-08-01 08:26:05 +01:00
Michael Grant 7c9fdcb770 Add k=c and a helper function to check for secondary prompt. 2026-07-31 10:39:09 +01:00
Michael Grant 0330d8ac57 Change line-numbers menu to On/Off. 2026-07-29 23:35:11 +01:00
Michael Grant 0a7788b66f Add Fold View menu item and O key to toggle fold view. 2026-07-29 23:25:36 +01:00
Michael Grant 458d045bd2 With collapsible output active, line-numbers-on now shows each visible line’s original buffer line number, so hidden lines remain counted. 2026-07-29 22:10:19 +01:00
Michael Grant a81154d6d9 Fix missing ! before commands with no output and non-zero exit status. 2026-07-22 23:40:01 +01:00
Michael Grant cffc86dae5 Merge branch 'osc133_line_collapsible' of github.com:tmux/tmux into osc133_line_collapsible 2026-07-22 11:39:04 +01:00
Michael Grant 4cfd155ffc Add copy-mode collapse output regression test. 2026-07-22 11:38:07 +01:00
Michael Grant 670ea5d083 Add regression test for copy-mode-exit-status. 2026-07-22 11:35:59 +01:00
Michael Grant eaeb234dd4 Remove code to put exit-code on right and go for just simple ! on left of +. 2026-07-22 11:35:59 +01:00
Michael Grant 5948722d5a Add highlighting to the current line. 2026-07-22 11:35:59 +01:00
Michael Grant d8ad7cd9c5 Add exit status on right. 2026-07-22 11:35:59 +01:00
Michael Grant b49cc2d936 Fix stray L on empty commands. 2026-07-22 11:35:59 +01:00
Michael Grant abb7108e25 Add vertical bar next to expanded lines. 2026-07-22 11:35:59 +01:00
Michael Grant d080378464 Add collapsible lines. 2026-07-22 11:35:59 +01:00
Michael Grant 473eca949a Add regression test for copy-mode-exit-status. 2026-07-22 11:35:28 +01:00
Michael Grant f0eaa5e9d6 Remove code to put exit-code on right and go for just simple ! on left of +. 2026-07-22 11:33:46 +01:00
Michael Grant f44246c764 Add highlighting to the current line. 2026-07-21 19:19:31 +01:00
Michael Grant 5d4d41da57 Add exit status on right. 2026-07-21 18:48:16 +01:00
Michael Grant bacbb4d9d8 Fix stray L on empty commands. 2026-07-21 15:03:52 +01:00
Michael Grant 07a3a16ae7 Add vertical bar next to expanded lines. 2026-07-21 07:42:20 +01:00
Michael Grant f41288ee27 Add collapsible lines. 2026-07-20 16:26:02 +01:00
Nicholas Marriott 457b6cbefd Put OSC 133 data into grid_line. 2026-07-20 11:24:33 +01:00
66 changed files with 2295 additions and 1194 deletions
+2 -2
View File
@@ -72,14 +72,14 @@ jobs:
- name: build
run: |
sh autogen.sh
sh autogen.sh
./configure ${{ matrix.configure }}
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
- name: test
run: |
cd regress
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
${{ matrix.make }}
- name: logs
if: failure()
+5 -34
View File
@@ -10,8 +10,8 @@ CHANGES FROM 3.7c TO 3.8
- modal panes may be created with new-pane -O; a window can have one modal
pane and it prevents interaction with the other panes while it is active.
-C closes the pane when the mouse is clicked outside it and -K sends all
keys, including the prefix key, to the pane. -D closes the modal pane with
Escape or C-c. A modal pane is now used for the editor in buffer mode;
keys, including the prefix key, to the pane. A modal pane is now used for
the editor in buffer mode;
- new-pane -A creates a floating pane which remains above a zoomed pane;
modal panes do this by default;
@@ -39,9 +39,6 @@ CHANGES FROM 3.7c TO 3.8
The old format is still accepted; control mode clients receive old layouts
unless they set the new-layouts flag.
* Add remain-on-exit failed-key to keep a pane open after a failed command
until a key is pressed.
* Add support for themes and improve default colours:
- tmux now has builtin light and dark colour themes for terminals with 256 or
@@ -118,12 +115,6 @@ CHANGES FROM 3.7c TO 3.8
in terminal-features, and add a utf8 feature for terminals which support
UTF-8 output.
* Add an appesc terminal feature for mintty's application escape key mode,
avoiding unsupported sequences on other terminals (Pete Dietl, issue 5626).
* Preserve existing Ss and Se terminfo capabilities for cursor styles and
restoring the default cursor (issue 5476).
* Add a #{A/count:frames} modifier to show a series of frames as an animation
in the status line (issue 5412 from Fernando Daciuk).
@@ -198,23 +189,10 @@ CHANGES FROM 3.7c TO 3.8
sent inside %begin/%end (issue 5458), do not let a stuck client prevent the
server from exiting (issue 5444), and reset control mode offsets when a pane
is respawned (issue 5498). Bound buffered command replies so a control mode
client that stops reading cannot consume unlimited memory (issue 5565), and
fix a collision between the output discard and bracketed paste flags (issue
5622).
client that stops reading cannot consume unlimited memory (issue 5565).
* Fix grouped sessions sometimes being left as unusable command targets while
they are being killed (Bryce Miller, issue 5180), and prevent reuse of a
session with no windows while detaching (Heon Jeong, issue 5620).
* Free wait-for clients when they disconnect and do not run their remaining
queued commands (issue 5614).
* Expand formats in run-shell -c like other start-directory flags (reported by
Saul Nogueras).
* Fix session alert flag formats to check all windows (issue 5599).
* Do not clear the marked pane when clicking on a pane border.
they are being killed (Bryce Miller, issue 5180).
* Fix choose mode filtering when more than one pane exists (issue 5326), and
fix an infinite loop in customize mode when a filter does not match.
@@ -225,18 +203,11 @@ CHANGES FROM 3.7c TO 3.8
status lines, menus, popups, wide characters and floating panes. This
includes fixes for flicker with alternate screen applications and scrollbars
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
5098), and misplaced wrapped lines during partial redraws (Ben Maurer, issue
5625).
5098).
* Do not crash looking for the next or previous session (issue 5344), or when
no client is available.
* Fix crashes when destroying a zoomed pane (Romain Francoise, issue 5591),
copying from a blank source pane (Kaixuan Li), or opening a mode prompt when
the client is offset into the window.
* Avoid an infinite loop when assigning a width to WCHAR_MAX (issue 5602).
CHANGES FROM 3.7b TO 3.7c
* Build with jemalloc on macOS to avoid what appears to be a bug in calloc
+2 -2
View File
@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "dekHMqSs:t:u", 0, 0, NULL },
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.args = { "UcdekHMqSs:t:u", 0, 0, NULL },
.usage = "[-UcdekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
+5 -14
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.75 2026/09/21 10:33:16 nicm Exp $ */
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -133,7 +133,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
} else if (strcmp(position, "back") == 0) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating_with_hidden(owp))
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
@@ -142,30 +142,24 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
} else if (strcmp(position, "forward") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
while (owp != NULL && owp->layout_cell == NULL)
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
if (owp != NULL) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_BEFORE(owp, wp, zentry);
}
} else if (strcmp(position, "backward") == 0) {
owp = TAILQ_NEXT(wp, zentry);
while (owp != NULL && owp->layout_cell == NULL)
owp = TAILQ_NEXT(owp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
}
} else if (strcmp(position, "forward-loop") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
while (owp != NULL && owp->layout_cell == NULL)
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else {
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating_with_hidden(owp))
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
@@ -175,8 +169,6 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
}
} else if (strcmp(position, "backward-loop") == 0) {
owp = TAILQ_NEXT(wp, zentry);
while (owp != NULL && owp->layout_cell == NULL)
owp = TAILQ_NEXT(owp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
@@ -355,10 +347,8 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
n = 0;
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating_with_hidden(owp))
if (!window_pane_is_floating(owp))
break;
if (owp->layout_cell == NULL)
continue;
if (n >= z)
break;
n++;
@@ -453,6 +443,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
if ((s = args_get(args, 'P')) != NULL)
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
if ((s = args_get(args, 'z')) != NULL)
+1 -4
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-queue.c,v 1.124 2026/09/22 06:46:50 nicm Exp $ */
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -580,9 +580,6 @@ cmdq_fire_command(struct cmdq_item *item)
int flags, quiet = 0;
char *tmp;
if (item->client != NULL && (item->client->flags & CLIENT_DEAD))
return (CMD_RETURN_ERROR);
if (cfg_finished)
cmdq_add_message(item);
if (log_get_level() > 1) {
+2 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-pane.c,v 1.69 2026/09/21 10:33:16 nicm Exp $ */
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -91,8 +91,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
if (!window_pane_is_floating(wp))
server_unzoom_window(w);
server_unzoom_window(w);
lc = wp->layout_cell; /* may have been replaced by unzoom */
if (args_has(args, 'x')) {
+2 -2
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-run-shell.c,v 1.95 2026/09/20 07:59:55 nicm Exp $ */
/* $OpenBSD: cmd-run-shell.c,v 1.94 2026/08/25 06:04:33 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -164,7 +164,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
if (cdata->client != NULL)
cdata->client->references++;
if (args_has(args, 'c'))
cdata->cwd = format_single_from_target(item, args_get(args, 'c'));
cdata->cwd = xstrdup(args_get(args, 'c'));
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
+1 -35
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-wait-for.c,v 1.24 2026/09/22 06:46:50 nicm Exp $ */
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -480,40 +480,6 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
return (CMD_RETURN_NORMAL);
}
void
cmd_wait_for_client_lost(struct client *c)
{
struct wait_channel *wc, *wc1;
struct wait_item *wi, *wi1;
struct wait_event_item *wei, *wei1;
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
if (cmdq_get_client(wei->item) == c) {
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
}
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
if (cmdq_get_client(wi->item) == c) {
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
}
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
if (cmdq_get_client(wi->item) == c) {
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
}
}
cmd_wait_for_remove_empty(wc);
}
}
void
cmd_wait_for_flush(void)
{
+1 -1
View File
@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.8-rc2)
AC_INIT([tmux], next-3.9)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
+6 -6
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.418 2026/09/20 08:19:31 nicm Exp $ */
/* $OpenBSD: format.c,v 1.417 2026/09/08 15:42:26 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2876,10 +2876,10 @@ format_cb_session_activity_flag(struct format_tree *ft)
if (ft->s != NULL) {
RB_FOREACH(wl, winlinks, &ft->s->windows) {
if (wl->flags & WINLINK_ACTIVITY)
if (ft->wl->flags & WINLINK_ACTIVITY)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (xstrdup("0"));
}
return (NULL);
}
@@ -2894,8 +2894,8 @@ format_cb_session_bell_flag(struct format_tree *ft)
RB_FOREACH(wl, winlinks, &ft->s->windows) {
if (wl->flags & WINLINK_BELL)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (xstrdup("0"));
}
return (NULL);
}
@@ -2908,10 +2908,10 @@ format_cb_session_silence_flag(struct format_tree *ft)
if (ft->s != NULL) {
RB_FOREACH(wl, winlinks, &ft->s->windows) {
if (wl->flags & WINLINK_SILENCE)
if (ft->wl->flags & WINLINK_SILENCE)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (xstrdup("0"));
}
return (NULL);
}
+2
View File
@@ -1760,6 +1760,8 @@ grid_line_flags_string(int flags)
strlcat(s, "START_OUTPUT,", sizeof s);
if (flags & GRID_LINE_END_OUTPUT)
strlcat(s, "END_OUTPUT,", sizeof s);
if (flags & GRID_LINE_END_OUTPUT_STATUS)
strlcat(s, "END_OUTPUT_STATUS,", sizeof s);
if (flags & GRID_LINE_HYPERLINK)
strlcat(s, "HYPERLINK,", sizeof s);
if (*s == '\0')
+45 -18
View File
@@ -3194,13 +3194,14 @@ input_osc_112(struct input_ctx *ictx, const char *p)
/* Parse the OSC 133 D exit status. */
static int
input_osc_133_exit_status(const char *p)
input_osc_133_exit_status(const char *p, int *present)
{
const char *end;
char *copy;
const char *errstr;
char *endptr;
long long status;
*present = 0;
if (p[1] != ';' || p[2] == '\0' || strchr(p + 2, '=') == p + 2)
return (0);
end = strchr(p + 2, ';');
@@ -3214,9 +3215,11 @@ input_osc_133_exit_status(const char *p)
free(copy);
return (0);
}
status = strtonum(copy, 0, 255, &errstr);
*present = 1;
errno = 0;
status = (int)strtol(copy, &endptr, 10);
free(copy);
if (errstr != NULL)
if (errno != 0 || endptr == copy || status < 0 || status > 255)
return (255);
return (status);
}
@@ -3261,6 +3264,22 @@ input_fire_command_event(struct window_pane *wp, const char *name)
events_fire(name, ep);
}
/* Check if an OSC 133 prompt is secondary or a continuation. */
static int
input_osc_133_secondary_prompt(const char *p)
{
const char *cp;
while ((cp = strstr(p, ";k=")) != NULL) {
cp += 3;
if ((*cp == 's' || *cp == 'c') &&
(cp[1] == '\0' || cp[1] == ';'))
return (1);
p = cp;
}
return (0);
}
/* Handle the OSC 133 sequence. */
static void
input_osc_133(struct input_ctx *ictx, const char *p)
@@ -3270,8 +3289,7 @@ input_osc_133(struct input_ctx *ictx, const char *p)
struct grid *gd = s->grid;
u_int line = s->cy + gd->hsize;
struct grid_line *gl = NULL;
const char *cp;
int status;
int status, status_present;
if (line < gd->hsize + gd->sy)
gl = grid_get_line(gd, line);
@@ -3280,9 +3298,14 @@ input_osc_133(struct input_ctx *ictx, const char *p)
case 'A':
case 'N':
if (gl != NULL) {
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
gl->osc133_data.prompt_col = s->cx;
gl->flags |= GRID_LINE_START_PROMPT;
if (!(gl->flags & (GRID_LINE_START_PROMPT|
GRID_LINE_SECOND_PROMPT))) {
gl->osc133_data.prompt_col = s->cx;
if (input_osc_133_secondary_prompt(p))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
}
}
if (wp != NULL) {
wp->last_prompt_time = time(NULL);
@@ -3291,23 +3314,25 @@ input_osc_133(struct input_ctx *ictx, const char *p)
break;
case 'P':
if (gl != NULL) {
cp = strstr(p, ";k=s");
if (cp != NULL && (cp[4] == ';' || cp[4] == '\0'))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
gl->osc133_data.prompt_col = s->cx;
if (!(gl->flags & (GRID_LINE_START_PROMPT|
GRID_LINE_SECOND_PROMPT))) {
gl->osc133_data.prompt_col = s->cx;
if (input_osc_133_secondary_prompt(p))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
}
}
break;
case 'B':
case 'I':
if (gl != NULL) {
if (gl != NULL && !(gl->flags & GRID_LINE_START_COMMAND)) {
gl->flags |= GRID_LINE_START_COMMAND;
gl->osc133_data.cmd_col = s->cx;
}
break;
case 'C':
if (gl != NULL) {
if (gl != NULL && !(gl->flags & GRID_LINE_START_OUTPUT)) {
gl->flags |= GRID_LINE_START_OUTPUT;
gl->osc133_data.out_start_col = s->cx;
}
@@ -3320,7 +3345,7 @@ input_osc_133(struct input_ctx *ictx, const char *p)
}
break;
case 'D':
status = input_osc_133_exit_status(p);
status = input_osc_133_exit_status(p, &status_present);
if (wp != NULL) {
wp->cmd_end_time = time(NULL);
wp->flags &= ~PANE_CMDRUNNING;
@@ -3329,6 +3354,8 @@ input_osc_133(struct input_ctx *ictx, const char *p)
}
if (gl != NULL) {
gl->flags |= GRID_LINE_END_OUTPUT;
if (status_present)
gl->flags |= GRID_LINE_END_OUTPUT_STATUS;
gl->osc133_data.out_end_col = s->cx;
gl->osc133_data.exit_status = status;
}
+17 -5
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
/* $OpenBSD: key-bindings.c,v 1.192 2026/08/06 09:05:04 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -52,7 +52,9 @@
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
" ''" \
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_line_numbers,Hide Line Numbers,Show Line Numbers},}' 'L' {send -X line-numbers-toggle}" \
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_line_numbers,Line Numbers Off,Line Numbers On},}' 'L' {send -X line-numbers-toggle}" \
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_fold_view,Fold View Off,Fold View On},}' 'O' {send -X fold-view-toggle}" \
" '#{?#{==:#{pane_mode},copy-mode},Edit,}' 'e' {if -F '#{selection_present}' {send -X open-selection} {send -X open-output}}" \
" '#{?#{==:#{pane_mode},copy-mode},#{?refresh_active,Refresh Off,Refresh On},}' 'r' {send -X refresh-toggle}" \
" ''" \
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
@@ -523,7 +525,7 @@ key_bindings_init(void)
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
/* Mouse button 1 on border. */
"bind -n MouseDown1Border { select-pane -t= }",
"bind -n MouseDown1Border { select-pane -M }",
/* Mouse button 1 drag on border. */
"bind -n MouseDrag1Border { resize-pane -M }",
@@ -575,6 +577,7 @@ key_bindings_init(void)
"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-o { send -X select-output }",
"bind -Tcopy-mode C-p { send -X cursor-up }",
"bind -Tcopy-mode C-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
@@ -583,15 +586,19 @@ key_bindings_init(void)
"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 Tab { send -X toggle-output }",
"bind -Tcopy-mode BTab { send -X toggle-output -a }",
"bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode L { send -X line-numbers-toggle }",
"bind -Tcopy-mode N { send -X search-reverse }",
"bind -Tcopy-mode O { send -X fold-view-toggle }",
"bind -Tcopy-mode P { send -X toggle-position }",
"bind -Tcopy-mode R { send -X rectangle-toggle }",
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode X { send -X set-mark }",
"bind -Tcopy-mode e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode n { send -X search-again }",
@@ -600,7 +607,7 @@ key_bindings_init(void)
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode Home { send -X start-of-line }",
"bind -Tcopy-mode End { send -X end-of-line }",
"bind -Tcopy-mode MouseDown1Pane select-pane",
"bind -Tcopy-mode MouseDown1Pane { select-pane; send -X toggle-output -m }",
"bind -Tcopy-mode MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode WheelUpPane { select-pane; send -N5 -X scroll-up }",
@@ -660,6 +667,8 @@ key_bindings_init(void)
"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 Tab { send -X toggle-output }",
"bind -Tcopy-mode-vi BTab { send -X toggle-output -a }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }",
@@ -687,7 +696,10 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi K { send -X scroll-up }",
"bind -Tcopy-mode-vi L { send -X bottom-line }",
"bind -Tcopy-mode-vi M { send -X middle-line }",
"bind -Tcopy-mode-vi M-e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
"bind -Tcopy-mode-vi M-o { send -X select-output }",
"bind -Tcopy-mode-vi N { send -X search-reverse }",
"bind -Tcopy-mode-vi O { send -X fold-view-toggle }",
"bind -Tcopy-mode-vi P { send -X toggle-position }",
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi V { send -X select-line }",
@@ -715,7 +727,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
"bind -Tcopy-mode-vi End { send -X end-of-line }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane; send -X toggle-output -m }",
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode-vi WheelUpPane { select-pane; send -N5 -X scroll-up }",
+19 -53
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: layout-custom.c,v 1.43 2026/09/22 06:48:01 nicm Exp $ */
/* $OpenBSD: layout-custom.c,v 1.41 2026/09/09 09:01:19 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -283,59 +283,26 @@ layout_checksum(const char *layout)
char *
layout_dump(__unused struct window *w, struct layout_cell *lcroot, int flags)
{
struct layout_string layout_string = { 0 };
char *out;
struct layout_string layout_string;
char *out = NULL;
if (lcroot == NULL)
goto bad;
return NULL;
layout_string_init(&layout_string);
if (layout_append(lcroot, &layout_string, flags) != 0)
goto bad;
if (~flags & LAYOUT_CUSTOM_OLD_FORMAT)
xasprintf(&out, "{\"V\":2,\"L\":%s}", layout_string.dat);
else {
xasprintf(&out, "%04hx,%s", layout_checksum(layout_string.dat),
layout_string.dat);
}
layout_string_free(&layout_string);
return (out);
bad:
layout_string_free(&layout_string);
if (~flags & LAYOUT_CUSTOM_OLD_FORMAT)
return (NULL);
return (xstrdup("0000,"));
}
/* Get a floating pane cell's z-index in the layout being dumped. */
static u_int
layout_cell_zindex(struct layout_cell *lc)
{
struct window_pane *wp = lc->wp, *wq;
struct window *w = wp->window;
struct layout_cell *other;
int saved = (lc == wp->saved_layout_cell);
u_int i = 0;
if (saved &&
w->active != NULL &&
(w->active->flags & PANE_ZOOMED) &&
(w->active->saved_layout_cell->flags & LAYOUT_CELL_FLOATING)) {
if (wp == w->active)
return (0);
i++;
}
TAILQ_FOREACH(wq, &w->z_index, zentry) {
if (wq == wp)
break;
if (saved)
other = wq->saved_layout_cell;
if (layout_append(lcroot, &layout_string, flags) == 0) {
if (flags & LAYOUT_CUSTOM_OLD_FORMAT)
xasprintf(&out, "%04hx,%s",
layout_checksum(layout_string.dat),
layout_string.dat);
else
other = wq->layout_cell;
if (other != NULL && (other->flags & LAYOUT_CELL_FLOATING))
i++;
xasprintf(&out, "{\"V\":2,\"L\":%s}",
layout_string.dat);
}
return (i);
layout_string_free(&layout_string);
return (out);
}
/* Append information for a single cell in a JSON (v2) format. */
@@ -346,7 +313,7 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
struct window_pane *wp;
enum layout_type type;
char c;
u_int i, n, z;
u_int i, n;
if (lc == NULL)
return (-1);
@@ -387,10 +354,9 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
if (window_pane_index(wp, &i) != 0)
return (-1);
layout_string_write(ls, ",\"i\":%u", i);
if (lc->flags & LAYOUT_CELL_FLOATING) {
z = layout_cell_zindex(lc);
layout_string_write(ls, ",\"z\":%u", z);
}
if ((lc->flags & LAYOUT_CELL_FLOATING) &&
window_pane_zindex(wp, &i) == 0)
layout_string_write(ls, ",\"z\":%u", i);
layout_string_write(ls, ",\"I\":\"%%%u\"", wp->id);
}
+4 -16
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: layout.c,v 1.101 2026/09/20 08:42:46 nicm Exp $ */
/* $OpenBSD: layout.c,v 1.100 2026/09/11 08:16:14 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1728,7 +1728,7 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
char **cause)
{
int sx, sy, ox, oy, pad;
int sx, sy, ox, oy;
char *error = NULL;
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
@@ -1777,20 +1777,12 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
}
}
if (!window_has_floating_panes(w)) {
w->last_new_pane_x = 0;
w->last_new_pane_y = 0;
}
if (ox == INT_MAX) {
if (w->last_new_pane_x == 0)
ox = 4;
else {
if (lines != PANE_LINES_NONE)
pad = 1;
else
pad = 0;
ox = w->last_new_pane_x + 4;
if (ox + sx + pad > (int)w->sx)
if (w->last_new_pane_x > w->sx)
ox = 4;
}
w->last_new_pane_x = ox;
@@ -1801,12 +1793,8 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
if (w->last_new_pane_y == 0)
oy = 2;
else {
if (lines != PANE_LINES_NONE)
pad = 1;
else
pad = 0;
oy = w->last_new_pane_y + 2;
if (oy + sy + pad > (int)w->sy)
if (w->last_new_pane_y > w->sy)
oy = 2;
}
w->last_new_pane_y = oy;
+10 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.245 2026/09/10 11:02:18 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.246 2026/09/11 10:17:16 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -76,7 +76,8 @@ static const char *options_table_pane_border_indicators_list[] = {
"off", "colour", "arrows", "both", NULL
};
static const char *options_table_pane_border_lines_list[] = {
"single", "double", "heavy", "simple", "number", "spaces", "none", NULL
"single", "double", "heavy", "simple", "number", "spaces", "none",
"rounded", NULL
};
static const char *options_table_popup_border_lines_list[] = {
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
@@ -114,7 +115,6 @@ static const char *options_table_theme_list[] = {
static const char *options_table_copy_mode_line_numbers_list[] = {
"off", "default", "absolute", "relative", "hybrid", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{E:status-left-style}]" \
@@ -1384,6 +1384,13 @@ const struct options_table_entry options_table[] = {
.text = "Style of search matches in copy mode."
},
{ .name = "copy-mode-exit-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?exit_status,#[fg=themered]!, }",
.text = "Format of OSC 133 exit status indicator in copy mode."
},
{ .name = "copy-mode-current-match-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
+20 -23
View File
@@ -1,17 +1,33 @@
TESTS!= echo *.sh
LOGDIR=logs
.PHONY: all prepare $(TESTS)
.PHONY: all
.NOTPARALLEL: all
all: prepare $(TESTS)
@failed=0; failures=; \
all:
@mkdir -p "$(LOGDIR)"; \
rm -f "$(LOGDIR)"/*.log; \
failed=0; failures=; \
for test in $(TESTS); do \
base=$${test##*/}; \
log="$(LOGDIR)/$${base%.sh}.log"; \
if [ -f "$$log" ]; then \
rm -f "$$log"; \
printf '%-40s ' "$$test"; \
start=$$(date +%s); \
env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
sh -x "$$test" >"$$log" 2>&1; \
if [ $$? -eq 0 ]; then \
end=$$(date +%s); \
rm -f "$$log"; \
echo "PASS ($$((end - start))s)"; \
else \
end=$$(date +%s); \
echo "FAIL ($$((end - start))s)"; \
echo " log: $$log"; \
failed=1; \
failures="$$failures $$test"; \
fi; \
sleep 1; \
done; \
if [ "$$failed" -ne 0 ]; then \
echo; \
@@ -23,22 +39,3 @@ all: prepare $(TESTS)
rmdir "$(LOGDIR)" 2>/dev/null || true; \
fi; \
exit $$failed
prepare:
@mkdir -p "$(LOGDIR)"
@rm -f "$(LOGDIR)"/*.log
$(TESTS): prepare
@base="$@"; base=$${base##*/}; \
log="$(LOGDIR)/$${base%.sh}.log"; \
start=$$(date +%s); \
if env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
sh -x "$@" >"$$log" 2>&1; then \
end=$$(date +%s); \
rm -f "$$log"; \
printf '%-40s PASS (%ss)\n' "$@" "$$((end - start))"; \
else \
end=$$(date +%s); \
printf '%-40s FAIL (%ss)\n log: %s\n' \
"$@" "$$((end - start))" "$$log"; \
fi
+2 -32
View File
@@ -2,9 +2,8 @@
# monitor-activity, monitor-bell and monitor-silence: both the
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
# flags (window_activity_flag, window_bell_flag, window_silence_flag,
# their session equivalents and the #, !, ~ characters in window_flags).
# The sessions are detached so
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
# the #, !, ~ characters in window_flags). The sessions are detached so
# alert flags are set even on the current window; the *-action options
# still decide whether the hooks fire. Panes run cat: activity is
# generated by the tty echo of send-keys and a bell by sending a BEL and
@@ -112,24 +111,6 @@ flags_have()
esac
}
assert_session_flag()
{
flag="#{session_${1}_flag}"
expected=$2
# Only mon exists here. Check both window contexts and list-sessions.
for target in mon:w0 "mon:$3"; do
value=$($TMUX display -pt "$target" "$flag") ||
fail "display $flag failed"
[ "$value" = "$expected" ] ||
fail "expected $flag for $target to be '$expected' but got '$value'"
done
value=$($TMUX list-sessions -F "$flag") ||
fail "list-sessions $flag failed"
[ "$value" = "$expected" ] ||
fail "expected list-sessions $flag to be '$expected' but got '$value'"
}
flags_lack()
{
target=$1
@@ -169,12 +150,10 @@ $TMUX set-hook -g alert-silence \
# alert-bell and sets the bell flag, shown as ! in window_flags.
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
assert_session_flag bell 0 bellw
flags_lack mon:bellw '!'
bell mon:bellw
wait_for @log '|alert-bell:mon:bellw'
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
assert_session_flag bell 1 bellw
flags_have mon:bellw '!'
# Bells are not deduplicated: a second bell fires the hook again even
@@ -185,7 +164,6 @@ wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
# Selecting the window clears the alert flags.
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
assert_session_flag bell 0 bellw
flags_lack mon:bellw '!'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
@@ -195,7 +173,6 @@ $TMUX set -g @log '' || fail "reset @log failed"
bell mon:w0
wait_for @log '|alert-bell:mon:w0'
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
assert_session_flag bell 1 bellw
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
@@ -222,7 +199,6 @@ $TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:actw
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
assert_session_flag activity 0 actw
assert_unchanged @log ''
# With monitor-activity on, output fires alert-activity and sets the
@@ -232,7 +208,6 @@ $TMUX set -wt mon:actw monitor-activity on ||
activity mon:actw
wait_for @log '|alert-activity:mon:actw'
wait_for_fmt mon:actw '#{window_activity_flag}' 1
assert_session_flag activity 1 actw
flags_have mon:actw '#'
# While the flag is set further activity does not fire the hook again.
@@ -259,7 +234,6 @@ exec 3>"$OUT/fifo"
wait_for_fmt mon: '#{session_attached}' 1
$TMUX selectw -t mon:actw || fail "select-window actw failed"
wait_for_fmt mon:actw '#{window_activity_flag}' 0
assert_session_flag activity 0 actw
flags_lack mon:actw '#'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
exec 3>&-
@@ -276,7 +250,6 @@ $TMUX set -wt mon:w0 monitor-activity on ||
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:w0
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
assert_session_flag activity 1 actw
assert_unchanged @log ''
$TMUX set -wut mon:w0 monitor-activity ||
fail "unset monitor-activity w0 failed"
@@ -300,19 +273,16 @@ $TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# running.
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
assert_session_flag silence 0 silw
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
wait_for @log '|alert-silence:mon:silw'
wait_for_fmt mon:silw '#{window_silence_flag}' 1
assert_session_flag silence 1 silw
flags_have mon:silw '~'
assert_unchanged @log '|alert-silence:mon:silw'
$TMUX set -wt mon:silw monitor-silence 0 ||
fail "reset monitor-silence failed"
$TMUX selectw -t mon:silw || fail "select-window silw failed"
wait_for_fmt mon:silw '#{window_silence_flag}' 0
assert_session_flag silence 0 silw
flags_lack mon:silw '~'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
-53
View File
@@ -1,53 +0,0 @@
#!/bin/sh
# Width cache ranges must include their endpoint without overflowing wchar_t.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
SERVER=
WATCHDOG=
cleanup()
{
if [ -n "$WATCHDOG" ]; then
kill "$WATCHDOG" 2>/dev/null
wait "$WATCHDOG" 2>/dev/null
fi
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
}
trap cleanup 0
trap 'exit 1' 1 2 15
$TMUX new-session -d 'exec sleep 60' || exit 1
SERVER=$($TMUX display-message -p '#{pid}') || exit 1
# kill-server cannot stop a server stuck rebuilding the width cache.
(
sleep 5
kill -9 "$SERVER" 2>/dev/null
) &
WATCHDOG=$!
$TMUX set -g codepoint-widths 'U+1F600=2' || exit 1
# Cover both signed and unsigned 32-bit wchar_t. Values above WCHAR_MAX
# are ignored by the parser on platforms where they cannot be represented.
for value in U+7FFFFFFF=1 U+7FFFFFFE-U+7FFFFFFF=2 \
U+FFFFFFFF=1 U+FFFFFFFE-U+FFFFFFFF=2; do
$TMUX set -g codepoint-widths "$value" || exit 1
[ "$($TMUX display-message -p alive)" = alive ] || exit 1
done
# Check that an ordinary range still includes both endpoints and stops there.
$TMUX set -g codepoint-widths 'U+03B1-U+03B3=2' || exit 1
$TMUX set -g @text 'αβγδ' || exit 1
[ "$($TMUX display-message -p '#{w:@text}')" = 7 ] || exit 1
$TMUX set -gu codepoint-widths || exit 1
[ "$($TMUX display-message -p '#{w:@text}')" = 4 ] || exit 1
$TMUX kill-server || exit 1
SERVER=
exit 0
+81
View File
@@ -0,0 +1,81 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B\\007echo hi\\n\\033]133;C\\007hello\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007\"; exec sleep 100'" || exit 1
sleep 1
$TMUX copy-mode -c || exit 1
$TMUX send-keys -X search-backward hello || exit 1
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$hidden" != hello ] || exit 1
$TMUX send-keys -X search-backward separator || exit 1
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$hidden" != separator ] || exit 1
$TMUX send-keys BTab || exit 1
$TMUX send-keys -X search-backward hello || exit 1
shown=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$shown" = hello ] || exit 1
$TMUX send-keys -X select-line || exit 1
$TMUX send-keys -X copy-selection || exit 1
selected=$($TMUX show-buffer)
[ "$selected" = hello ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX kill-server
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"first\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007\"; exec sleep 100'" || exit 1
sleep 1
$TMUX copy-mode -c || exit 1
$TMUX send-keys -X search-backward separator || exit 1
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$hidden" != separator ] || exit 1
$TMUX send-keys BTab || exit 1
$TMUX send-keys -X search-backward separator || exit 1
shown=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$shown" = separator ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX kill-server
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007P> \\033]133;B\\007for i in 1 2 3; do\\n\\033]133;A;k=s\\007> \\033]133;B\\007echo \\$i\\n\\033]133;P;k=s\\007> \\033]133;B\\007done\\033]133;C\\007\\nRESULT\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007P> \\033]133;B\\007\"; exec sleep 100'" || exit 1
sleep 1
$TMUX copy-mode -c || exit 1
$TMUX send-keys -X search-backward RESULT || exit 1
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$hidden" != RESULT ] || exit 1
$TMUX send-keys -X search-backward 'echo $i' || exit 1
shown=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$shown" = 'echo $i' ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode -U || exit 1
$TMUX send-keys -X search-backward RESULT || exit 1
$TMUX send-keys Tab || exit 1
cursor=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$cursor" = done ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX kill-server
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007P0> \\033]133;B\\007\\r\\n\\033]133;D;0\\007empty-separator\\n\\033]133;A\\007P1> \\033]133;B\\007\"; exec sleep 100'" || exit 1
sleep 1
$TMUX copy-mode -c || exit 1
collapsed=$($TMUX capture-pane -p)
case "$collapsed" in
*"+ P0>"*) ;;
*) exit 1 ;;
esac
$TMUX send-keys -X search-backward empty-separator || exit 1
hidden=$($TMUX display-message -p '#{copy_cursor_line}')
[ "$hidden" != empty-separator ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX kill-server 2>/dev/null
exit 0
+1 -1
View File
@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest$$"
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
DIR=$(mktemp -d)
+1 -1
View File
@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest$$"
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
DIR=$(mktemp -d)
+1 -1
View File
@@ -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"
$TMUX kill-server 2>/dev/null
OUT=$(mktemp)
+1 -1
View File
@@ -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"
$TMUX kill-server 2>/dev/null
FIFO=$(mktemp -u)
+102
View File
@@ -0,0 +1,102 @@
#!/bin/sh
# Check OSC 133 exit status indicators in copy mode using a real client.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
fail() {
echo "$*" >&2
exit 1
}
capture() {
$TMUX capture-pane -pS0 -E- >$TMP || exit 1
}
check_grep() {
grep -Fq "$1" $TMP || fail "missing pattern: $1"
}
check_no_grep() {
grep -Fq "$1" $TMP && fail "unexpected pattern: $1"
}
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" 0 1 15
$TMUX2 new-session -d -x80 -y10 \
"printf '\033]133;A\007p\$ \033]133;B\007one\n\033]133;C\007out1\n\033]133;D;0\007\033]133;A\007p\$ \033]133;B\007two\n\033]133;C\007out2\n\033]133;D;123\007\033]133;A\007p\$ \033]133;B\007silent\n\033]133;C\007\033]133;D;7\007\033]133;A\007p\$ \033]133;B\007three\n\033]133;C\007out3\n\033]133;D\007\033]133;A\007p\$ \033]133;B\007'; exec sleep 100" || \
exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g copy-mode-position-format '#[align=left]POS' || exit 1
$TMUX2 list-keys -Tcopy-mode O | grep -Fq 'fold-view-toggle' || exit 1
$TMUX2 list-keys -Tcopy-mode-vi O | grep -Fq 'fold-view-toggle' || exit 1
$TMUX new-session -d -x80 -y10 || exit 1
$TMUX set -g status off || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
$TMUX2 copy-mode -U || exit 1
$TMUX2 send -X history-top || exit 1
sleep 1
capture
check_grep "!- p\$ two"
check_grep "! p\$ silent"
check_no_grep "!+ p\$ silent"
check_grep "POS"
# Rebuilding with collapsed output must not expand copy-mode formats before
# the viewport has been restored for the shorter backing grid.
$TMUX2 send -X collapse-output -a || exit 1
sleep 1
capture
check_grep "!+ p\$ two"
check_grep "! p\$ silent"
check_no_grep "!+ p\$ silent"
$TMUX2 send -X line-numbers-on || exit 1
sleep 1
capture
check_grep " 3 !+ p\$ two"
$TMUX2 send -X fold-view-toggle || exit 1
[ "$($TMUX2 display -p '#{copy_fold_view}')" = 0 ] || exit 1
sleep 1
capture
check_grep "out2"
check_no_grep "!+ p\$ two"
check_grep " 3 p\$ two"
$TMUX2 send -X fold-view-toggle || exit 1
[ "$($TMUX2 display -p '#{copy_fold_view}')" = 1 ] || exit 1
sleep 1
capture
check_grep " 3 !- p\$ two"
# A format wider than the standard three-column gutter is not truncated.
$TMUX2 send -X cancel || exit 1
$TMUX2 set -g copy-mode-exit-status-format \
'#[align=right]#{?exit_status,!#{exit_status}, }' || exit 1
$TMUX2 copy-mode -c || exit 1
$TMUX2 send -X history-top || exit 1
sleep 1
capture
check_grep "!123+ p\$ two"
$TMUX2 send -X select-line || exit 1
$TMUX2 send -X copy-selection || exit 1
selection=$($TMUX2 show-buffer)
case $selection in
*RC=*) fail "exit status was copied" ;;
esac
exit 0
+3 -5
View File
@@ -153,13 +153,11 @@ wait_format "$p0" '#{pane_in_mode}' '0'
wait_option @picked "$p1"
# Commands after display-panes run immediately while the mode remains.
# Record the mode in the same command queue so client startup cannot race
# the 500ms timeout.
$TMUX set -g @after none || fail "set @after failed"
$TMUX display-panes -Nd 500 -t "$p0" \; \
set -gF -t "$p0" @after '#{pane_mode}' ||
$TMUX display-panes -Nd 500 -t "$p0" \; set -g @after fast ||
fail "display-panes immediate command failed"
wait_option @after 'panes-mode'
wait_option @after fast
wait_format "$p0" '#{pane_mode}' 'panes-mode'
wait_format "$p0" '#{pane_in_mode}' '0'
# Existing zoom is restored on exit.
-158
View File
@@ -1,158 +0,0 @@
#!/bin/sh
# Moving, resizing and reordering an over-zoom float must leave the tiled
# pane zoomed, and the changes must survive an explicit unzoom.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
trap '$TMUX kill-server 2>/dev/null' 0
trap 'exit 1' 1 2 15
fail()
{
echo "$*" >&2
exit 1
}
run()
{
$TMUX "$@" || fail "failed: $*"
}
check()
{
got=$(run display-message -p -t "$1" "$2") || exit 1
[ "$got" = "$3" ] || fail "$1 $2: got '$got', expected '$3'"
}
check_zoom()
{
check "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
check "$float" '#{pane_floating_flag}:#{pane_active}' "1:$active"
check "$base" '#{@unzoomed}' ''
}
run new-session -d -x 80 -y 24
base=$(run display-message -p '#{pane_id}') || exit 1
other=$(run split-window -dPF '#{pane_id}') || exit 1
layout=$(run display-message -p '#{window_layout}') || exit 1
run set-hook -g window-unzoomed 'set -g @unzoomed 1'
# Cover creation before and after zoom, with both the float and the tiled
# pane active. Modal panes use the same over-zoom flag.
for mode in before after detached modal; do
if [ "$mode" != before ]; then
run resize-pane -Z -t "$base"
fi
case "$mode" in
detached) flags=-Ad; active=0 ;;
modal) flags=-O; active=1 ;;
*) flags=-A; active=1 ;;
esac
float=$(run new-pane "$flags" -PF '#{pane_id}' -t "$base" \
-x 20 -y 8 -X 8 -Y 3 '') || exit 1
if [ "$mode" = before ]; then
run resize-pane -Z -t "$base"
fi
run set -gu @unzoomed
check_zoom
run move-pane -t "$float" -D
check "$float" '#{pane_top}' 5
check_zoom
run move-pane -t "$float" -U 2 -R 3 -L 1
check "$float" '#{pane_left}:#{pane_top}' '11:3'
check_zoom
run move-pane -t "$float" -X 25% -Y 25%
check "$float" '#{pane_left}:#{pane_top}' '21:7'
check_zoom
run move-pane -t "$float" -P bottom-right
check "$float" '#{pane_left}:#{pane_top}' '61:17'
check_zoom
run resize-pane -t "$float" -x 30 -y 10
check "$float" '#{pane_width}:#{pane_height}' '28:8'
check_zoom
run resize-pane -t "$float" -U 1 -L 2
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
'59:16:30:9'
check_zoom
run move-pane -t "$float" -P centre
check "$float" '#{pane_left}:#{pane_top}' '25:7'
check_zoom
# Failed commands must not change zoom either.
for command in 'move-pane -D invalid' 'move-pane -P invalid' \
'move-pane -z invalid' 'resize-pane -x invalid' \
'resize-pane -D invalid'; do
$TMUX $command -t "$float" 2>/dev/null &&
fail "unexpected success: $command"
check_zoom
done
run resize-pane -Z -t "$base"
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
'25:7:30:9'
run kill-pane -t "$float"
check "$base" '#{window_layout}' "$layout"
done
# Put an ordinary (hidden while zoomed) float between two over-zoom floats
# in the stacking order. Reordering must skip it when counting visible
# positions, but keep all floats ahead of the tiled panes after unzoom.
back=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
float=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
active=0
run resize-pane -Z -t "$base"
run set -gu @unzoomed
for position in backward back forward-loop; do
run move-pane -t "$float" -P front
run move-pane -t "$float" -P "$position"
check "$float" '#{pane_z}' 1
check "$back" '#{pane_z}' 0
check_zoom
done
for position in forward front backward-loop; do
run move-pane -t "$float" -P back
run move-pane -t "$float" -P "$position"
check "$float" '#{pane_z}' 0
check "$back" '#{pane_z}' 1
check_zoom
done
for z in 1 0 99; do
run move-pane -t "$float" -z "$z"
want=$z
[ "$z" = 99 ] && want=1
check "$float" '#{pane_z}' "$want"
check_zoom
done
run resize-pane -Z -t "$base"
check "$float" '#{pane_z}' 2
check "$base" '#{pane_z}' 4
run kill-pane -t "$hidden"
run kill-pane -t "$back"
# Explicit zoom toggling and operations on tiled panes retain their
# existing behaviour. The over-zoom flag alone does not make a pane a float:
# zooming the float itself gives it a tiled cell until unzoom.
run resize-pane -Z -t "$float"
check "$float" '#{pane_zoomed_flag}:#{pane_floating_flag}' '1:0'
run resize-pane -t "$float" -x 25
check "$float" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
'0:1:23'
run resize-pane -Z -t "$base"
run resize-pane -t "$other" -D 1
check "$base" '#{window_zoomed_flag}' 0
# A hidden ordinary float must still be unzoomed before resizing it.
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
run resize-pane -Z -t "$base"
run resize-pane -t "$hidden" -x 25
check "$hidden" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
'0:1:23'
exit 0
-2
View File
@@ -7,8 +7,6 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
trap '$TMUX kill-server 2>/dev/null' 0
trap 'exit 1' 1 2 15
# test_format $format $expected_result
test_format()
+4 -20
View File
@@ -18,24 +18,6 @@ export TZ LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
HOLD1=
HOLD2=
CC1=
CC2=
cleanup()
{
for pid in $HOLD1 $HOLD2 $CC1 $CC2; do
kill "$pid" 2>/dev/null
wait "$pid" 2>/dev/null
done
$TMUX kill-server 2>/dev/null
rm -f "$FIFO1" "$FIFO2"
}
trap cleanup 0
trap 'exit 1' 1 2 15
ESC=$(printf '\033')
@@ -597,6 +579,8 @@ assert_alive "verbose loop expansion"
# L loops over attached clients. Attach two control-mode clients, each held
# open by a background process keeping a FIFO's write end open.
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
rm -f "$FIFO1" "$FIFO2"
mkfifo "$FIFO1" "$FIFO2" || exit 1
# Hold the write ends open so the control clients stay attached.
@@ -621,9 +605,9 @@ test_format "#{L/nr:x}" "xx"
test_format "#{L/r:x}" "xx"
# Now detach one and confirm the count drops to one.
kill $HOLD2 2>/dev/null
wait $HOLD2 2>/dev/null
HOLD2=
sleep 1
test_format "#{L:x}" "x"
kill $HOLD1 $CC1 $CC2 2>/dev/null
rm -f "$FIFO1" "$FIFO2"
exit 0
-2
View File
@@ -7,8 +7,6 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
trap '$TMUX kill-server 2>/dev/null' 0
trap 'exit 1' 1 2 15
# test_format $format $expected_result
test_format()
+6 -6
View File
@@ -76,14 +76,14 @@ $TMUX set-hook -g session-closed \
fail "set-hook session-closed failed"
# The only pane of the only window of a session exits: pane-exited, then
# session-closed, then window-unlinked, each seeing the dead object in the
# window-unlinked, then session-closed, each seeing the dead object in the
# hook formats.
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
fail "new-session doomed failed"
wait_for @log \
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
assert_unchanged @log \
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
# The dead pane, window and session cannot be used as targets but the
# server survives.
@@ -110,14 +110,14 @@ $TMUX set-hook -g pane-exited \
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
fail "restore pane-exited hook failed"
# kill-window on the last window: session-closed then window-unlinked but
# kill-window on the last window: window-unlinked then session-closed but
# no pane-exited for the panes in the killed window.
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
wait_for @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
assert_unchanged @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
wait_for @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
assert_unchanged @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
$TMUX has -t main || fail "server died after kill-window chain"
# kill-session: session-closed fires first, then window-unlinked for its
+1 -2
View File
@@ -12,8 +12,7 @@ $TMUX kill-server 2>/dev/null
TMP=$(mktemp)
OUT=$(mktemp)
trap 'rm -f "$TMP" "$OUT"; $TMUX kill-server 2>/dev/null' 0
trap 'exit 1' 1 2 15
trap "rm -f $TMP $OUT" 0 1 15
cat <<EOF >$TMP
if 'true' 'wibble wobble'
+2 -5
View File
@@ -4,9 +4,6 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
# Keep panes alive until the next case or cleanup, without reading replies.
INPUT_HOLD='while :; do sleep 3600; done'
TMP=$(mktemp)
EXP=$(mktemp)
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
@@ -40,10 +37,10 @@ start_pane_hlimit()
$TMUX kill-server 2>/dev/null
sleep 0.1
$TMUX new-session -d -x 1 -y 1 -s test-setup "$INPUT_HOLD" || exit 1
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
$TMUX set-option -g history-limit "$hlimit" || exit 1
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
"printf '$seq'; $INPUT_HOLD" || exit 1
"printf '$seq'; sleep 2" || exit 1
$TMUX kill-session -t test-setup
sleep 0.3
}
+1 -13
View File
@@ -22,19 +22,7 @@ assert_key () {
clear-history -t$W \; \
send-keys -t$W "$key" 'EOL' || exit 1
# cat echoes the keys back asynchronously, so wait for the EOL marker
# to reach the pane instead of capturing straight away.
i=0
while [ $i -lt 50 ]; do
screen=$($TMUX capturep -pt$W)
case "$screen" in
*EOL*) break ;;
esac
i=$((i + 1))
sleep 0.1
done
actual_code=$(printf '%s\n' "$screen" | \
actual_code=$($TMUX capturep -pt$W | \
head -1 | \
sed -e 's/EOL.*$//')
+7 -34
View File
@@ -2,38 +2,6 @@
. ./input-common.inc
# Wait for the terminator and following text to reach the parser.
check_discard()
{
name=$1
printf 'OK\n' >"$EXP"
i=0
while [ "$i" -lt 300 ]; do
capture_grid "$name" >"$TMP"
cmp -s "$TMP" "$EXP" && return 0
sleep 0.05
i=$((i + 1))
done
fail "$name (timed out waiting for discard)"
}
# The missing-terminator timer may expire before the string limit is reached
# on a slow build. Clear any payload printed after the timeout, then check
# that the string did not change the title and normal output has resumed.
test_discard()
{
name=$1
prefix=$2
start_cmd "$name" 8 3 "$INPUT_HOLD"
$TMUX select-pane -T discard-test || exit 1
$TMUX respawn-pane -k \
"perl -e 'print qq{$prefix}, q{x} x 1100000, qq{\e\\\\\e[H\e[2JOK}'; $INPUT_HOLD" || exit 1
check_discard "$name"
$TMUX display-message -p -t "$name:" '#{pane_title}' >"$TMP" || exit 1
printf 'discard-test\n' >"$EXP"
cmp "$TMP" "$EXP" || fail "$name title"
}
start_cmd csi-param-discard 8 3 \
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
check_capture csi-param-discard 'OK'
@@ -42,8 +10,13 @@ start_cmd csi-interm-discard 8 3 \
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
check_capture csi-interm-discard 'OK'
test_discard osc-discard '\e]2;'
test_discard apc-discard '\e_'
start_cmd osc-discard 8 3 \
"perl -e 'print qq{\e]2;}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
check_capture osc-discard 'OK'
start_cmd apc-discard 8 3 \
"perl -e 'print qq{\e_}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
check_capture apc-discard 'OK'
start_pane unknown-csi 8 3 '\033[?9999zOK'
check_capture unknown-csi 'OK'
+11 -5
View File
@@ -5,9 +5,12 @@
start_pane alternate 10 3 'MAIN\033[?1049hALT\033[?1049lZ\n'
check_capture alternate 'MAINZ'
start_pane osc133 20 8 'xx\033]133;A\007p>\033]133;B\007cmd\nxy\033]133;P;k=s\007more\nzz\033]133;C\007out\033]133;D;7\007\nq\033]133;C\007bad\033]133;D;-1\007\nqq\033]133;C\007big\033]133;D;300\007\nzzz\033]133;C\007ok\033]133;D\007\n'
start_pane osc133 20 12 'xx\033]133;A\007p>\033]133;B\007cmd\nxy\033]133;P;k=s\007more\nxz\033]133;A;k=s\007more\nxw\033]133;P;k=c\007more\nxv\033]133;A;k=c\007more\nzz\033]133;C\007out\033]133;D;7\007\nq\033]133;C\007bad\033]133;D;-1\007\nqq\033]133;C\007big\033]133;D;300\007\nzzz\033]133;C\007ok\033]133;D\007\n'
check_capture osc133 'xxp>cmd
xymore
xzmore
xwmore
xvmore
zzout
qbad
qqbig
@@ -15,10 +18,13 @@ zzzok'
check_raw_matches osc133 \
'L 0 \(0\) flags=START_PROMPT,START_COMMAND\[[0-9a-f]+\].* osc133=2,4,0,0,0' \
'L 1 \(1\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
'L 2 \(2\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,7' \
'L 3 \(3\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,1,4,255' \
'L 4 \(4\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
'L 2 \(2\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
'L 3 \(3\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
'L 4 \(4\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,2,5,7' \
'L 6 \(6\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,1,4,255' \
'L 7 \(7\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
'L 8 \(8\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
$TMUX kill-server 2>/dev/null
exit $exit_status
+1 -1
View File
@@ -51,7 +51,7 @@ $TMUX kill-server 2>/dev/null
sleep 0.1
$TMUX new-session -d -x 5 -y 3 -s history \; \
set-option -g history-limit 3 \; \
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; $INPUT_HOLD" || exit 1
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; sleep 2" || exit 1
sleep 0.3
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
printf "%s\n" '01
-28
View File
@@ -1,28 +0,0 @@
#!/bin/sh
# Killing a session whose window is zoomed must not crash the server:
# window_destroy used to unzoom the window, which resized the panes and
# fired pane-resized with the window in the payload, and releasing that
# reference destroyed the window a second time.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d -sfoo || exit 1
$TMUX split-window -d -h -tfoo:0 || exit 1
$TMUX resize-pane -Z -tfoo:0 || exit 1
$TMUX new -d -sbar || exit 1
$TMUX kill-session -tfoo || exit 1
$TMUX has-session -tbar || exit 1
# The same with the zoomed window in a session killed by kill-server.
$TMUX new -d -sbaz || exit 1
$TMUX split-window -d -v -tbaz:0 || exit 1
$TMUX resize-pane -Z -tbaz:0 || exit 1
$TMUX kill-server || exit 1
exit 0
+9 -43
View File
@@ -50,8 +50,7 @@
# layout it names is a single cell or a split;
# - a window whose only tiled pane has been killed, which leaves it with a
# floating cell as its layout root or with a root node holding nothing but
# floating cells, producing an empty v1 body with a checksum, and being
# parsed as v1;
# floating cells, producing no v1 dump at all, and being parsed as v1;
# - the %layout-change notification, in both formats at once: two control
# clients watching one layout change, only one of which has asked for new
# layouts, and the number of notifications a change produces in each format;
@@ -377,7 +376,10 @@ must_equal 'Layout after select-pane back' "$(layout L:two)" "$SPLIT"
# With nothing zoomed the two layout formats agree.
#
# Zoomed layouts are checked below with floating panes.
# The zoomed case is deliberately not covered here. While a pane is zoomed
# #{window_layout} dumps the saved (unzoomed) layout and
# #{window_visible_layout} the zoomed one, but that depends on how zooming
# stashes the layout root rather than on anything in layout-custom.c.
must_equal 'Visible layout' "$(visible_layout L:two)" "$SPLIT"
# ---------------------------------------------------------------------------
@@ -701,42 +703,6 @@ must_contain 'Floating layout back z-index' "$floating" '"z":1'
check_ok select-layout -t L:float "$floating"
must_equal 'Floating layout after round trip' "$(raw_layout L:float)" "$floating"
# A dump taken while zoomed must retain the unzoomed floating z-indexes.
# Check hidden floats, floats above zoom, and a mixture of the two.
for flags in '' A mixed; do
check_ok new-window -d -t L: -n zoom
zt=$($TMUX display-message -p -t L:zoom '#{pane_id}')
check_ok split-window -d -h -t "$zt"
case "$flags" in
A|mixed) first=-Ad ;;
*) first=-d ;;
esac
case "$flags" in
A) second=-Ad ;;
*) second=-d ;;
esac
check_ok new-pane "$first" -t "$zt" -x 20 -y 6 ''
check_ok new-pane "$second" -t "$zt" -x 30 -y 8 ''
for pane in 0 2 3; do
check_ok select-pane -t "L:zoom.$pane"
before=$(raw_layout L:zoom)
legacy=$(v1_layout L:zoom)
check_ok resize-pane -Z -t "L:zoom.$pane"
during=$(raw_layout L:zoom)
must_equal 'Layout while zoomed' "$during" "$before"
must_equal 'Legacy layout while zoomed' \
"$(v1_layout L:zoom)" "$legacy"
must_equal 'Zoom after dumping layout' \
"$($TMUX display-message -p -t L:zoom '#{window_zoomed_flag}')" 1
must_differ 'Visible layout while zoomed' \
"$(visible_layout L:zoom)" "$(layout L:zoom)"
check_ok select-layout -t L:zoom "$during"
must_equal 'Round trip from zoomed layout' \
"$(raw_layout L:zoom)" "$before"
done
check_ok kill-window -t L:zoom
done
# ---------------------------------------------------------------------------
# Floating panes and the legacy (v1) format.
#
@@ -872,12 +838,12 @@ must_equal 'Panes left with one floating pane' \
"$($TMUX display-message -p -t L:gone1 '#{window_panes}')" '1'
# The floating cell is the root and there is nothing tiled under it, so there is
# only an empty v1 body to dump. The floating cell must not be written out on
# no v1 dump to make. In particular the floating cell must not be written out on
# its own, which would be a layout claiming the window is the size and position
# of the floating pane with no pane in it at all.
got=$(v1_layout L:gone1)
check_ok display-message -p alive
must_equal 'v1 dump with one floating pane and no tiled panes' "$got" '0000,'
must_equal 'v1 dump with one floating pane and no tiled panes' "$got" ''
# Two floating panes left, so the node keeps two children, does not collapse,
# and stays the root with nothing but floating cells in it.
@@ -890,11 +856,11 @@ must_equal 'Panes left with two floating panes' \
"$($TMUX display-message -p -t L:gone2 '#{window_panes}')" '2'
# The node is the root this time rather than the floating cell, but it has no
# tiled cell anywhere under it either, so the v1 body is still empty -
# tiled cell anywhere under it either, so there is still no v1 dump to make -
# and making one must not take the server with it.
got=$(v1_layout L:gone2)
check_ok display-message -p alive
must_equal 'v1 dump with two floating panes and no tiled panes' "$got" '0000,'
must_equal 'v1 dump with two floating panes and no tiled panes' "$got" ''
# Nor must parsing a v1 layout against it. There is no tiled pane for the
# layout to name, so whether it is applied or rejected is the format's business;
+5
View File
@@ -86,6 +86,11 @@ check_value "-gv status-keys" "vi"
check_fail "unknown value: bogus" set -g status-keys bogus
check_value "-gv status-keys" "vi"
# pane-border-lines accepts rounded as a pane border style.
check_ok set -gw pane-border-lines rounded
check_value "-gwv pane-border-lines" "rounded"
check_ok set -gw pane-border-lines single
# --- flag options ---------------------------------------------------------
#
# focus-events is an on/off flag. Setting with no value toggles it; explicit
+132
View File
@@ -0,0 +1,132 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -L${TEST_SOCKET:-testO$$} -f/dev/null"
OUT=/tmp/tmux-output-commands-$$
EXPECTED=$(printf 'one\ntwo')
cleanup()
{
$TMUX kill-server 2>/dev/null
rm -f "$OUT"
}
trap cleanup EXIT HUP INT TERM
$TMUX kill-server 2>/dev/null
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\ntwo\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007broken\\n\\033]133;C\\007unfinished\"; exec sleep 100'" || exit 1
sleep 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
$TMUX send-keys -X pipe-output "wc -c >$OUT" || exit 1
sleep 1
[ "$(cat "$OUT")" = 10 ] || exit 1
$TMUX send-keys -X copy-pipe-output "wc -c >$OUT" output || exit 1
sleep 1
[ "$(cat "$OUT")" = 10 ] || exit 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
$TMUX send-keys -X select-output || exit 1
$TMUX send-keys -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X select-output || exit 1
$TMUX send-keys -X pipe-selection "wc -c >$OUT" || exit 1
sleep 1
[ "$(cat "$OUT")" = 10 ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X search-backward separator || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode -c || exit 1
$TMUX send-keys -X search-backward echo || exit 1
$TMUX send-keys -X select-output || exit 1
$TMUX send-keys -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X search-backward one || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX new-window -d -n plain "printf 'alpha\\nbeta\\n'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :plain || exit 1
$TMUX send-keys -t :plain.0 -X copy-output -a || exit 1
all=$($TMUX show-buffer)
case "$all" in
*alpha*beta*) ;;
*) exit 1 ;;
esac
$TMUX send-keys -t :plain.0 -X cancel || exit 1
$TMUX set-buffer -b keep unchanged || exit 1
$TMUX copy-mode -t :plain || exit 1
$TMUX send-keys -t :plain.0 -X copy-output || exit 1
[ "$($TMUX show-buffer -b keep)" = unchanged ] || exit 1
$TMUX send-keys -t :plain.0 -X cancel || exit 1
$TMUX new-window -d -n empty "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007true\\n\\033]133;C\\033]133;D;0\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :empty || exit 1
$TMUX send-keys -t :empty.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :empty.0 -X cancel || exit 1
$TMUX new-window -d -n prompt "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :prompt || exit 1
$TMUX send-keys -t :prompt.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
$TMUX copy-mode -c -t :prompt || exit 1
$TMUX send-keys -t :prompt.0 -X expand-output || exit 1
$TMUX send-keys -t :prompt.0 -X -N 100 cursor-down || exit 1
$TMUX send-keys -t :prompt.0 -X select-output || exit 1
[ "$($TMUX display-message -p -t :prompt.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX send-keys -t :prompt.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
$TMUX copy-mode -t :prompt || exit 1
$TMUX send-keys -t :prompt.0 -X -N 100 cursor-down || exit 1
$TMUX send-keys -t :prompt.0 C-o || exit 1
[ "$($TMUX display-message -p -t :prompt.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX send-keys -t :prompt.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
$TMUX new-window -d -n same "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :same || exit 1
$TMUX send-keys -t :same.0 -X search-backward one || exit 1
$TMUX send-keys -t :same.0 -X select-output || exit 1
$TMUX send-keys -t :same.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :same.0 -X cancel || exit 1
$TMUX set-option -g scroll-on-clear off || exit 1
$TMUX new-window -d -n clear "printf 'old1\\nold2\\nold3\\nold4\\nold5\\nold6\\n\\033]133;A\\007p\\$ \\033]133;B\\007echo 1; clear; ps\\n\\033]133;C\\0071\\n\\033[H\\033[2JPID TTY\\n1 pts/0\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :clear || exit 1
$TMUX send-keys -t :clear.0 C-o || exit 1
$TMUX send-keys -t :clear.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'PID TTY\n1 pts/0')" ] || exit 1
$TMUX send-keys -t :clear.0 -X cancel || exit 1
exit 0
+1 -1
View File
@@ -15,7 +15,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest$$"
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
DIR=$(mktemp -d)
-55
View File
@@ -1,55 +0,0 @@
#!/bin/sh
# run-shell -c should expand formats using the target pane.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMP=$(mktemp -d)
trap '$TMUX kill-server 2>/dev/null; rm -rf "$TMP"' 0 1 15
TMP=$(cd "$TMP" && pwd -P)
mkdir "$TMP/first" "$TMP/second dir" || exit 1
check_directory()
{
actual=$(cat "$TMP/out")
if [ "$actual" != "$1" ]; then
echo "Expected directory '$1', got '$actual'"
exit 1
fi
}
# The start path is available immediately, before the child is scheduled.
# Use it to test the format context in the same command queue as creation.
$TMUX new-session -d -s test -c "$TMP/first" 'sleep 60' \; \
run-shell -c '#{pane_start_path}' "pwd >'$TMP/out'" || exit 1
check_directory "$TMP/first"
# An explicit target must supply the format context, including with a delay.
pane=$($TMUX new-window -d -P -F '#{pane_id}' -t test \
-c "$TMP/second dir" 'sleep 60') || exit 1
# The current path depends on the operating system finding the child process.
i=0
while [ "$($TMUX display-message -p -t "$pane" '#{pane_current_path}')" != \
"$TMP/second dir" ]; do
if [ "$i" -ge 100 ]; then
echo "Timed out waiting for pane current directory"
exit 1
fi
sleep 0.05
i=$((i + 1))
done
$TMUX run-shell -t "$pane" -d 0.1 -c '#{pane_current_path}' \
"pwd >'$TMP/out'" || exit 1
check_directory "$TMP/second dir"
# Literal directories and the default client directory still work.
$TMUX run-shell -c "$TMP/second dir" "pwd >'$TMP/out'" || exit 1
check_directory "$TMP/second dir"
(cd "$TMP/first" && $TMUX run-shell "pwd >'$TMP/out'") || exit 1
check_directory "$TMP/first"
exit 0
+6
View File
@@ -107,6 +107,12 @@ $TMUX2 new-pane -x28 -y8 -X4 -Y1 -B double \
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
compare floating-border-double
# Larger floating pane with rounded border lines.
new_scene 40 12
$TMUX2 new-pane -x28 -y8 -X4 -Y1 -B rounded \
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
compare floating-border-rounded
# Floating pane with no border lines: redraw_mark_pane_borders returns early so
# the float has no border at all, only its (clipped) content over the base pane.
new_scene 40 12
@@ -0,0 +1,12 @@
base
╭──────────────────────────╮
│FLOAT │
│ │
│ │
│ │
│ │
│ │
╰──────────────────────────╯
+1 -2
View File
@@ -95,8 +95,7 @@ layout_tee_down() { # top/bottom, bottom split: a top tee
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
# Keep the anchor window alive until cleanup, even on slow test runs.
$TMUX2 new -d -x40 -y14 'exec cat' || exit 1
$TMUX2 new -d -x40 -y14 "sh -c 'exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX2 set -g pane-border-format " #{pane_index} " || exit 1
-116
View File
@@ -1,116 +0,0 @@
#!/bin/sh
# When only some lines are redrawn after a synchronized update, a line
# following a wrapped line must be drawn at its own position, not where the
# cursor happens to be.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export PATH TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
DIR=$(mktemp -d) || exit 1
TMUX_TMPDIR=$DIR
export TMUX_TMPDIR
INNER="$TEST_TMUX -Li$$ -f/dev/null"
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
CONTROL=$DIR/control
EMITTER=$DIR/emitter.pl
fail()
{
echo "$*" >&2
exit 1
}
cleanup()
{
$OUTER kill-server 2>/dev/null
$INNER kill-server 2>/dev/null
rm -rf "$DIR"
}
trap cleanup 0 1 15
wait_for_file()
{
i=0
while [ "$i" -lt 50 ] && [ ! -e "$1" ]; do
sleep 0.1
i=$((i + 1))
done
[ -e "$1" ] || fail "$2"
}
wait_for_client()
{
i=0
while [ "$i" -lt 50 ]; do
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
grep -q 'sync' && return 0
sleep 0.1
i=$((i + 1))
done
fail "sync-capable client did not attach"
}
cat >"$EMITTER" <<'PERL'
use strict;
use warnings;
my $control = $ENV{CONTROL};
(my $dir = $control) =~ s{/[^/]+$}{};
# Fill the screen with 80 column lines and no newlines so every line wraps.
my $out = "\e[H\e[2J";
for my $row (1 .. 24) {
$out .= substr(sprintf("ROW%02d_", $row) . ('x' x 80), 0, 80);
}
syswrite STDOUT, $out . "\e[1;1H";
open my $painted, '>', "$dir/painted" or die "$dir/painted: $!\n";
close $painted;
while (!-e $control) {
select undef, undef, undef, 0.01;
}
# Change rows 2 and 6 in one synchronized update.
my $frame = "\e[?2026h" .
"\e[2;1H" . substr("NEW02_" . ('#' x 80), 0, 80) .
"\e[6;1H" . substr("NEW06_" . ('%' x 80), 0, 80) .
"\e[24;1H" .
"\e[?2026l";
my $written = syswrite STDOUT, $frame;
die "short synchronized frame write\n"
unless defined $written && $written == length $frame;
open my $done, '>', "$dir/done" or die "$dir/done: $!\n";
close $done;
select undef, undef, undef, 3;
PERL
$INNER new-session -d -s inner -x 80 -y 24 \
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
$INNER set-option -g status off || exit 1
$INNER set-option -g window-size manual || exit 1
$INNER set-option -as terminal-features '*:sync' || exit 1
$OUTER new-session -d -s outer -x 80 -y 24 \
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" ||
exit 1
$OUTER set-option -g status off || exit 1
$OUTER set-option -g window-size manual || exit 1
wait_for_client
wait_for_file "$DIR/painted" "application did not paint"
sleep 1
: >"$CONTROL"
wait_for_file "$DIR/done" "application did not finish"
sleep 1
$OUTER capture-pane -p -t outer:0.0 >"$DIR/screen" || exit 1
sed -n 2p "$DIR/screen" | grep -q '^NEW02_#' || fail "row 2 not redrawn"
sed -n 3p "$DIR/screen" | grep -q '^ROW03_x' || fail "row 3 overwritten"
sed -n 6p "$DIR/screen" | grep -q '^NEW06_%' || fail "row 6 not redrawn"
exit 0
+10 -61
View File
@@ -9,37 +9,14 @@ $TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp) || exit 1
prompt_pid=
cleanup () {
if [ -n "$prompt_pid" ]; then
kill "$prompt_pid" 2>/dev/null
wait "$prompt_pid" 2>/dev/null
fi
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
rm -f "$TMP"
}
trap cleanup 0
trap 'exit 1' 1 2 15
fail () {
echo "[FAIL] $*" >&2
exit 1
}
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
i=0
while [ -z "$($TMUX2 list-clients -F '#{client_name}')" ]; do
[ "$i" -lt 100 ] || fail "inner client did not attach"
sleep 0.05
i=$((i + 1))
done
sleep 1
exit_status=0
assertion=0
format_string () {
case $1 in
@@ -57,33 +34,12 @@ assert_key () {
expected_name=$2
format_string=$(format_string "$expected_name")
# Use a different prompt each time so a stale redraw cannot look ready.
assertion=$((assertion + 1))
prompt="tty-keys-$assertion:"
$TMUX2 command-prompt -k -p "$prompt" \
'display-message -pl '"$format_string" > "$TMP" &
prompt_pid=$!
i=0
while ! $TMUX capture-pane -p | grep -Fq "$prompt"; do
kill -0 "$prompt_pid" 2>/dev/null || \
fail "$keys -> $expected_name: prompt exited before becoming ready"
[ "$i" -lt 100 ] || \
fail "$keys -> $expected_name: timed out waiting for prompt"
sleep 0.05
i=$((i + 1))
done
$TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
sleep 0.05
$TMUX send-keys $keys || fail "could not send $keys"
$TMUX send-keys $keys
i=0
while kill -0 "$prompt_pid" 2>/dev/null; do
[ "$i" -lt 100 ] || \
fail "$keys -> $expected_name: timed out waiting for key"
sleep 0.05
i=$((i + 1))
done
wait "$prompt_pid" || fail "$keys -> $expected_name: prompt failed"
prompt_pid=
wait
keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
actual_name=$(tr -d '[:space:]' < "$TMP")
@@ -337,16 +293,6 @@ assert_key 'Escape [24@' 'C-S-F12'
assert_key 'Escape [I' 'FocusIn'
assert_key 'Escape [O' 'FocusOut'
# Focus events do not leave the prefix table.
$TMUX2 bind C-c set -g @focus-prefix yes
$TMUX2 set -g focus-events on
$TMUX send-keys C-b Escape '[O' Escape '[I' C-c
sleep 0.05
if [ "$($TMUX2 show -gv @focus-prefix)" != yes ]; then
echo "[FAIL] FocusIn and FocusOut left the prefix table"
exit_status=1
fi
# Paste keys
assert_key 'Escape [200~' 'PasteStart'
assert_key 'Escape [201~' 'PasteEnd'
@@ -409,4 +355,7 @@ assert_key 'Escape [32;2u' 'S-Space'
assert_key 'Escape [9;5u' 'C-Tab'
assert_key 'Escape [1;5Z' 'C-S-Tab'
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
exit $exit_status
-164
View File
@@ -1,164 +0,0 @@
#!/bin/sh
# A disconnected waiter must release both its wait and its command queue.
PATH=/bin:/usr/bin
TERM=screen
export TERM
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
OUT=$(mktemp -d)
TMUX="$TEST_TMUX -S$OUT/socket -f/dev/null"
pids=
cleanup()
{
for pid in $pids; do
kill "$pid" 2>/dev/null || true
done
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
fail()
{
echo "$*" >&2
exit 1
}
wait_list()
{
expected=$1
shift
i=0
while [ $i -lt 100 ]; do
value=$($TMUX wait-for -l "$@") || fail "list waiters failed"
[ "$value" = "$expected" ] && return
i=$((i + 1))
sleep 0.05
done
fail "expected waiters '$expected', got '$value'"
}
wait_free()
{
client_pid=$1
i=0
while [ $i -lt 100 ]; do
awk -v pid="$client_pid" '
$4 == "IDENTIFY_CLIENTPID" && $5 == pid { address = $3 }
address != "" && $2 == "free" && $3 == "client" &&
$4 == address && $5 == "(0" { freed = 1 }
END { exit !freed }
' "$OUT"/tmux-server-*.log && return
i=$((i + 1))
sleep 0.05
done
fail "client $client_pid was not freed"
}
cd "$OUT" || exit 1
$TMUX -vv new-session -d -s test || fail "new-session failed"
$TMUX set -g @continued 0 || fail "set option failed"
for signal in TERM KILL; do
for kind in channel lock event; do
name="$kind-$signal"
case $kind in
channel) args="$name"; list_args="$name" ;;
lock)
$TMUX wait-for -L "$name" || fail "lock failed"
args="-L $name"; list_args="$name"
;;
event) args="-E @$name"; list_args="-E @$name" ;;
esac
$TMUX wait-for $args \; set -g @continued 1 &
dead=$!
pids="$dead"
wait_list "client-$dead" $list_args
$TMUX wait-for $args &
live=$!
pids="$dead $live"
wait_list "$(printf 'client-%s\nclient-%s' "$dead" "$live")" \
$list_args
kill -"$signal" "$dead" || fail "kill failed"
wait "$dead" 2>/dev/null || true
pids="$live"
wait_list "client-$live" $list_args
wait_free "$dead"
[ "$($TMUX show -gqv @continued)" = 0 ] ||
fail "disconnected client's next command ran"
case $kind in
channel) $TMUX wait-for -S "$name" || fail "signal failed" ;;
lock) $TMUX wait-for -U "$name" || fail "unlock failed" ;;
event)
$TMUX wait-for -E -w "client-$live" "@$name" ||
fail "wake event waiter failed"
;;
esac
wait_list "" $list_args
wait_free "$live"
wait "$live" || fail "surviving waiter failed"
pids=
if [ "$kind" = lock ]; then
$TMUX wait-for -U "$name" || fail "final unlock failed"
fi
done
done
# Removing the last waiter must remove its otherwise unused channel, and must
# not create a pending signal. Unrelated pending signals must be preserved.
$TMUX wait-for -S saved || fail "save signal failed"
$TMUX wait-for last &
dead=$!
pids="$dead"
wait_list "client-$dead" last
kill -KILL "$dead" || fail "kill last waiter failed"
wait "$dead" 2>/dev/null || true
pids=
wait_list "" last
wait_free "$dead"
grep -F 'remove empty wait channel last' "$OUT"/tmux-server-*.log \
>/dev/null || fail "empty channel was not removed"
$TMUX wait-for saved \; wait-for last &
live=$!
pids="$live"
wait_list "client-$live" last
$TMUX wait-for -S last || fail "signal last failed"
wait_free "$live"
wait "$live" || fail "pending signal was not preserved"
pids=
# Source-file leaves a cleanup callback behind the blocked command.
printf 'wait-for sourced\nset -g @continued 1\n' >"$OUT/source.conf"
$TMUX source-file "$OUT/source.conf" &
dead=$!
pids="$dead"
wait_list "client-$dead" sourced
kill -KILL "$dead" || fail "kill source-file waiter failed"
wait "$dead" 2>/dev/null || true
pids=
wait_list "" sourced
wait_free "$dead"
[ "$($TMUX show -gqv @continued)" = 0 ] ||
fail "disconnected source-file client's next command ran"
# Firing an event after cancellation must not call the removed event sink.
$TMUX wait-for -E window-renamed &
dead=$!
pids="$dead"
wait_list "client-$dead" -E window-renamed
kill -KILL "$dead" || fail "kill event waiter failed"
wait "$dead" 2>/dev/null || true
pids=
wait_list "" -E window-renamed
wait_free "$dead"
$TMUX rename-window -t test:0 renamed || fail "rename-window failed"
$TMUX display-message -p '#{pid}' >/dev/null || fail "server exited"
exit 0
+2 -2
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.161 2026/09/22 16:56:07 nicm Exp $ */
/* $OpenBSD: screen-redraw.c,v 1.159 2026/09/09 08:31:42 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1673,7 +1673,7 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
prompt_draw(wp->prompt, &pdd);
screen_write_stop(&ctx);
tty_draw_line(tty, &screen, offset, 0, width, px, cy, NULL);
tty_draw_line(tty, &screen, 0, offset, width, px, cy, NULL);
screen_free(&screen);
}
+1 -4
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.514 2026/09/22 06:46:50 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.512 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -502,9 +502,6 @@ server_client_lost(struct client *c)
TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c);
cmd_wait_for_client_lost(c);
cmdq_next(c);
if (c->flags & CLIENT_ATTACHED) {
server_client_attached_lost(c);
events_fire_client("client-detached", c);
+3 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: server-fn.c,v 1.154 2026/09/21 12:43:36 nicm Exp $ */
/* $OpenBSD: server-fn.c,v 1.153 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -310,8 +310,8 @@ server_link_window(struct session *src, struct winlink *srcwl,
}
if (killflag) {
/*
* Can't use session_detach as it won't detach the only
* window.
* Can't use session_detach as it will destroy session
* if this makes it empty.
*/
events_fire_winlink("window-unlinked", dstwl);
dstwl->flags &= ~WINLINK_ALERTFLAGS;
+4 -9
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: session.c,v 1.108 2026/09/21 12:43:36 nicm Exp $ */
/* $OpenBSD: session.c,v 1.107 2026/08/05 07:35:35 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -336,17 +336,10 @@ session_attach(struct session *s, struct window *w, int idx, char **cause)
return (wl);
}
/*
* Detach a window from a session. Returns 1 if the window has not been
* detached - the caller must destroy the session.
*/
/* Detach a window from a session. */
int
session_detach(struct session *s, struct winlink *wl)
{
if (RB_MIN(winlinks, &s->windows) == wl &&
RB_MAX(winlinks, &s->windows) == wl)
return (1);
if (s->curw == wl &&
session_last(s) != 0 &&
session_previous(s, 0) != 0)
@@ -359,6 +352,8 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows))
return (1);
return (0);
}
+6 -6
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: spawn.c,v 1.53 2026/09/21 12:43:36 nicm Exp $ */
/* $OpenBSD: spawn.c,v 1.52 2026/08/20 09:19:24 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -166,8 +166,8 @@ spawn_window(struct spawn_context *sc, char **cause)
}
if (wl != NULL) {
/*
* Can't use session_detach as it won't detach the only
* window.
* Can't use session_detach as it will destroy session
* if this makes it empty.
*/
wl->flags &= ~WINLINK_ALERTFLAGS;
events_fire_winlink("window-unlinked", wl);
@@ -701,7 +701,7 @@ spawn_editor_finish(struct window_pane *wp)
struct spawn_editor_state *
spawn_editor(struct client *c, const char *buf, size_t len,
spawn_finish_edit_cb cb, void *arg)
const char *editor, spawn_finish_edit_cb cb, void *arg)
{
struct spawn_editor_state *es;
struct spawn_context sc = { 0 };
@@ -715,13 +715,13 @@ spawn_editor(struct client *c, const char *buf, size_t len,
FILE *f;
char *cmd, *cause = NULL;
char path[] = _PATH_TMP "tmux.XXXXXXXX";
const char *editor;
int fd;
if (w->modal != NULL)
return (NULL);
editor = options_get_string(global_options, "editor");
if (editor == NULL)
editor = options_get_string(global_options, "editor");
fd = mkstemp(path);
if (fd == -1)
return (NULL);
+130 -8
View File
@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1172 2026/09/22 06:58:05 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1170 2026/09/11 10:17:16 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: September 22 2026 $
.Dd $Mdocdate: September 11 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -2006,6 +2006,25 @@ Same as
.Ic copy\-pipe
but also exit copy mode.
.It Xo
.Ic copy\-pipe\-output
.Op Fl aCP
.Op Ar command
.Op Ar prefix
.Xc
Copy the current OSC 133 command output, pipe it to
.Ar command ,
and clear the selection.
.Ar prefix
is used to name the new paste buffer.
.It Xo
.Ic copy\-output
.Op Fl aCP
.Op Ar prefix
.Xc
Copy the current OSC 133 command output and clear the selection.
.Ar prefix
is used to name the new paste buffer.
.It Xo
.Ic copy\-selection
.Op Fl CP
.Op Ar prefix
@@ -2171,6 +2190,27 @@ Hide line numbers in copy mode.
.Xc
Toggle line numbers in copy mode.
.It Xo
.Ic collapse\-output
.Op Fl a
.Xc
Collapse the output for the command on the cursor line.
With
.Fl a ,
collapse all command output in the copy-mode buffer.
Text before the first OSC 133 prompt is treated as an initial command output
region.
.It Xo
.Ic expand\-output
.Op Fl a
.Xc
Expand the output for the command on the cursor line.
With
.Fl a ,
expand all command output in the copy-mode buffer.
.Pp
The Tab key toggles output for the command on the cursor line and S-Tab
toggles all command output.
.It Xo
.Ic middle\-line
(vi: M)
(emacs: M\-r)
@@ -2224,6 +2264,32 @@ but treat all non-whitespace characters as part of a word.
.Xc
Switch at which end of the selection the cursor sits.
.It Xo
.Ic open\-output
.Op Fl a
.Op Ar command
.Xc
Open the current OSC 133 command output in an editor.
If
.Ar command
is given, it is used as the editor command; otherwise, the
.Ic editor
option is used.
.It Xo
.Ic open\-selection
.Op Ar command
.Xc
Open the current selection in an editor.
If
.Ar command
is given, it is used as the editor command; otherwise, the
.Ic editor
option is used.
In emacs mode,
.Ql e ,
and in vi mode,
.Ql M\-e ,
opens the selection if one is present, otherwise the command output.
.It Xo
.Ic page\-down
(vi: C\-f)
(emacs: PageDown)
@@ -2264,6 +2330,21 @@ Same as
.Ic pipe
but also exit copy mode.
.It Xo
.Ic pipe\-output
.Op Fl a
.Op Ar command
.Xc
Pipe the current OSC 133 command output to
.Ar command
and clear the selection.
.It Xo
.Ic pipe\-selection
.Op Ar command
.Xc
Pipe the selected text to
.Ar command
and clear the selection.
.It Xo
.Ic previous\-matching\-bracket
(emacs: M\-C\-b)
.Xc
@@ -2453,6 +2534,15 @@ Search forward for the specified plain text.
Repeat the last search in the reverse direction (forward becomes backward and
backward becomes forward).
.It Xo
.Ic select\-output
.Op Fl a
.Xc
Select the current OSC 133 command output.
The default emacs binding is
.Ql C\-o
and the default vi binding is
.Ql M\-o .
.It Xo
.Ic select\-line
(vi: V)
.Xc
@@ -2496,6 +2586,20 @@ Toggle the visibility of the position indicator in the top right.
Move to the top line.
.El
.Pp
The output commands use OSC 133 output text from the
.Ql C
marker up to its matching
.Ql D
marker.
If there is no
.Ql D
marker, the output ends at the end of the copy-mode buffer.
At the following prompt or below it, they use the most recent nonempty output.
With
.Fl a ,
they use the entire copy-mode buffer instead.
If no output range can be found, they do nothing.
.Pp
The search commands come in several varieties:
.Ql search\-forward
and
@@ -2603,12 +2707,19 @@ The synopsis for the
command is:
.Bl -tag -width Ds
.It Xo Ic copy\-mode
.Op Fl dekHMqSu
.Op Fl UcdekHMqSu
.Op Fl s Ar src\-pane
.Op Fl t Ar target\-pane
.Xc
Enter copy mode.
.Pp
.Fl c
enters copy mode with OSC 133 command output collapsed.
.Fl U
enters copy mode with OSC 133 output controls shown and all output expanded.
When output is collapsed, the prompt and entered command are shown and text
from the output start marker to the next primary prompt marker is hidden.
.Pp
.Fl u
enters copy mode and scrolls one page up and
.Fl d
@@ -5085,9 +5196,6 @@ The available features are:
.Bl -tag -width Ds
.It 256
Supports 256 colours with the SGR escape sequences.
.It appesc
Supports application escape key mode, where the Escape key sends a sequence
that cannot be mistaken for the start of another key.
.It clipboard
Allows setting the system clipboard.
.It ccolour
@@ -5812,6 +5920,20 @@ section.
.It Ic copy\-mode\-position\-format Ar format
Format of the position indicator in copy mode.
.Pp
.It Ic copy\-mode\-exit\-status\-format Ar format
Format of the OSC 133 exit status indicator in the copy-mode gutter.
The default displays a theme red
.Ql !
for a nonzero status and a space otherwise.
The gutter grows to fit the expanded format.
The format is drawn to the left of the collapse control, and its alignment
applies within that area.
The format is expanded with
.Ql exit_status
and
.Ql exit_status_present ,
which is set only when the OSC 133 end-of-output marker includes a status.
.Pp
.It Ic copy\-mode\-position\-style Ar style
Set the style of the position indicator in copy mode.
For how to specify
@@ -5983,6 +6105,8 @@ single lines using ACS or UTF\-8 characters
double lines using UTF\-8 characters
.It heavy
heavy lines using UTF\-8 characters
.It rounded
single lines with rounded corners using UTF\-8 characters
.It simple
simple ASCII characters
.It number
@@ -8971,8 +9095,6 @@ These are set automatically if the
capability is present.
.It Em \&Dseks , \&Eneks
Disable and enable extended keys.
.It Em \&Dsesc , \&Enesc
Disable and enable application escape key mode.
.It Em \&Dsfcs , \&Enfcs
Disable and enable focus reporting.
These are set automatically if the
+9 -11
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1447 2026/09/22 14:10:26 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1440 2026/09/11 10:17:16 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -464,7 +464,6 @@ enum tty_code_code {
TTYC_DL1,
TTYC_DSBP,
TTYC_DSEKS,
TTYC_DSESC,
TTYC_DSFCS,
TTYC_DSMG,
TTYC_E3,
@@ -475,7 +474,6 @@ enum tty_code_code {
TTYC_ENACS,
TTYC_ENBP,
TTYC_ENEKS,
TTYC_ENESC,
TTYC_ENFCS,
TTYC_ENMG,
TTYC_FSL,
@@ -818,6 +816,7 @@ struct colour_palette {
#define GRID_LINE_START_OUTPUT 0x40
#define GRID_LINE_END_OUTPUT 0x80
#define GRID_LINE_HYPERLINK 0x100
#define GRID_LINE_END_OUTPUT_STATUS 0x200
/* All OSC 133 flags. */
#define GRID_LINE_OSC133_FLAGS \
@@ -825,7 +824,8 @@ struct colour_palette {
GRID_LINE_SECOND_PROMPT| \
GRID_LINE_START_COMMAND| \
GRID_LINE_START_OUTPUT| \
GRID_LINE_END_OUTPUT)
GRID_LINE_END_OUTPUT| \
GRID_LINE_END_OUTPUT_STATUS)
/* Grid string flags. */
#define GRID_STRING_WITH_SEQUENCES 0x1
@@ -1152,7 +1152,8 @@ enum pane_lines {
PANE_LINES_SIMPLE,
PANE_LINES_NUMBER,
PANE_LINES_SPACES,
PANE_LINES_NONE
PANE_LINES_NONE,
PANE_LINES_ROUNDED
};
/* Pane border indicator option. */
@@ -1740,7 +1741,6 @@ struct tty_term {
#define TERM_VT100LIKE 0x20
#define TERM_SIXEL 0x40
#define TERM_INVALIDMS 0x80
#define TERM_NOREPLACE 0x100
int flags;
LIST_ENTRY(tty_term) entry;
@@ -2306,7 +2306,7 @@ struct client {
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_WRITE_ACK 0x4000000000ULL
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
#define CLIENT_CONTROL_DISCARD 0x10000000000ULL
#define CLIENT_CONTROL_DISCARD 0x1000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
@@ -3010,7 +3010,7 @@ void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
/* tty-term.c */
extern struct tty_terms tty_terms;
u_int tty_term_ncodes(void);
void tty_term_apply(struct tty_term *, const char *, int, int);
void tty_term_apply(struct tty_term *, const char *, int);
void tty_term_apply_overrides(struct tty_term *);
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, char **);
void tty_term_free(struct tty_term *);
@@ -3230,7 +3230,6 @@ void cmdq_print_data(struct cmdq_item *, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */
void cmd_wait_for_client_lost(struct client *);
void cmd_wait_for_flush(void);
/* client.c */
@@ -3837,7 +3836,6 @@ int window_pane_get_pane_status(struct window_pane *);
struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
u_int);
int window_pane_is_floating(struct window_pane *);
int window_pane_is_floating_with_hidden(struct window_pane *);
/* window-border.c */
void window_set_fill_cells(struct window *);
@@ -4231,7 +4229,7 @@ struct winlink *spawn_window(struct spawn_context *, char **);
struct window_pane *spawn_pane(struct spawn_context *, char **);
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
spawn_finish_edit_cb, void *);
const char *, spawn_finish_edit_cb, void *);
void spawn_cancel_editor(struct spawn_editor_state *);
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
void spawn_editor_finish(struct window_pane *);
+2 -7
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: tty-draw.c,v 1.17 2026/09/22 06:49:47 nicm Exp $ */
/* $OpenBSD: tty-draw.c,v 1.15 2026/07/26 09:02:08 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -219,12 +219,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
}
/* Did the previous line wrap on to this one? */
if (py != 0 &&
atx == 0 &&
tty->cx >= tty->sx &&
tty->cy != UINT_MAX &&
tty->cy + 1 == aty &&
nx == tty->sx) {
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl->flags & GRID_LINE_WRAPPED)
wrapped = 1;
+4 -18
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: tty-features.c,v 1.45 2026/09/22 14:10:26 nicm Exp $ */
/* $OpenBSD: tty-features.c,v 1.43 2026/08/31 12:41:03 kirill Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -189,18 +189,6 @@ static const struct tty_feature tty_feature_focus = {
0
};
/* Terminal supports application escape key mode. */
static const char *const tty_feature_appesc_capabilities[] = {
"Enesc=\\E[?7727h",
"Dsesc=\\E[?7727l",
NULL
};
static const struct tty_feature tty_feature_appesc = {
"appesc",
tty_feature_appesc_capabilities,
0
};
/* Terminal supports cursor styles. */
static const char *const tty_feature_cstyle_capabilities[] = {
"Ss=\\E[%p1%d q",
@@ -210,7 +198,7 @@ static const char *const tty_feature_cstyle_capabilities[] = {
static const struct tty_feature tty_feature_cstyle = {
"cstyle",
tty_feature_cstyle_capabilities,
TERM_NOREPLACE
0
};
/* Terminal supports cursor colours. */
@@ -390,7 +378,6 @@ static const struct tty_feature tty_feature_utf8 = {
/* Available terminal features. */
static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
&tty_feature_appesc,
&tty_feature_bpaste,
&tty_feature_ccolour,
&tty_feature_clipboard,
@@ -554,11 +541,11 @@ tty_apply_features(struct tty_term *term)
capability = tf->capabilities;
while (*capability != NULL) {
log_debug("adding capability: %s", *capability);
tty_term_apply(term, *capability, 1, tf->flags);
tty_term_apply(term, *capability, 1);
capability++;
}
}
term->flags |= (tf->flags & ~TERM_NOREPLACE);
term->flags |= tf->flags;
if (tf == &tty_feature_utf8)
c->flags |= CLIENT_UTF8;
}
@@ -581,7 +568,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
"256,RGB,bpaste,clipboard,mouse,strikethrough,title"
{ .name = "mintty",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"appesc,"
"ccolour,"
"cstyle,"
"extkeys,"
+3 -9
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: tty-term.c,v 1.111 2026/09/22 14:10:26 nicm Exp $ */
/* $OpenBSD: tty-term.c,v 1.109 2026/08/25 08:37:08 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -90,7 +90,6 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_DL1] = { TTYCODE_STRING, "dl1" },
[TTYC_DL] = { TTYCODE_STRING, "dl" },
[TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" },
[TTYC_DSESC] = { TTYCODE_STRING, "Dsesc" },
[TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" },
[TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" },
[TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" },
@@ -102,7 +101,6 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_ENACS] = { TTYCODE_STRING, "enacs" },
[TTYC_ENBP] = { TTYCODE_STRING, "Enbp" },
[TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" },
[TTYC_ENESC] = { TTYCODE_STRING, "Enesc" },
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
@@ -364,8 +362,7 @@ tty_term_override_next(const char *s, size_t *offset)
}
void
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet,
int flags)
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet)
{
const struct tty_term_code_entry *ent;
struct tty_code *code;
@@ -408,9 +405,6 @@ tty_term_apply(struct tty_term *term, const char *capabilities, int quiet,
if (strcmp(s, ent->name) != 0)
continue;
code = &term->codes[i];
if ((flags & TERM_NOREPLACE) &&
code->type != TTYCODE_NONE)
continue;
if (remove) {
code->type = TTYCODE_NONE;
@@ -463,7 +457,7 @@ tty_term_apply_overrides(struct tty_term *term)
offset = 0;
first = tty_term_override_next(s, &offset);
if (first != NULL && fnmatch(first, term->name, 0) == 0)
tty_term_apply(term, s + offset, 0, 0);
tty_term_apply(term, s + offset, 0);
a = options_array_next(a);
}
+5 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: tty.c,v 1.482 2026/09/22 06:58:06 nicm Exp $ */
/* $OpenBSD: tty.c,v 1.480 2026/08/25 08:37:08 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -503,7 +503,8 @@ tty_stop_tty(struct tty *tty)
if (tty_term_has(tty->term, TTYC_DSBP))
tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
tty_raw(tty, tty_term_string(tty->term, TTYC_DSESC));
if (tty->term->flags & TERM_VT100LIKE)
tty_raw(tty, "\033[?7727l");
tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
@@ -562,7 +563,8 @@ tty_update_features(struct tty *tty)
tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
if (options_get_number(global_options, "focus-events"))
tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
tty_puts(tty, tty_term_string(tty->term, TTYC_ENESC));
if (tty->term->flags & TERM_VT100LIKE)
tty_puts(tty, "\033[?7727h");
/*
* Features might have changed since the first draw during attach. For
+2 -7
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: utf8.c,v 1.73 2026/09/20 08:11:00 nicm Exp $ */
/* $OpenBSD: utf8.c,v 1.72 2026/09/01 12:49:49 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -374,13 +374,8 @@ utf8_add_to_width_cache(const char *s)
wc_end = wc_start;
}
wc = wc_start;
for (;;) {
for (wc = wc_start; wc <= wc_end; wc++)
utf8_insert_width_cache(wc, width);
if (wc == wc_end)
break;
wc++;
}
} else {
utf8_no_width = 1;
ud = utf8_fromcstr(copy);
+5 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: window-border.c,v 1.3 2026/07/23 09:38:27 nicm Exp $ */
/* $OpenBSD: window-border.c,v 1.4 2026/09/11 10:17:16 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -120,6 +120,10 @@ window_get_border_cell(struct window_pane *wp, enum pane_lines pane_lines,
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
break;
case PANE_LINES_ROUNDED:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
break;
case PANE_LINES_SIMPLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
+2 -1
View File
@@ -606,7 +606,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
ed->name = xstrdup(paste_buffer_name(pb));
ed->pb = pb;
ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
ed->editor = spawn_editor(c, buf, len, NULL, window_buffer_edit_close_cb,
ed);
if (ed->editor == NULL)
window_buffer_finish_edit(ed);
else {
+1568 -48
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -2104,8 +2104,8 @@ window_customize_start_edit(struct window_customize_modedata *data,
buf = "\n";
len = 1;
}
ed->editor = spawn_editor(c, buf, len, window_customize_edit_close_cb,
ed);
ed->editor = spawn_editor(c, buf, len, NULL,
window_customize_edit_close_cb, ed);
free(value);
if (ed->editor == NULL)
window_customize_finish_edit(ed);
+2 -22
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.377 2026/09/21 10:33:16 nicm Exp $ */
/* $OpenBSD: window.c,v 1.374 2026/09/08 08:37:56 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -449,17 +449,9 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
static void
window_destroy(struct window *w)
{
struct window_pane *wp;
log_debug("window @%u destroyed (%d references)", w->id, w->references);
if (w->flags & WINDOW_ZOOMED) {
w->flags &= ~WINDOW_ZOOMED;
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->flags &= ~PANE_ZOOMED;
wp->saved_layout_cell = NULL;
}
}
window_unzoom(w, 0);
RB_REMOVE(windows, &windows, w);
layout_free_cell(w->layout_root, 0);
@@ -2914,15 +2906,3 @@ window_pane_is_floating(struct window_pane *wp)
return (0);
return (1);
}
int
window_pane_is_floating_with_hidden(struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
if (lc == NULL)
lc = wp->saved_layout_cell;
if (lc == NULL || (lc->flags & LAYOUT_CELL_FLOATING) == 0)
return (0);
return (1);
}