mirror of
https://github.com/tmux/tmux.git
synced 2026-10-10 20:24:21 +00:00
Merge and fix conflicts.
This commit is contained in:
6
.github/workflows/regress.yml
vendored
6
.github/workflows/regress.yml
vendored
@@ -70,6 +70,12 @@ jobs:
|
|||||||
utf8proc \
|
utf8proc \
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
|
- name: increase pty limit
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
sysctl kern.tty.ptmx_max
|
||||||
|
sudo sysctl -w kern.tty.ptmx_max=999
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
run: |
|
run: |
|
||||||
sh autogen.sh
|
sh autogen.sh
|
||||||
|
|||||||
5
CHANGES
5
CHANGES
@@ -136,6 +136,8 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
* Preserve existing Ss and Se terminfo capabilities for cursor styles and
|
* Preserve existing Ss and Se terminfo capabilities for cursor styles and
|
||||||
restoring the default cursor (issue 5476).
|
restoring the default cursor (issue 5476).
|
||||||
|
|
||||||
|
* Enable left and right margins for WezTerm and Ghostty (Michael Grant).
|
||||||
|
|
||||||
* Add a #{A/count:frames} modifier to show a series of frames as an animation
|
* Add a #{A/count:frames} modifier to show a series of frames as an animation
|
||||||
in the status line (issue 5412 from Fernando Daciuk).
|
in the status line (issue 5412 from Fernando Daciuk).
|
||||||
|
|
||||||
@@ -231,6 +233,9 @@ CHANGES FROM 3.7c TO 3.8
|
|||||||
* Fix choose mode filtering when more than one pane exists (issue 5326), and
|
* 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.
|
fix an infinite loop in customize mode when a filter does not match.
|
||||||
|
|
||||||
|
* Preserve the scroll position in tree modes when rebuilding the list
|
||||||
|
(Kim A Brandt, issue 5631).
|
||||||
|
|
||||||
* Fix regsub when the pattern is anchored at the start (issue 5341).
|
* Fix regsub when the pattern is anchored at the start (issue 5341).
|
||||||
|
|
||||||
* Fix redraw issues around synchronized updates, copy mode, tabs, padding,
|
* Fix redraw issues around synchronized updates, copy mode, tabs, padding,
|
||||||
|
|||||||
30
cfg.c
30
cfg.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */
|
/* $OpenBSD: cfg.c,v 1.92 2026/10/02 12:28:07 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,14 +31,17 @@ int cfg_finished;
|
|||||||
static char **cfg_causes;
|
static char **cfg_causes;
|
||||||
static u_int cfg_ncauses;
|
static u_int cfg_ncauses;
|
||||||
static struct cmdq_item *cfg_item;
|
static struct cmdq_item *cfg_item;
|
||||||
|
static int cfg_started;
|
||||||
|
|
||||||
int cfg_quiet = 1;
|
int cfg_quiet = 1;
|
||||||
char **cfg_files;
|
char **cfg_files;
|
||||||
u_int cfg_nfiles;
|
u_int cfg_nfiles;
|
||||||
|
|
||||||
static enum cmd_retval
|
static enum cmd_retval
|
||||||
cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
|
cfg_client_done(struct cmdq_item *item, __unused void *data)
|
||||||
{
|
{
|
||||||
|
if (cmdq_get_client(item)->flags & CLIENT_DEAD)
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
if (!cfg_finished)
|
if (!cfg_finished)
|
||||||
return (CMD_RETURN_WAIT);
|
return (CMD_RETURN_WAIT);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
@@ -53,14 +56,28 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
|
|||||||
|
|
||||||
cfg_show_causes(NULL);
|
cfg_show_causes(NULL);
|
||||||
|
|
||||||
if (cfg_item != NULL)
|
if (cfg_item != NULL) {
|
||||||
cmdq_continue(cfg_item);
|
cmdq_continue(cfg_item);
|
||||||
|
cfg_item = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
prompt_load_history();
|
prompt_load_history();
|
||||||
|
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cfg_client_lost(struct client *c)
|
||||||
|
{
|
||||||
|
if (c != cfg_client)
|
||||||
|
return;
|
||||||
|
cfg_client = NULL;
|
||||||
|
if (cfg_item != NULL) {
|
||||||
|
cmdq_continue(cfg_item);
|
||||||
|
cfg_item = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start_cfg(void)
|
start_cfg(void)
|
||||||
{
|
{
|
||||||
@@ -77,12 +94,19 @@ start_cfg(void)
|
|||||||
* Because start_cfg() is called so early, we can be sure the client's
|
* Because start_cfg() is called so early, we can be sure the client's
|
||||||
* command queue is currently empty and our callback will be at the
|
* command queue is currently empty and our callback will be at the
|
||||||
* front - we need to get in before MSG_COMMAND.
|
* front - we need to get in before MSG_COMMAND.
|
||||||
|
*
|
||||||
|
* If the initial client is lost before the configuration finishes, the
|
||||||
|
* next client to identify is first in the list and ends up here. Block
|
||||||
|
* it instead, but do not load the files again.
|
||||||
*/
|
*/
|
||||||
cfg_client = c = TAILQ_FIRST(&clients);
|
cfg_client = c = TAILQ_FIRST(&clients);
|
||||||
if (c != NULL) {
|
if (c != NULL) {
|
||||||
cfg_item = cmdq_get_callback(cfg_client_done, NULL);
|
cfg_item = cmdq_get_callback(cfg_client_done, NULL);
|
||||||
cmdq_append(c, cfg_item);
|
cmdq_append(c, cfg_item);
|
||||||
}
|
}
|
||||||
|
if (cfg_started)
|
||||||
|
return;
|
||||||
|
cfg_started = 1;
|
||||||
|
|
||||||
if (cfg_quiet)
|
if (cfg_quiet)
|
||||||
flags = CMD_PARSE_QUIET;
|
flags = CMD_PARSE_QUIET;
|
||||||
|
|||||||
2
client.c
2
client.c
@@ -283,7 +283,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||||||
#ifdef HAVE_SYSTEMD
|
#ifdef HAVE_SYSTEMD
|
||||||
if (systemd_activated()) {
|
if (systemd_activated()) {
|
||||||
/* socket-based activation, do not even try to be a client. */
|
/* socket-based activation, do not even try to be a client. */
|
||||||
fd = server_start(client_proc, flags, base, 0, NULL);
|
fd = server_start(client_proc, flags, base, -1, NULL);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
fd = client_connect(base, socket_path, client_flags);
|
fd = client_connect(base, socket_path, client_flags);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-attach-session.c,v 1.91 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-attach-session.c,v 1.93 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -35,6 +35,7 @@ static enum cmd_retval cmd_attach_session_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_attach_session_entry = {
|
const struct cmd_entry cmd_attach_session_entry = {
|
||||||
.name = "attach-session",
|
.name = "attach-session",
|
||||||
|
.description = "Attach to an existing session.",
|
||||||
.alias = "attach",
|
.alias = "attach",
|
||||||
|
|
||||||
.args = { "c:dEf:rt:x", 0, 0, NULL },
|
.args = { "c:dEf:rt:x", 0, 0, NULL },
|
||||||
@@ -91,8 +92,10 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
|||||||
wp = target.wp;
|
wp = target.wp;
|
||||||
|
|
||||||
if (wl != NULL) {
|
if (wl != NULL) {
|
||||||
if (wp != NULL)
|
if (wp != NULL) {
|
||||||
|
window_redraw_active_switch(wp->window, wp);
|
||||||
window_set_active_pane(wp->window, wp, 1);
|
window_set_active_pane(wp->window, wp, 1);
|
||||||
|
}
|
||||||
session_set_current(s, wl);
|
session_set_current(s, wl);
|
||||||
if (wp != NULL)
|
if (wp != NULL)
|
||||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-bind-key.c,v 1.47 2025/04/09 07:03:04 nicm Exp $ */
|
/* $OpenBSD: cmd-bind-key.c,v 1.48 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -34,6 +34,7 @@ static enum cmd_retval cmd_bind_key_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_bind_key_entry = {
|
const struct cmd_entry cmd_bind_key_entry = {
|
||||||
.name = "bind-key",
|
.name = "bind-key",
|
||||||
|
.description = "Bind a key to one or more commands.",
|
||||||
.alias = "bind",
|
.alias = "bind",
|
||||||
|
|
||||||
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
|
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */
|
/* $OpenBSD: cmd-break-pane.c,v 1.77 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_break_pane_entry = {
|
const struct cmd_entry cmd_break_pane_entry = {
|
||||||
.name = "break-pane",
|
.name = "break-pane",
|
||||||
|
.description = "Move a pane into its own window or make it floating.",
|
||||||
.alias = "breakp",
|
.alias = "breakp",
|
||||||
|
|
||||||
.args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
|
.args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-capture-pane.c,v 1.69 2026/09/08 07:31:59 nicm Exp $ */
|
/* $OpenBSD: cmd-capture-pane.c,v 1.70 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
|
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
|
||||||
@@ -40,6 +40,7 @@ static char *cmd_capture_pane_hyperlinks(struct grid *, struct screen *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_capture_pane_entry = {
|
const struct cmd_entry cmd_capture_pane_entry = {
|
||||||
.name = "capture-pane",
|
.name = "capture-pane",
|
||||||
|
.description = "Capture pane content.",
|
||||||
.alias = "capturep",
|
.alias = "capturep",
|
||||||
|
|
||||||
.args = { "ab:CeE:FHIJLMNpPqRS:Tt:", 0, 0, NULL },
|
.args = { "ab:CeE:FHIJLMNpPqRS:Tt:", 0, 0, NULL },
|
||||||
@@ -54,6 +55,7 @@ const struct cmd_entry cmd_capture_pane_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_clear_history_entry = {
|
const struct cmd_entry cmd_clear_history_entry = {
|
||||||
.name = "clear-history",
|
.name = "clear-history",
|
||||||
|
.description = "Clear a pane's history.",
|
||||||
.alias = "clearhist",
|
.alias = "clearhist",
|
||||||
|
|
||||||
.args = { "Ht:", 0, 0, NULL },
|
.args = { "Ht:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-choose-tree.c,v 1.58 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cmd-choose-tree.c,v 1.60 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
|
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_choose_tree_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_choose_tree_entry = {
|
const struct cmd_entry cmd_choose_tree_entry = {
|
||||||
.name = "choose-tree",
|
.name = "choose-tree",
|
||||||
|
.description = "Browse sessions and windows interactively.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
|
.args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
|
||||||
@@ -45,6 +46,7 @@ const struct cmd_entry cmd_choose_tree_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_choose_client_entry = {
|
const struct cmd_entry cmd_choose_client_entry = {
|
||||||
.name = "choose-client",
|
.name = "choose-client",
|
||||||
|
.description = "Choose a client interactively.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:hiK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
.args = { "F:f:hiK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||||
@@ -59,6 +61,7 @@ const struct cmd_entry cmd_choose_client_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_choose_buffer_entry = {
|
const struct cmd_entry cmd_choose_buffer_entry = {
|
||||||
.name = "choose-buffer",
|
.name = "choose-buffer",
|
||||||
|
.description = "Choose a paste buffer interactively.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
.args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||||
@@ -73,6 +76,7 @@ const struct cmd_entry cmd_choose_buffer_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_customize_mode_entry = {
|
const struct cmd_entry cmd_customize_mode_entry = {
|
||||||
.name = "customize-mode",
|
.name = "customize-mode",
|
||||||
|
.description = "Browse and edit options and key bindings.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:kNt:yZ", 0, 0, NULL },
|
.args = { "F:f:kNt:yZ", 0, 0, NULL },
|
||||||
@@ -86,10 +90,12 @@ const struct cmd_entry cmd_customize_mode_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_switch_mode_entry = {
|
const struct cmd_entry cmd_switch_mode_entry = {
|
||||||
.name = "switch-mode",
|
.name = "switch-mode",
|
||||||
|
.description = "Switch the active pane mode.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:kst:wZ", 0, 1, cmd_choose_tree_args_parse },
|
.args = { "F:kO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
|
||||||
.usage = "[-kswZ] [-F format] " CMD_TARGET_PANE_USAGE " [command]",
|
.usage = "[-krsZw] [-F format] [-O sort-order] " CMD_TARGET_PANE_USAGE
|
||||||
|
" [command]",
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
@@ -99,6 +105,7 @@ const struct cmd_entry cmd_switch_mode_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_display_panes_entry = {
|
const struct cmd_entry cmd_display_panes_entry = {
|
||||||
.name = "display-panes",
|
.name = "display-panes",
|
||||||
|
.description = "Display pane numbers.",
|
||||||
.alias = "displayp",
|
.alias = "displayp",
|
||||||
|
|
||||||
.args = { "d:kNs:t:Z", 0, 1, cmd_choose_tree_args_parse },
|
.args = { "d:kNs:t:Z", 0, 1, cmd_choose_tree_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-command-prompt.c,v 1.76 2026/08/25 06:04:33 nicm Exp $ */
|
/* $OpenBSD: cmd-command-prompt.c,v 1.77 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -40,6 +40,7 @@ static void cmd_command_prompt_free(void *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_command_prompt_entry = {
|
const struct cmd_entry cmd_command_prompt_entry = {
|
||||||
.name = "command-prompt",
|
.name = "command-prompt",
|
||||||
|
.description = "Open an interactive command prompt.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
|
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-confirm-before.c,v 1.61 2026/06/24 10:55:39 nicm Exp $ */
|
/* $OpenBSD: cmd-confirm-before.c,v 1.62 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -39,6 +39,7 @@ static void cmd_confirm_before_free(void *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_confirm_before_entry = {
|
const struct cmd_entry cmd_confirm_before_entry = {
|
||||||
.name = "confirm-before",
|
.name = "confirm-before",
|
||||||
|
.description = "Ask for confirmation before a command.",
|
||||||
.alias = "confirm",
|
.alias = "confirm",
|
||||||
|
|
||||||
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
|
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-copy-mode.c,v 1.54 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cmd-copy-mode.c,v 1.56 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -28,6 +28,7 @@ static enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_copy_mode_entry = {
|
const struct cmd_entry cmd_copy_mode_entry = {
|
||||||
.name = "copy-mode",
|
.name = "copy-mode",
|
||||||
|
.description = "Enter copy mode.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "dekHMqSs:t:u", 0, 0, NULL },
|
.args = { "dekHMqSs:t:u", 0, 0, NULL },
|
||||||
@@ -42,6 +43,7 @@ const struct cmd_entry cmd_copy_mode_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_clock_mode_entry = {
|
const struct cmd_entry cmd_clock_mode_entry = {
|
||||||
.name = "clock-mode",
|
.name = "clock-mode",
|
||||||
|
.description = "Show a large clock in a pane.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
@@ -103,6 +105,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'd'))
|
if (args_has(args, 'd'))
|
||||||
window_copy_pagedown(wp, 0, args_has(args, 'e'));
|
window_copy_pagedown(wp, 0, args_has(args, 'e'));
|
||||||
if (args_has(args, 'S')) {
|
if (args_has(args, 'S')) {
|
||||||
|
if (c == NULL)
|
||||||
|
return (CMD_RETURN_NORMAL);
|
||||||
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
|
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
|
||||||
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
|
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
|
||||||
tty_oy, args_has(args, 'e'));
|
tty_oy, args_has(args, 'e'));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-detach-client.c,v 1.39 2026/05/22 15:22:43 nicm Exp $ */
|
/* $OpenBSD: cmd-detach-client.c,v 1.40 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_detach_client_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_detach_client_entry = {
|
const struct cmd_entry cmd_detach_client_entry = {
|
||||||
.name = "detach-client",
|
.name = "detach-client",
|
||||||
|
.description = "Detach one or more clients.",
|
||||||
.alias = "detach",
|
.alias = "detach",
|
||||||
|
|
||||||
.args = { "aE:s:t:P", 0, 0, NULL },
|
.args = { "aE:s:t:P", 0, 0, NULL },
|
||||||
@@ -45,6 +46,7 @@ const struct cmd_entry cmd_detach_client_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_suspend_client_entry = {
|
const struct cmd_entry cmd_suspend_client_entry = {
|
||||||
.name = "suspend-client",
|
.name = "suspend-client",
|
||||||
|
.description = "Suspend a client.",
|
||||||
.alias = "suspendc",
|
.alias = "suspendc",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-menu.c,v 1.55 2026/09/21 12:14:32 nicm Exp $ */
|
/* $OpenBSD: cmd-display-menu.c,v 1.56 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static enum cmd_retval cmd_display_popup_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_display_menu_entry = {
|
const struct cmd_entry cmd_display_menu_entry = {
|
||||||
.name = "display-menu",
|
.name = "display-menu",
|
||||||
|
.description = "Display an interactive menu.",
|
||||||
.alias = "menu",
|
.alias = "menu",
|
||||||
|
|
||||||
.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
|
.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
|
||||||
@@ -52,6 +53,7 @@ const struct cmd_entry cmd_display_menu_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_display_popup_entry = {
|
const struct cmd_entry cmd_display_popup_entry = {
|
||||||
.name = "display-popup",
|
.name = "display-popup",
|
||||||
|
.description = "Create a floating pane (compatibility command).",
|
||||||
.alias = "popup",
|
.alias = "popup",
|
||||||
|
|
||||||
.args = { "Bb:Cc:d:e:Eh:ks:S:t:T:w:x:y:", 0, -1, NULL },
|
.args = { "Bb:Cc:d:e:Eh:ks:S:t:T:w:x:y:", 0, -1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-display-message.c,v 1.66 2026/09/08 08:33:10 nicm Exp $ */
|
/* $OpenBSD: cmd-display-message.c,v 1.68 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -37,6 +37,7 @@ static enum cmd_retval cmd_display_message_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_display_message_entry = {
|
const struct cmd_entry cmd_display_message_entry = {
|
||||||
.name = "display-message",
|
.name = "display-message",
|
||||||
|
.description = "Display a message or expand formats.",
|
||||||
.alias = "display",
|
.alias = "display",
|
||||||
|
|
||||||
.args = { "aCc:d:jlINpt:F:v", 0, 1, NULL },
|
.args = { "aCc:d:jlINpt:F:v", 0, 1, NULL },
|
||||||
@@ -114,12 +115,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
template = DISPLAY_MESSAGE_TEMPLATE;
|
template = DISPLAY_MESSAGE_TEMPLATE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -c is intended to be the client where the message should be
|
* -c is also used for the client formats. If it was not given, tc is
|
||||||
* displayed if -p is not given. But it makes sense to use it for the
|
* only the current client, so use it only if it matches the session.
|
||||||
* formats too, assuming it matches the session. If it doesn't, use the
|
|
||||||
* best client for the session.
|
|
||||||
*/
|
*/
|
||||||
if (tc != NULL && tc->session == s)
|
if (tc != NULL && (args_has(args, 'c') || tc->session == s))
|
||||||
c = tc;
|
c = tc;
|
||||||
else if (s != NULL)
|
else if (s != NULL)
|
||||||
c = cmd_find_best_client(s);
|
c = cmd_find_best_client(s);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-find-window.c,v 1.57 2026/07/14 17:17:17 nicm Exp $ */
|
/* $OpenBSD: cmd-find-window.c,v 1.58 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -30,6 +30,7 @@ static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_find_window_entry = {
|
const struct cmd_entry cmd_find_window_entry = {
|
||||||
.name = "find-window",
|
.name = "find-window",
|
||||||
|
.description = "Find windows by content or name.",
|
||||||
.alias = "findw",
|
.alias = "findw",
|
||||||
|
|
||||||
.args = { "CiNrt:TZ", 1, 1, NULL },
|
.args = { "CiNrt:TZ", 1, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-if-shell.c,v 1.87 2026/08/25 06:04:33 nicm Exp $ */
|
/* $OpenBSD: cmd-if-shell.c,v 1.88 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -40,6 +40,7 @@ static void cmd_if_shell_free(void *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_if_shell_entry = {
|
const struct cmd_entry cmd_if_shell_entry = {
|
||||||
.name = "if-shell",
|
.name = "if-shell",
|
||||||
|
.description = "Run a command conditionally.",
|
||||||
.alias = "if",
|
.alias = "if",
|
||||||
|
|
||||||
.args = { "bFt:", 2, 3, cmd_if_shell_args_parse },
|
.args = { "bFt:", 2, 3, cmd_if_shell_args_parse },
|
||||||
|
|||||||
@@ -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.78 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||||
@@ -36,6 +36,7 @@ static void cmd_join_pane_mouse_move(struct client *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_join_pane_entry = {
|
const struct cmd_entry cmd_join_pane_entry = {
|
||||||
.name = "join-pane",
|
.name = "join-pane",
|
||||||
|
.description = "Move a pane into another window.",
|
||||||
.alias = "joinp",
|
.alias = "joinp",
|
||||||
|
|
||||||
.args = { "bdfhvp:l:s:t:", 0, 0, NULL },
|
.args = { "bdfhvp:l:s:t:", 0, 0, NULL },
|
||||||
@@ -50,6 +51,7 @@ const struct cmd_entry cmd_join_pane_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_move_pane_entry = {
|
const struct cmd_entry cmd_move_pane_entry = {
|
||||||
.name = "move-pane",
|
.name = "move-pane",
|
||||||
|
.description = "Move a pane or reposition a floating pane.",
|
||||||
.alias = "movep",
|
.alias = "movep",
|
||||||
|
|
||||||
.args = { "bdD::fhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
|
.args = { "bdD::fhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
|
||||||
@@ -307,7 +309,7 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
|
|||||||
struct window *w;
|
struct window *w;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
int y, ly, x, lx;
|
int y, ly, x, lx, oxoff, oyoff, osx, osy;
|
||||||
|
|
||||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||||
if (wp == NULL) {
|
if (wp == NULL) {
|
||||||
@@ -329,10 +331,16 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
|
|||||||
ly = m->statusat - 1;
|
ly = m->statusat - 1;
|
||||||
|
|
||||||
if (x != lx || y != ly) {
|
if (x != lx || y != ly) {
|
||||||
|
oxoff = wp->xoff;
|
||||||
|
oyoff = wp->yoff;
|
||||||
|
osx = wp->sx;
|
||||||
|
osy = wp->sy;
|
||||||
|
|
||||||
lc->g.xoff += x - lx;
|
lc->g.xoff += x - lx;
|
||||||
lc->g.yoff += y - ly;
|
lc->g.yoff += y - ly;
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
server_redraw_window(w);
|
|
||||||
|
window_redraw_floating_pane(wp, oxoff, oyoff, osx, osy);
|
||||||
server_redraw_window_borders(w);
|
server_redraw_window_borders(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,7 +505,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
layout_close_pane(src_wp);
|
layout_close_pane(src_wp);
|
||||||
|
|
||||||
server_client_remove_pane(src_wp);
|
server_client_remove_pane(src_wp);
|
||||||
window_lost_pane(src_w, src_wp);
|
if (src_w != dst_w)
|
||||||
|
window_lost_pane(src_w, src_wp);
|
||||||
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
|
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
|
||||||
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);
|
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-kill-pane.c,v 1.34 2026/06/09 21:22:22 nicm Exp $ */
|
/* $OpenBSD: cmd-kill-pane.c,v 1.35 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -34,6 +34,7 @@ static int cmd_kill_pane_filter(struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_kill_pane_entry = {
|
const struct cmd_entry cmd_kill_pane_entry = {
|
||||||
.name = "kill-pane",
|
.name = "kill-pane",
|
||||||
|
.description = "Destroy one or more panes.",
|
||||||
.alias = "killp",
|
.alias = "killp",
|
||||||
|
|
||||||
.args = { "af:t:", 0, 0, NULL },
|
.args = { "af:t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-kill-server.c,v 1.20 2021/08/21 10:22:39 nicm Exp $ */
|
/* $OpenBSD: cmd-kill-server.c,v 1.21 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_kill_server_entry = {
|
const struct cmd_entry cmd_kill_server_entry = {
|
||||||
.name = "kill-server",
|
.name = "kill-server",
|
||||||
|
.description = "Stop the tmux server.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "", 0, 0, NULL },
|
.args = { "", 0, 0, NULL },
|
||||||
@@ -42,6 +43,7 @@ const struct cmd_entry cmd_kill_server_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_start_server_entry = {
|
const struct cmd_entry cmd_start_server_entry = {
|
||||||
.name = "start-server",
|
.name = "start-server",
|
||||||
|
.description = "Start the tmux server.",
|
||||||
.alias = "start",
|
.alias = "start",
|
||||||
|
|
||||||
.args = { "", 0, 0, NULL },
|
.args = { "", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-kill-session.c,v 1.31 2026/06/09 12:57:40 nicm Exp $ */
|
/* $OpenBSD: cmd-kill-session.c,v 1.32 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static int cmd_kill_session_filter(struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_kill_session_entry = {
|
const struct cmd_entry cmd_kill_session_entry = {
|
||||||
.name = "kill-session",
|
.name = "kill-session",
|
||||||
|
.description = "Destroy one or more sessions.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "aCgf:t:", 0, 0, NULL },
|
.args = { "aCgf:t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-kill-window.c,v 1.30 2026/06/09 12:57:40 nicm Exp $ */
|
/* $OpenBSD: cmd-kill-window.c,v 1.31 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -33,6 +33,7 @@ static int cmd_kill_window_filter(struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_kill_window_entry = {
|
const struct cmd_entry cmd_kill_window_entry = {
|
||||||
.name = "kill-window",
|
.name = "kill-window",
|
||||||
|
.description = "Destroy one or more windows.",
|
||||||
.alias = "killw",
|
.alias = "killw",
|
||||||
|
|
||||||
.args = { "af:t:", 0, 0, NULL },
|
.args = { "af:t:", 0, 0, NULL },
|
||||||
@@ -46,6 +47,7 @@ const struct cmd_entry cmd_kill_window_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_unlink_window_entry = {
|
const struct cmd_entry cmd_unlink_window_entry = {
|
||||||
.name = "unlink-window",
|
.name = "unlink-window",
|
||||||
|
.description = "Unlink a window from a session.",
|
||||||
.alias = "unlinkw",
|
.alias = "unlinkw",
|
||||||
|
|
||||||
.args = { "kt:", 0, 0, NULL },
|
.args = { "kt:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-buffers.c,v 1.41 2026/02/27 08:25:12 nicm Exp $ */
|
/* $OpenBSD: cmd-list-buffers.c,v 1.42 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -34,6 +34,7 @@ static enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_buffers_entry = {
|
const struct cmd_entry cmd_list_buffers_entry = {
|
||||||
.name = "list-buffers",
|
.name = "list-buffers",
|
||||||
|
.description = "List paste buffers.",
|
||||||
.alias = "lsb",
|
.alias = "lsb",
|
||||||
|
|
||||||
.args = { "F:f:O:r", 0, 0, NULL },
|
.args = { "F:f:O:r", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-clients.c,v 1.42 2026/02/27 08:25:12 nicm Exp $ */
|
/* $OpenBSD: cmd-list-clients.c,v 1.43 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -39,6 +39,7 @@ static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_clients_entry = {
|
const struct cmd_entry cmd_list_clients_entry = {
|
||||||
.name = "list-clients",
|
.name = "list-clients",
|
||||||
|
.description = "List connected clients.",
|
||||||
.alias = "lsc",
|
.alias = "lsc",
|
||||||
|
|
||||||
.args = { "F:f:O:rt:", 0, 0, NULL },
|
.args = { "F:f:O:rt:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-commands.c,v 1.15 2026/02/24 08:22:13 nicm Exp $ */
|
/* $OpenBSD: cmd-list-commands.c,v 1.16 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,20 +31,43 @@
|
|||||||
"#{command_list_name}" \
|
"#{command_list_name}" \
|
||||||
"#{?command_list_alias, (#{command_list_alias}),} " \
|
"#{?command_list_alias, (#{command_list_alias}),} " \
|
||||||
"#{command_list_usage}"
|
"#{command_list_usage}"
|
||||||
|
#define LIST_COMMANDS_HELP_TEMPLATE \
|
||||||
|
"#{p|#{command_list_name_width}:command_list_name} " \
|
||||||
|
"#{command_list_description}"
|
||||||
|
#define LIST_COMMANDS_VERBOSE_TEMPLATE \
|
||||||
|
LIST_COMMANDS_TEMPLATE "\n #{command_list_description}"
|
||||||
|
|
||||||
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
|
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
|
||||||
|
|
||||||
const struct cmd_entry cmd_list_commands_entry = {
|
const struct cmd_entry cmd_list_commands_entry = {
|
||||||
.name = "list-commands",
|
.name = "list-commands",
|
||||||
|
.description = "List commands or show command syntax.",
|
||||||
.alias = "lscm",
|
.alias = "lscm",
|
||||||
|
|
||||||
.args = { "F:", 0, 1, NULL },
|
.args = { "F:h", 0, 1, NULL },
|
||||||
.usage = "[-F format] [command]",
|
.usage = "[-h] [-F format] [command]",
|
||||||
|
|
||||||
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
|
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
|
||||||
.exec = cmd_list_commands
|
.exec = cmd_list_commands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static u_int
|
||||||
|
cmd_list_commands_get_width(const struct cmd_entry *only)
|
||||||
|
{
|
||||||
|
const struct cmd_entry **entryp;
|
||||||
|
u_int width, largest = 0;
|
||||||
|
|
||||||
|
if (only != NULL)
|
||||||
|
return (utf8_cstrwidth(only->name));
|
||||||
|
|
||||||
|
for (entryp = cmd_table; *entryp != NULL; entryp++) {
|
||||||
|
width = utf8_cstrwidth((*entryp)->name);
|
||||||
|
if (width > largest)
|
||||||
|
largest = width;
|
||||||
|
}
|
||||||
|
return (largest);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
|
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
|
||||||
const char *template, struct cmdq_item *item)
|
const char *template, struct cmdq_item *item)
|
||||||
@@ -63,6 +86,11 @@ cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
|
|||||||
else
|
else
|
||||||
s = "";
|
s = "";
|
||||||
format_add(ft, "command_list_usage", "%s", s);
|
format_add(ft, "command_list_usage", "%s", s);
|
||||||
|
if (entry->description != NULL)
|
||||||
|
s = entry->description;
|
||||||
|
else
|
||||||
|
s = "";
|
||||||
|
format_add(ft, "command_list_description", "%s", s);
|
||||||
|
|
||||||
line = format_expand(ft, template);
|
line = format_expand(ft, template);
|
||||||
if (*line != '\0')
|
if (*line != '\0')
|
||||||
@@ -75,33 +103,44 @@ cmd_list_commands(struct cmd *self, struct cmdq_item *item)
|
|||||||
{
|
{
|
||||||
struct args *args = cmd_get_args(self);
|
struct args *args = cmd_get_args(self);
|
||||||
const struct cmd_entry **entryp;
|
const struct cmd_entry **entryp;
|
||||||
const struct cmd_entry *entry;
|
const struct cmd_entry *entry = NULL;
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
const char *template, *command;
|
const char *template, *command;
|
||||||
char *cause;
|
char *cause;
|
||||||
|
int help;
|
||||||
|
u_int width;
|
||||||
|
|
||||||
if ((template = args_get(args, 'F')) == NULL)
|
if ((template = args_get(args, 'F')) == NULL) {
|
||||||
template = LIST_COMMANDS_TEMPLATE;
|
help = args_has(args, 'h');
|
||||||
|
if (help > 1)
|
||||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
template = LIST_COMMANDS_VERBOSE_TEMPLATE;
|
||||||
format_defaults(ft, NULL, NULL, NULL, NULL);
|
else if (help == 1)
|
||||||
|
template = LIST_COMMANDS_HELP_TEMPLATE;
|
||||||
|
else
|
||||||
|
template = LIST_COMMANDS_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
command = args_string(args, 0);
|
command = args_string(args, 0);
|
||||||
if (command == NULL) {
|
if (command != NULL) {
|
||||||
for (entryp = cmd_table; *entryp != NULL; entryp++)
|
|
||||||
cmd_list_single_command(*entryp, ft, template, item);
|
|
||||||
} else {
|
|
||||||
entry = cmd_find(command, &cause);
|
entry = cmd_find(command, &cause);
|
||||||
if (entry != NULL)
|
if (entry == NULL) {
|
||||||
cmd_list_single_command(entry, ft, template, item);
|
|
||||||
else {
|
|
||||||
cmdq_error(item, "%s", cause);
|
cmdq_error(item, "%s", cause);
|
||||||
free(cause);
|
free(cause);
|
||||||
format_free(ft);
|
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||||
|
format_defaults(ft, NULL, NULL, NULL, NULL);
|
||||||
|
width = cmd_list_commands_get_width(entry);
|
||||||
|
format_add(ft, "command_list_name_width", "%u", width);
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
|
for (entryp = cmd_table; *entryp != NULL; entryp++)
|
||||||
|
cmd_list_single_command(*entryp, ft, template, item);
|
||||||
|
} else
|
||||||
|
cmd_list_single_command(entry, ft, template, item);
|
||||||
|
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */
|
/* $OpenBSD: cmd-list-keys.c,v 1.79 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -42,6 +42,7 @@ static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_keys_entry = {
|
const struct cmd_entry cmd_list_keys_entry = {
|
||||||
.name = "list-keys",
|
.name = "list-keys",
|
||||||
|
.description = "List key bindings.",
|
||||||
.alias = "lsk",
|
.alias = "lsk",
|
||||||
|
|
||||||
.args = { "1aF:NO:P:rT:", 0, 1, NULL },
|
.args = { "1aF:NO:P:rT:", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-panes.c,v 1.41 2026/09/08 10:20:08 nicm Exp $ */
|
/* $OpenBSD: cmd-list-panes.c,v 1.42 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static void cmd_list_panes_window(struct cmd *, struct session *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_panes_entry = {
|
const struct cmd_entry cmd_list_panes_entry = {
|
||||||
.name = "list-panes",
|
.name = "list-panes",
|
||||||
|
.description = "List panes and their state.",
|
||||||
.alias = "lsp",
|
.alias = "lsp",
|
||||||
|
|
||||||
.args = { "aF:f:O:rst:", 0, 0, NULL },
|
.args = { "aF:f:O:rst:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-sessions.c,v 1.37 2026/09/08 10:20:08 nicm Exp $ */
|
/* $OpenBSD: cmd-list-sessions.c,v 1.38 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -40,6 +40,7 @@ static enum cmd_retval cmd_list_sessions_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_sessions_entry = {
|
const struct cmd_entry cmd_list_sessions_entry = {
|
||||||
.name = "list-sessions",
|
.name = "list-sessions",
|
||||||
|
.description = "List sessions and their state.",
|
||||||
.alias = "ls",
|
.alias = "ls",
|
||||||
|
|
||||||
.args = { "F:f:O:r", 0, 0, NULL },
|
.args = { "F:f:O:r", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-list-windows.c,v 1.51 2026/09/08 10:20:08 nicm Exp $ */
|
/* $OpenBSD: cmd-list-windows.c,v 1.52 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -43,6 +43,7 @@ static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_list_windows_entry = {
|
const struct cmd_entry cmd_list_windows_entry = {
|
||||||
.name = "list-windows",
|
.name = "list-windows",
|
||||||
|
.description = "List windows and their state.",
|
||||||
.alias = "lsw",
|
.alias = "lsw",
|
||||||
|
|
||||||
.args = { "aF:f:O:rt:", 0, 0, NULL },
|
.args = { "aF:f:O:rt:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */
|
/* $OpenBSD: cmd-load-buffer.c,v 1.68 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -35,6 +35,7 @@ static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_load_buffer_entry = {
|
const struct cmd_entry cmd_load_buffer_entry = {
|
||||||
.name = "load-buffer",
|
.name = "load-buffer",
|
||||||
|
.description = "Load a file into a paste buffer.",
|
||||||
.alias = "loadb",
|
.alias = "loadb",
|
||||||
|
|
||||||
.args = { "b:t:w", 1, 1, NULL },
|
.args = { "b:t:w", 1, 1, NULL },
|
||||||
@@ -78,9 +79,9 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
|
|||||||
tc->session != NULL &&
|
tc->session != NULL &&
|
||||||
(~tc->flags & CLIENT_DEAD))
|
(~tc->flags & CLIENT_DEAD))
|
||||||
tty_set_selection(&tc->tty, "", copy, bsize);
|
tty_set_selection(&tc->tty, "", copy, bsize);
|
||||||
if (tc != NULL)
|
|
||||||
server_client_unref(tc);
|
|
||||||
}
|
}
|
||||||
|
if (tc != NULL)
|
||||||
|
server_client_unref(tc);
|
||||||
cmdq_continue(item);
|
cmdq_continue(item);
|
||||||
|
|
||||||
free(cdata->name);
|
free(cdata->name);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-lock-server.c,v 1.31 2021/08/21 10:22:39 nicm Exp $ */
|
/* $OpenBSD: cmd-lock-server.c,v 1.32 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -28,6 +28,7 @@ static enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_lock_server_entry = {
|
const struct cmd_entry cmd_lock_server_entry = {
|
||||||
.name = "lock-server",
|
.name = "lock-server",
|
||||||
|
.description = "Lock every client.",
|
||||||
.alias = "lock",
|
.alias = "lock",
|
||||||
|
|
||||||
.args = { "", 0, 0, NULL },
|
.args = { "", 0, 0, NULL },
|
||||||
@@ -39,6 +40,7 @@ const struct cmd_entry cmd_lock_server_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_lock_session_entry = {
|
const struct cmd_entry cmd_lock_session_entry = {
|
||||||
.name = "lock-session",
|
.name = "lock-session",
|
||||||
|
.description = "Lock every client attached to a session.",
|
||||||
.alias = "locks",
|
.alias = "locks",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
@@ -52,6 +54,7 @@ const struct cmd_entry cmd_lock_session_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_lock_client_entry = {
|
const struct cmd_entry cmd_lock_client_entry = {
|
||||||
.name = "lock-client",
|
.name = "lock-client",
|
||||||
|
.description = "Lock a client.",
|
||||||
.alias = "lockc",
|
.alias = "lockc",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-move-window.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
|
/* $OpenBSD: cmd-move-window.c,v 1.35 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -30,6 +30,7 @@ static enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_move_window_entry = {
|
const struct cmd_entry cmd_move_window_entry = {
|
||||||
.name = "move-window",
|
.name = "move-window",
|
||||||
|
.description = "Move a window between sessions.",
|
||||||
.alias = "movew",
|
.alias = "movew",
|
||||||
|
|
||||||
.args = { "abdkrs:t:", 0, 0, NULL },
|
.args = { "abdkrs:t:", 0, 0, NULL },
|
||||||
@@ -44,6 +45,7 @@ const struct cmd_entry cmd_move_window_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_link_window_entry = {
|
const struct cmd_entry cmd_link_window_entry = {
|
||||||
.name = "link-window",
|
.name = "link-window",
|
||||||
|
.description = "Link a window into another session.",
|
||||||
.alias = "linkw",
|
.alias = "linkw",
|
||||||
|
|
||||||
.args = { "abdks:t:", 0, 0, NULL },
|
.args = { "abdks:t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-new-session.c,v 1.152 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-new-session.c,v 1.153 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -37,6 +37,7 @@ static enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_new_session_entry = {
|
const struct cmd_entry cmd_new_session_entry = {
|
||||||
.name = "new-session",
|
.name = "new-session",
|
||||||
|
.description = "Create a new session.",
|
||||||
.alias = "new",
|
.alias = "new",
|
||||||
|
|
||||||
.args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL },
|
.args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL },
|
||||||
@@ -53,6 +54,7 @@ const struct cmd_entry cmd_new_session_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_has_session_entry = {
|
const struct cmd_entry cmd_has_session_entry = {
|
||||||
.name = "has-session",
|
.name = "has-session",
|
||||||
|
.description = "Check whether a session exists.",
|
||||||
.alias = "has",
|
.alias = "has",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-new-window.c,v 1.104 2026/09/03 21:04:11 nicm Exp $ */
|
/* $OpenBSD: cmd-new-window.c,v 1.105 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_new_window_entry = {
|
const struct cmd_entry cmd_new_window_entry = {
|
||||||
.name = "new-window",
|
.name = "new-window",
|
||||||
|
.description = "Create a window.",
|
||||||
.alias = "neww",
|
.alias = "neww",
|
||||||
|
|
||||||
.args = { "abc:de:EF:kn:PSt:", 0, -1, NULL },
|
.args = { "abc:de:EF:kn:PSt:", 0, -1, NULL },
|
||||||
|
|||||||
334
cmd-parse.y
334
cmd-parse.y
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-parse.y,v 1.60 2026/09/08 10:20:08 nicm Exp $ */
|
/* $OpenBSD: cmd-parse.y,v 1.63 2026/10/08 06:44:16 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -41,6 +41,7 @@ static char *yylex_format(void);
|
|||||||
|
|
||||||
struct cmd_parse_scope {
|
struct cmd_parse_scope {
|
||||||
int flag;
|
int flag;
|
||||||
|
int taken;
|
||||||
TAILQ_ENTRY (cmd_parse_scope) entry;
|
TAILQ_ENTRY (cmd_parse_scope) entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +69,22 @@ struct cmd_parse_command {
|
|||||||
};
|
};
|
||||||
TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
|
TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
|
||||||
|
|
||||||
|
enum cmd_parse_value_type {
|
||||||
|
CMD_PARSE_VALUE_STRING,
|
||||||
|
CMD_PARSE_VALUE_ARGUMENT,
|
||||||
|
CMD_PARSE_VALUE_ARGUMENTS,
|
||||||
|
CMD_PARSE_VALUE_COMMAND,
|
||||||
|
CMD_PARSE_VALUE_COMMANDS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_parse_owned_value {
|
||||||
|
enum cmd_parse_value_type type;
|
||||||
|
void *value;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(cmd_parse_owned_value) entry;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(cmd_parse_owned_values, cmd_parse_owned_value);
|
||||||
|
|
||||||
struct cmd_parse_state {
|
struct cmd_parse_state {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
@@ -86,10 +103,16 @@ struct cmd_parse_state {
|
|||||||
|
|
||||||
struct cmd_parse_scope *scope;
|
struct cmd_parse_scope *scope;
|
||||||
TAILQ_HEAD(, cmd_parse_scope) stack;
|
TAILQ_HEAD(, cmd_parse_scope) stack;
|
||||||
|
|
||||||
|
struct cmd_parse_owned_values values;
|
||||||
};
|
};
|
||||||
static struct cmd_parse_state parse_state;
|
static struct cmd_parse_state parse_state;
|
||||||
|
|
||||||
static char *cmd_parse_get_error(const char *, u_int, const char *);
|
static char *cmd_parse_get_error(const char *, u_int, const char *);
|
||||||
|
static void *cmd_parse_own(enum cmd_parse_value_type, void *);
|
||||||
|
static struct cmd_parse_commands *cmd_parse_own_commands(void);
|
||||||
|
static void *cmd_parse_take(void *);
|
||||||
|
static void cmd_parse_drop(void *);
|
||||||
static void cmd_parse_free_command(struct cmd_parse_command *);
|
static void cmd_parse_free_command(struct cmd_parse_command *);
|
||||||
static struct cmd_parse_commands *cmd_parse_new_commands(void);
|
static struct cmd_parse_commands *cmd_parse_new_commands(void);
|
||||||
static void cmd_parse_free_commands(struct cmd_parse_commands *);
|
static void cmd_parse_free_commands(struct cmd_parse_commands *);
|
||||||
@@ -138,7 +161,7 @@ lines : /* empty */
|
|||||||
{
|
{
|
||||||
struct cmd_parse_state *ps = &parse_state;
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
|
||||||
ps->commands = $1;
|
ps->commands = cmd_parse_take($1);
|
||||||
}
|
}
|
||||||
|
|
||||||
statements : statement '\n'
|
statements : statement '\n'
|
||||||
@@ -149,18 +172,16 @@ statements : statement '\n'
|
|||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
TAILQ_CONCAT($$, $2, entry);
|
TAILQ_CONCAT($$, $2, entry);
|
||||||
free($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement : /* empty */
|
statement : /* empty */
|
||||||
{
|
{
|
||||||
$$ = xmalloc (sizeof *$$);
|
$$ = cmd_parse_own_commands();
|
||||||
TAILQ_INIT($$);
|
|
||||||
}
|
}
|
||||||
| hidden_assignment
|
| hidden_assignment
|
||||||
{
|
{
|
||||||
$$ = xmalloc (sizeof *$$);
|
$$ = cmd_parse_own_commands();
|
||||||
TAILQ_INIT($$);
|
|
||||||
}
|
}
|
||||||
| condition
|
| condition
|
||||||
{
|
{
|
||||||
@@ -169,8 +190,8 @@ statement : /* empty */
|
|||||||
if (ps->scope == NULL || ps->scope->flag)
|
if (ps->scope == NULL || ps->scope->flag)
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
else {
|
else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($1);
|
cmd_parse_drop($1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| commands
|
| commands
|
||||||
@@ -180,8 +201,8 @@ statement : /* empty */
|
|||||||
if (ps->scope == NULL || ps->scope->flag)
|
if (ps->scope == NULL || ps->scope->flag)
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
else {
|
else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($1);
|
cmd_parse_drop($1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +224,7 @@ expanded : format
|
|||||||
struct cmd_find_state *fsp;
|
struct cmd_find_state *fsp;
|
||||||
struct cmd_find_state fs;
|
struct cmd_find_state fs;
|
||||||
int flags = FORMAT_NOJOBS;
|
int flags = FORMAT_NOJOBS;
|
||||||
|
char *expanded;
|
||||||
|
|
||||||
if (cmd_find_valid_state(&pi->fs))
|
if (cmd_find_valid_state(&pi->fs))
|
||||||
fsp = &pi->fs;
|
fsp = &pi->fs;
|
||||||
@@ -213,9 +235,10 @@ expanded : format
|
|||||||
ft = format_create(c, pi->item, FORMAT_NONE, flags);
|
ft = format_create(c, pi->item, FORMAT_NONE, flags);
|
||||||
format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
|
format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
|
||||||
|
|
||||||
$$ = format_expand(ft, $1);
|
expanded = format_expand(ft, $1);
|
||||||
|
$$ = cmd_parse_own(CMD_PARSE_VALUE_STRING, expanded);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
free($1);
|
cmd_parse_drop($1);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional_assignment : /* empty */
|
optional_assignment : /* empty */
|
||||||
@@ -240,7 +263,7 @@ assignment : EQUALS
|
|||||||
}
|
}
|
||||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||||
environ_put(global_environ, $1, 0);
|
environ_put(global_environ, $1, 0);
|
||||||
free($1);
|
cmd_parse_drop($1);
|
||||||
}
|
}
|
||||||
|
|
||||||
hidden_assignment : HIDDEN EQUALS
|
hidden_assignment : HIDDEN EQUALS
|
||||||
@@ -262,7 +285,7 @@ hidden_assignment : HIDDEN EQUALS
|
|||||||
}
|
}
|
||||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||||
environ_put(global_environ, $2, ENVIRON_HIDDEN);
|
environ_put(global_environ, $2, ENVIRON_HIDDEN);
|
||||||
free($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if_open : IF expanded
|
if_open : IF expanded
|
||||||
@@ -272,7 +295,8 @@ if_open : IF expanded
|
|||||||
|
|
||||||
scope = xmalloc(sizeof *scope);
|
scope = xmalloc(sizeof *scope);
|
||||||
$$ = scope->flag = format_true($2);
|
$$ = scope->flag = format_true($2);
|
||||||
free($2);
|
scope->taken = scope->flag;
|
||||||
|
cmd_parse_drop($2);
|
||||||
|
|
||||||
if (ps->scope != NULL)
|
if (ps->scope != NULL)
|
||||||
TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
|
TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
|
||||||
@@ -285,7 +309,8 @@ if_else : ELSE
|
|||||||
struct cmd_parse_scope *scope;
|
struct cmd_parse_scope *scope;
|
||||||
|
|
||||||
scope = xmalloc(sizeof *scope);
|
scope = xmalloc(sizeof *scope);
|
||||||
scope->flag = !ps->scope->flag;
|
scope->flag = !ps->scope->taken;
|
||||||
|
scope->taken = 1;
|
||||||
|
|
||||||
free(ps->scope);
|
free(ps->scope);
|
||||||
ps->scope = scope;
|
ps->scope = scope;
|
||||||
@@ -297,8 +322,10 @@ if_elif : ELIF expanded
|
|||||||
struct cmd_parse_scope *scope;
|
struct cmd_parse_scope *scope;
|
||||||
|
|
||||||
scope = xmalloc(sizeof *scope);
|
scope = xmalloc(sizeof *scope);
|
||||||
$$ = scope->flag = format_true($2);
|
scope->flag = !ps->scope->taken && format_true($2);
|
||||||
free($2);
|
scope->taken = ps->scope->taken || scope->flag;
|
||||||
|
$$ = scope->flag;
|
||||||
|
cmd_parse_drop($2);
|
||||||
|
|
||||||
free(ps->scope);
|
free(ps->scope);
|
||||||
ps->scope = scope;
|
ps->scope = scope;
|
||||||
@@ -319,48 +346,48 @@ condition : if_open '\n' statements if_close
|
|||||||
if ($1)
|
if ($1)
|
||||||
$$ = $3;
|
$$ = $3;
|
||||||
else {
|
else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open '\n' statements if_else '\n' statements if_close
|
| if_open '\n' statements if_else '\n' statements if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $3;
|
$$ = $3;
|
||||||
cmd_parse_free_commands($6);
|
cmd_parse_drop($6);
|
||||||
} else {
|
} else {
|
||||||
$$ = $6;
|
$$ = $6;
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open '\n' statements elif if_close
|
| if_open '\n' statements elif if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $3;
|
$$ = $3;
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
} else if ($4.flag) {
|
} else if ($4.flag) {
|
||||||
$$ = $4.commands;
|
$$ = $4.commands;
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
} else {
|
} else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open '\n' statements elif if_else '\n' statements if_close
|
| if_open '\n' statements elif if_else '\n' statements if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $3;
|
$$ = $3;
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
cmd_parse_free_commands($7);
|
cmd_parse_drop($7);
|
||||||
} else if ($4.flag) {
|
} else if ($4.flag) {
|
||||||
$$ = $4.commands;
|
$$ = $4.commands;
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
cmd_parse_free_commands($7);
|
cmd_parse_drop($7);
|
||||||
} else {
|
} else {
|
||||||
$$ = $7;
|
$$ = $7;
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,8 +398,8 @@ elif : if_elif '\n' statements
|
|||||||
$$.commands = $3;
|
$$.commands = $3;
|
||||||
} else {
|
} else {
|
||||||
$$.flag = 0;
|
$$.flag = 0;
|
||||||
$$.commands = cmd_parse_new_commands();
|
$$.commands = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_elif '\n' statements elif
|
| if_elif '\n' statements elif
|
||||||
@@ -380,16 +407,16 @@ elif : if_elif '\n' statements
|
|||||||
if ($1) {
|
if ($1) {
|
||||||
$$.flag = 1;
|
$$.flag = 1;
|
||||||
$$.commands = $3;
|
$$.commands = $3;
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
} else if ($4.flag) {
|
} else if ($4.flag) {
|
||||||
$$.flag = 1;
|
$$.flag = 1;
|
||||||
$$.commands = $4.commands;
|
$$.commands = $4.commands;
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
} else {
|
} else {
|
||||||
$$.flag = 0;
|
$$.flag = 0;
|
||||||
$$.commands = cmd_parse_new_commands();
|
$$.commands = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($3);
|
cmd_parse_drop($3);
|
||||||
cmd_parse_free_commands($4.commands);
|
cmd_parse_drop($4.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,12 +424,13 @@ commands : command
|
|||||||
{
|
{
|
||||||
struct cmd_parse_state *ps = &parse_state;
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
if (!TAILQ_EMPTY(&$1->arguments) &&
|
if (!TAILQ_EMPTY(&$1->arguments) &&
|
||||||
(ps->scope == NULL || ps->scope->flag))
|
(ps->scope == NULL || ps->scope->flag)) {
|
||||||
|
cmd_parse_take($1);
|
||||||
TAILQ_INSERT_TAIL($$, $1, entry);
|
TAILQ_INSERT_TAIL($$, $1, entry);
|
||||||
else
|
} else
|
||||||
cmd_parse_free_command($1);
|
cmd_parse_drop($1);
|
||||||
}
|
}
|
||||||
| commands ';'
|
| commands ';'
|
||||||
{
|
{
|
||||||
@@ -412,21 +440,19 @@ commands : command
|
|||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
TAILQ_CONCAT($$, $3, entry);
|
TAILQ_CONCAT($$, $3, entry);
|
||||||
free($3);
|
cmd_parse_drop($3);
|
||||||
}
|
}
|
||||||
| commands ';' command
|
| commands ';' command
|
||||||
{
|
{
|
||||||
struct cmd_parse_state *ps = &parse_state;
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
|
||||||
|
$$ = $1;
|
||||||
if (!TAILQ_EMPTY(&$3->arguments) &&
|
if (!TAILQ_EMPTY(&$3->arguments) &&
|
||||||
(ps->scope == NULL || ps->scope->flag)) {
|
(ps->scope == NULL || ps->scope->flag)) {
|
||||||
$$ = $1;
|
cmd_parse_take($3);
|
||||||
TAILQ_INSERT_TAIL($$, $3, entry);
|
TAILQ_INSERT_TAIL($$, $3, entry);
|
||||||
} else {
|
} else
|
||||||
$$ = cmd_parse_new_commands();
|
cmd_parse_drop($3);
|
||||||
cmd_parse_free_commands($1);
|
|
||||||
cmd_parse_free_command($3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
| condition1
|
| condition1
|
||||||
{
|
{
|
||||||
@@ -440,6 +466,7 @@ command : assignment
|
|||||||
$$ = xcalloc(1, sizeof *$$);
|
$$ = xcalloc(1, sizeof *$$);
|
||||||
$$->line = ps->input->line;
|
$$->line = ps->input->line;
|
||||||
TAILQ_INIT(&$$->arguments);
|
TAILQ_INIT(&$$->arguments);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||||
}
|
}
|
||||||
| optional_assignment TOKEN
|
| optional_assignment TOKEN
|
||||||
{
|
{
|
||||||
@@ -452,8 +479,9 @@ command : assignment
|
|||||||
|
|
||||||
arg = xcalloc(1, sizeof *arg);
|
arg = xcalloc(1, sizeof *arg);
|
||||||
arg->type = CMD_PARSE_STRING;
|
arg->type = CMD_PARSE_STRING;
|
||||||
arg->string = $2;
|
arg->string = cmd_parse_take($2);
|
||||||
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||||
}
|
}
|
||||||
| optional_assignment TOKEN arguments
|
| optional_assignment TOKEN arguments
|
||||||
{
|
{
|
||||||
@@ -465,12 +493,13 @@ command : assignment
|
|||||||
TAILQ_INIT(&$$->arguments);
|
TAILQ_INIT(&$$->arguments);
|
||||||
|
|
||||||
TAILQ_CONCAT(&$$->arguments, $3, entry);
|
TAILQ_CONCAT(&$$->arguments, $3, entry);
|
||||||
free($3);
|
cmd_parse_drop($3);
|
||||||
|
|
||||||
arg = xcalloc(1, sizeof *arg);
|
arg = xcalloc(1, sizeof *arg);
|
||||||
arg->type = CMD_PARSE_STRING;
|
arg->type = CMD_PARSE_STRING;
|
||||||
arg->string = $2;
|
arg->string = cmd_parse_take($2);
|
||||||
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||||
}
|
}
|
||||||
|
|
||||||
condition1 : if_open commands if_close
|
condition1 : if_open commands if_close
|
||||||
@@ -478,48 +507,48 @@ condition1 : if_open commands if_close
|
|||||||
if ($1)
|
if ($1)
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
else {
|
else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open commands if_else commands if_close
|
| if_open commands if_else commands if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
cmd_parse_free_commands($4);
|
cmd_parse_drop($4);
|
||||||
} else {
|
} else {
|
||||||
$$ = $4;
|
$$ = $4;
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open commands elif1 if_close
|
| if_open commands elif1 if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
} else if ($3.flag) {
|
} else if ($3.flag) {
|
||||||
$$ = $3.commands;
|
$$ = $3.commands;
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
} else {
|
} else {
|
||||||
$$ = cmd_parse_new_commands();
|
$$ = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_open commands elif1 if_else commands if_close
|
| if_open commands elif1 if_else commands if_close
|
||||||
{
|
{
|
||||||
if ($1) {
|
if ($1) {
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
cmd_parse_free_commands($5);
|
cmd_parse_drop($5);
|
||||||
} else if ($3.flag) {
|
} else if ($3.flag) {
|
||||||
$$ = $3.commands;
|
$$ = $3.commands;
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
cmd_parse_free_commands($5);
|
cmd_parse_drop($5);
|
||||||
} else {
|
} else {
|
||||||
$$ = $5;
|
$$ = $5;
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,8 +559,8 @@ elif1 : if_elif commands
|
|||||||
$$.commands = $2;
|
$$.commands = $2;
|
||||||
} else {
|
} else {
|
||||||
$$.flag = 0;
|
$$.flag = 0;
|
||||||
$$.commands = cmd_parse_new_commands();
|
$$.commands = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| if_elif commands elif1
|
| if_elif commands elif1
|
||||||
@@ -539,16 +568,16 @@ elif1 : if_elif commands
|
|||||||
if ($1) {
|
if ($1) {
|
||||||
$$.flag = 1;
|
$$.flag = 1;
|
||||||
$$.commands = $2;
|
$$.commands = $2;
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
} else if ($3.flag) {
|
} else if ($3.flag) {
|
||||||
$$.flag = 1;
|
$$.flag = 1;
|
||||||
$$.commands = $3.commands;
|
$$.commands = $3.commands;
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
} else {
|
} else {
|
||||||
$$.flag = 0;
|
$$.flag = 0;
|
||||||
$$.commands = cmd_parse_new_commands();
|
$$.commands = cmd_parse_own_commands();
|
||||||
cmd_parse_free_commands($2);
|
cmd_parse_drop($2);
|
||||||
cmd_parse_free_commands($3.commands);
|
cmd_parse_drop($3.commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,10 +586,13 @@ arguments : argument
|
|||||||
$$ = xcalloc(1, sizeof *$$);
|
$$ = xcalloc(1, sizeof *$$);
|
||||||
TAILQ_INIT($$);
|
TAILQ_INIT($$);
|
||||||
|
|
||||||
|
cmd_parse_take($1);
|
||||||
TAILQ_INSERT_HEAD($$, $1, entry);
|
TAILQ_INSERT_HEAD($$, $1, entry);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENTS, $$);
|
||||||
}
|
}
|
||||||
| argument arguments
|
| argument arguments
|
||||||
{
|
{
|
||||||
|
cmd_parse_take($1);
|
||||||
TAILQ_INSERT_HEAD($2, $1, entry);
|
TAILQ_INSERT_HEAD($2, $1, entry);
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
}
|
}
|
||||||
@@ -569,19 +601,22 @@ argument : TOKEN
|
|||||||
{
|
{
|
||||||
$$ = xcalloc(1, sizeof *$$);
|
$$ = xcalloc(1, sizeof *$$);
|
||||||
$$->type = CMD_PARSE_STRING;
|
$$->type = CMD_PARSE_STRING;
|
||||||
$$->string = $1;
|
$$->string = cmd_parse_take($1);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||||
}
|
}
|
||||||
| EQUALS
|
| EQUALS
|
||||||
{
|
{
|
||||||
$$ = xcalloc(1, sizeof *$$);
|
$$ = xcalloc(1, sizeof *$$);
|
||||||
$$->type = CMD_PARSE_STRING;
|
$$->type = CMD_PARSE_STRING;
|
||||||
$$->string = $1;
|
$$->string = cmd_parse_take($1);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||||
}
|
}
|
||||||
| '{' argument_statements
|
| '{' argument_statements
|
||||||
{
|
{
|
||||||
$$ = xcalloc(1, sizeof *$$);
|
$$ = xcalloc(1, sizeof *$$);
|
||||||
$$->type = CMD_PARSE_COMMANDS;
|
$$->type = CMD_PARSE_COMMANDS;
|
||||||
$$->commands = $2;
|
$$->commands = cmd_parse_take($2);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||||
}
|
}
|
||||||
|
|
||||||
argument_statements : statement '}'
|
argument_statements : statement '}'
|
||||||
@@ -592,7 +627,7 @@ argument_statements : statement '}'
|
|||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
TAILQ_CONCAT($$, $2, entry);
|
TAILQ_CONCAT($$, $2, entry);
|
||||||
free($2);
|
cmd_parse_drop($2);
|
||||||
}
|
}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@@ -681,22 +716,116 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds)
|
|||||||
free(cmds);
|
free(cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
cmd_parse_own(enum cmd_parse_value_type type, void *value)
|
||||||
|
{
|
||||||
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
struct cmd_parse_owned_value *v;
|
||||||
|
|
||||||
|
v = xmalloc(sizeof *v);
|
||||||
|
v->type = type;
|
||||||
|
v->value = value;
|
||||||
|
TAILQ_INSERT_TAIL(&ps->values, v, entry);
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cmd_parse_commands *
|
||||||
|
cmd_parse_own_commands(void)
|
||||||
|
{
|
||||||
|
struct cmd_parse_commands *cmds;
|
||||||
|
|
||||||
|
cmds = cmd_parse_new_commands();
|
||||||
|
return (cmd_parse_own(CMD_PARSE_VALUE_COMMANDS, cmds));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cmd_parse_owned_value *
|
||||||
|
cmd_parse_find_value(void *value)
|
||||||
|
{
|
||||||
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
struct cmd_parse_owned_value *v;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_REVERSE(v, &ps->values, cmd_parse_owned_values, entry) {
|
||||||
|
if (v->value == value)
|
||||||
|
return (v);
|
||||||
|
}
|
||||||
|
fatalx("parser value not owned");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
cmd_parse_take(void *value)
|
||||||
|
{
|
||||||
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
struct cmd_parse_owned_value *v;
|
||||||
|
|
||||||
|
v = cmd_parse_find_value(value);
|
||||||
|
TAILQ_REMOVE(&ps->values, v, entry);
|
||||||
|
free(v);
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cmd_parse_free_value(enum cmd_parse_value_type type, void *value)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case CMD_PARSE_VALUE_STRING:
|
||||||
|
free(value);
|
||||||
|
break;
|
||||||
|
case CMD_PARSE_VALUE_ARGUMENT:
|
||||||
|
cmd_parse_free_argument(value);
|
||||||
|
break;
|
||||||
|
case CMD_PARSE_VALUE_ARGUMENTS:
|
||||||
|
cmd_parse_free_arguments(value);
|
||||||
|
free(value);
|
||||||
|
break;
|
||||||
|
case CMD_PARSE_VALUE_COMMAND:
|
||||||
|
cmd_parse_free_command(value);
|
||||||
|
break;
|
||||||
|
case CMD_PARSE_VALUE_COMMANDS:
|
||||||
|
cmd_parse_free_commands(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cmd_parse_drop(void *value)
|
||||||
|
{
|
||||||
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
|
struct cmd_parse_owned_value *v;
|
||||||
|
|
||||||
|
v = cmd_parse_find_value(value);
|
||||||
|
TAILQ_REMOVE(&ps->values, v, entry);
|
||||||
|
cmd_parse_free_value(v->type, v->value);
|
||||||
|
free(v);
|
||||||
|
}
|
||||||
|
|
||||||
static struct cmd_parse_commands *
|
static struct cmd_parse_commands *
|
||||||
cmd_parse_run_parser(char **cause)
|
cmd_parse_run_parser(char **cause)
|
||||||
{
|
{
|
||||||
struct cmd_parse_state *ps = &parse_state;
|
struct cmd_parse_state *ps = &parse_state;
|
||||||
struct cmd_parse_scope *scope, *scope1;
|
struct cmd_parse_scope *scope, *scope1;
|
||||||
int retval;
|
struct cmd_parse_owned_value *v, *v1;
|
||||||
|
int retval;
|
||||||
|
|
||||||
ps->commands = NULL;
|
ps->commands = NULL;
|
||||||
TAILQ_INIT(&ps->stack);
|
TAILQ_INIT(&ps->stack);
|
||||||
|
TAILQ_INIT(&ps->values);
|
||||||
|
|
||||||
retval = yyparse();
|
retval = yyparse();
|
||||||
TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
|
TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
|
||||||
TAILQ_REMOVE(&ps->stack, scope, entry);
|
TAILQ_REMOVE(&ps->stack, scope, entry);
|
||||||
free(scope);
|
free(scope);
|
||||||
}
|
}
|
||||||
|
free(ps->scope);
|
||||||
|
ps->scope = NULL;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(v, &ps->values, entry, v1) {
|
||||||
|
TAILQ_REMOVE(&ps->values, v, entry);
|
||||||
|
cmd_parse_free_value(v->type, v->value);
|
||||||
|
free(v);
|
||||||
|
}
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
|
if (ps->commands != NULL)
|
||||||
|
cmd_parse_free_commands(ps->commands);
|
||||||
*cause = ps->error;
|
*cause = ps->error;
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -1327,9 +1456,10 @@ yylex(void)
|
|||||||
*/
|
*/
|
||||||
next = yylex_getc();
|
next = yylex_getc();
|
||||||
if (condition && next == '{') {
|
if (condition && next == '{') {
|
||||||
yylval.token = yylex_format();
|
token = yylval.token = yylex_format();
|
||||||
if (yylval.token == NULL)
|
if (token == NULL)
|
||||||
return (ERROR);
|
return (ERROR);
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||||
return (FORMAT);
|
return (FORMAT);
|
||||||
}
|
}
|
||||||
while (next != '\n' && next != EOF)
|
while (next != '\n' && next != EOF)
|
||||||
@@ -1346,35 +1476,37 @@ yylex(void)
|
|||||||
* % is a condition unless it is all % or all numbers,
|
* % is a condition unless it is all % or all numbers,
|
||||||
* then it is a token.
|
* then it is a token.
|
||||||
*/
|
*/
|
||||||
yylval.token = yylex_get_word('%');
|
token = yylval.token = yylex_get_word('%');
|
||||||
for (cp = yylval.token; *cp != '\0'; cp++) {
|
for (cp = token; *cp != '\0'; cp++) {
|
||||||
if (*cp != '%' && !isdigit((u_char)*cp))
|
if (*cp != '%' && !isdigit((u_char)*cp))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (*cp == '\0')
|
if (*cp == '\0') {
|
||||||
|
cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||||
return (TOKEN);
|
return (TOKEN);
|
||||||
|
}
|
||||||
ps->condition = 1;
|
ps->condition = 1;
|
||||||
if (strcmp(yylval.token, "%hidden") == 0) {
|
if (strcmp(token, "%hidden") == 0) {
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (HIDDEN);
|
return (HIDDEN);
|
||||||
}
|
}
|
||||||
if (strcmp(yylval.token, "%if") == 0) {
|
if (strcmp(token, "%if") == 0) {
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (IF);
|
return (IF);
|
||||||
}
|
}
|
||||||
if (strcmp(yylval.token, "%else") == 0) {
|
if (strcmp(token, "%else") == 0) {
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (ELSE);
|
return (ELSE);
|
||||||
}
|
}
|
||||||
if (strcmp(yylval.token, "%elif") == 0) {
|
if (strcmp(token, "%elif") == 0) {
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (ELIF);
|
return (ELIF);
|
||||||
}
|
}
|
||||||
if (strcmp(yylval.token, "%endif") == 0) {
|
if (strcmp(token, "%endif") == 0) {
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (ENDIF);
|
return (ENDIF);
|
||||||
}
|
}
|
||||||
free(yylval.token);
|
free(token);
|
||||||
return (ERROR);
|
return (ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1384,7 +1516,7 @@ yylex(void)
|
|||||||
token = yylex_token(ch);
|
token = yylex_token(ch);
|
||||||
if (token == NULL)
|
if (token == NULL)
|
||||||
return (ERROR);
|
return (ERROR);
|
||||||
yylval.token = token;
|
yylval.token = cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||||
|
|
||||||
if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
|
if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
|
||||||
for (cp = token + 1; *cp != '='; cp++) {
|
for (cp = token + 1; *cp != '='; cp++) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-paste-buffer.c,v 1.44 2026/03/04 07:19:32 tb Exp $ */
|
/* $OpenBSD: cmd-paste-buffer.c,v 1.45 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_paste_buffer_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_paste_buffer_entry = {
|
const struct cmd_entry cmd_paste_buffer_entry = {
|
||||||
.name = "paste-buffer",
|
.name = "paste-buffer",
|
||||||
|
.description = "Paste a buffer into a pane.",
|
||||||
.alias = "pasteb",
|
.alias = "pasteb",
|
||||||
|
|
||||||
.args = { "db:prSs:t:", 0, 0, NULL },
|
.args = { "db:prSs:t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-pipe-pane.c,v 1.64 2026/07/27 14:25:46 nicm Exp $ */
|
/* $OpenBSD: cmd-pipe-pane.c,v 1.65 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -41,6 +41,7 @@ static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_pipe_pane_entry = {
|
const struct cmd_entry cmd_pipe_pane_entry = {
|
||||||
.name = "pipe-pane",
|
.name = "pipe-pane",
|
||||||
|
.description = "Pipe pane input or output to a command.",
|
||||||
.alias = "pipep",
|
.alias = "pipep",
|
||||||
|
|
||||||
.args = { "IOot:", 0, 1, NULL },
|
.args = { "IOot:", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-refresh-client.c,v 1.55 2026/07/17 08:37:29 nicm Exp $ */
|
/* $OpenBSD: cmd-refresh-client.c,v 1.56 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_refresh_client_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_refresh_client_entry = {
|
const struct cmd_entry cmd_refresh_client_entry = {
|
||||||
.name = "refresh-client",
|
.name = "refresh-client",
|
||||||
|
.description = "Refresh or resize a client.",
|
||||||
.alias = "refresh",
|
.alias = "refresh",
|
||||||
|
|
||||||
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
|
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-rename-session.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */
|
/* $OpenBSD: cmd-rename-session.c,v 1.40 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_rename_session_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_rename_session_entry = {
|
const struct cmd_entry cmd_rename_session_entry = {
|
||||||
.name = "rename-session",
|
.name = "rename-session",
|
||||||
|
.description = "Rename a session.",
|
||||||
.alias = "rename",
|
.alias = "rename",
|
||||||
|
|
||||||
.args = { "t:", 1, 1, NULL },
|
.args = { "t:", 1, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-rename-window.c,v 1.30 2026/06/29 18:17:28 nicm Exp $ */
|
/* $OpenBSD: cmd-rename-window.c,v 1.31 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_rename_window_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_rename_window_entry = {
|
const struct cmd_entry cmd_rename_window_entry = {
|
||||||
.name = "rename-window",
|
.name = "rename-window",
|
||||||
|
.description = "Rename a window.",
|
||||||
.alias = "renamew",
|
.alias = "renamew",
|
||||||
|
|
||||||
.args = { "t:", 1, 1, NULL },
|
.args = { "t:", 1, 1, NULL },
|
||||||
|
|||||||
@@ -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.71 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -39,6 +39,7 @@ static void cmd_resize_pane_mouse_resize_tiled(struct client *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_resize_pane_entry = {
|
const struct cmd_entry cmd_resize_pane_entry = {
|
||||||
.name = "resize-pane",
|
.name = "resize-pane",
|
||||||
|
.description = "Resize or zoom a pane.",
|
||||||
.alias = "resizep",
|
.alias = "resizep",
|
||||||
|
|
||||||
.args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL },
|
.args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL },
|
||||||
@@ -237,8 +238,8 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
|||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct layout_cell *lc;
|
struct layout_cell *lc;
|
||||||
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
||||||
int left, right;
|
int left, right, resizes = 0;
|
||||||
int new_xoff, new_yoff, resizes = 0;
|
int new_xoff, new_yoff, old_xoff, old_yoff;
|
||||||
|
|
||||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||||
if (wp == NULL) {
|
if (wp == NULL) {
|
||||||
@@ -249,6 +250,8 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
|||||||
lc = wp->layout_cell;
|
lc = wp->layout_cell;
|
||||||
sx = wp->sx;
|
sx = wp->sx;
|
||||||
sy = wp->sy;
|
sy = wp->sy;
|
||||||
|
old_xoff = wp->xoff;
|
||||||
|
old_yoff = wp->yoff;
|
||||||
left = wp->xoff - 1;
|
left = wp->xoff - 1;
|
||||||
right = wp->xoff + sx;
|
right = wp->xoff + sx;
|
||||||
if (window_pane_scrollbar_reserve(wp) &&
|
if (window_pane_scrollbar_reserve(wp) &&
|
||||||
@@ -348,7 +351,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
|||||||
}
|
}
|
||||||
if (resizes != 0) {
|
if (resizes != 0) {
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
server_redraw_window(w);
|
window_redraw_floating_pane(wp, old_xoff, old_yoff, sx, sy);
|
||||||
server_redraw_window_borders(w);
|
server_redraw_window_borders(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-resize-window.c,v 1.10 2023/06/30 13:19:32 nicm Exp $ */
|
/* $OpenBSD: cmd-resize-window.c,v 1.11 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_resize_window_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_resize_window_entry = {
|
const struct cmd_entry cmd_resize_window_entry = {
|
||||||
.name = "resize-window",
|
.name = "resize-window",
|
||||||
|
.description = "Resize a window.",
|
||||||
.alias = "resizew",
|
.alias = "resizew",
|
||||||
|
|
||||||
.args = { "aADLRt:Ux:y:", 0, 1, NULL },
|
.args = { "aADLRt:Ux:y:", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-respawn-pane.c,v 1.40 2026/07/03 16:09:49 nicm Exp $ */
|
/* $OpenBSD: cmd-respawn-pane.c,v 1.41 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_respawn_pane_entry = {
|
const struct cmd_entry cmd_respawn_pane_entry = {
|
||||||
.name = "respawn-pane",
|
.name = "respawn-pane",
|
||||||
|
.description = "Restart a pane's command.",
|
||||||
.alias = "respawnp",
|
.alias = "respawnp",
|
||||||
|
|
||||||
.args = { "c:e:Ekt:", 0, -1, NULL },
|
.args = { "c:e:Ekt:", 0, -1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-respawn-window.c,v 1.51 2026/07/03 16:09:49 nicm Exp $ */
|
/* $OpenBSD: cmd-respawn-window.c,v 1.52 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_respawn_window_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_respawn_window_entry = {
|
const struct cmd_entry cmd_respawn_window_entry = {
|
||||||
.name = "respawn-window",
|
.name = "respawn-window",
|
||||||
|
.description = "Restart every pane in a window.",
|
||||||
.alias = "respawnw",
|
.alias = "respawnw",
|
||||||
|
|
||||||
.args = { "c:e:Ekt:", 0, -1, NULL },
|
.args = { "c:e:Ekt:", 0, -1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-rotate-window.c,v 1.34 2026/06/22 08:47:45 nicm Exp $ */
|
/* $OpenBSD: cmd-rotate-window.c,v 1.35 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -29,6 +29,7 @@ static enum cmd_retval cmd_rotate_window_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_rotate_window_entry = {
|
const struct cmd_entry cmd_rotate_window_entry = {
|
||||||
.name = "rotate-window",
|
.name = "rotate-window",
|
||||||
|
.description = "Rotate panes in a window.",
|
||||||
.alias = "rotatew",
|
.alias = "rotatew",
|
||||||
|
|
||||||
.args = { "Dt:UZ", 0, 0, NULL },
|
.args = { "Dt:UZ", 0, 0, NULL },
|
||||||
|
|||||||
@@ -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.96 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -42,6 +42,7 @@ static void cmd_run_shell_print(struct job *, const char *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_run_shell_entry = {
|
const struct cmd_entry cmd_run_shell_entry = {
|
||||||
.name = "run-shell",
|
.name = "run-shell",
|
||||||
|
.description = "Run a shell command.",
|
||||||
.alias = "run",
|
.alias = "run",
|
||||||
|
|
||||||
.args = { "bd:Ct:Es:c:", 0, -1, cmd_run_shell_args_parse },
|
.args = { "bd:Ct:Es:c:", 0, -1, cmd_run_shell_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-save-buffer.c,v 1.57 2025/10/28 07:32:26 nicm Exp $ */
|
/* $OpenBSD: cmd-save-buffer.c,v 1.58 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -35,6 +35,7 @@ static enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_save_buffer_entry = {
|
const struct cmd_entry cmd_save_buffer_entry = {
|
||||||
.name = "save-buffer",
|
.name = "save-buffer",
|
||||||
|
.description = "Save a paste buffer to a file.",
|
||||||
.alias = "saveb",
|
.alias = "saveb",
|
||||||
|
|
||||||
.args = { "ab:", 1, 1, NULL },
|
.args = { "ab:", 1, 1, NULL },
|
||||||
@@ -46,6 +47,7 @@ const struct cmd_entry cmd_save_buffer_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_buffer_entry = {
|
const struct cmd_entry cmd_show_buffer_entry = {
|
||||||
.name = "show-buffer",
|
.name = "show-buffer",
|
||||||
|
.description = "Print a paste buffer.",
|
||||||
.alias = "showb",
|
.alias = "showb",
|
||||||
|
|
||||||
.args = { "b:", 0, 0, NULL },
|
.args = { "b:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-layout.c,v 1.45 2026/09/09 07:03:39 nicm Exp $ */
|
/* $OpenBSD: cmd-select-layout.c,v 1.46 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_select_layout_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_select_layout_entry = {
|
const struct cmd_entry cmd_select_layout_entry = {
|
||||||
.name = "select-layout",
|
.name = "select-layout",
|
||||||
|
.description = "Choose or apply a layout.",
|
||||||
.alias = "selectl",
|
.alias = "selectl",
|
||||||
|
|
||||||
.args = { "Enopt:", 0, 1, NULL },
|
.args = { "Enopt:", 0, 1, NULL },
|
||||||
@@ -44,6 +45,7 @@ const struct cmd_entry cmd_select_layout_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_next_layout_entry = {
|
const struct cmd_entry cmd_next_layout_entry = {
|
||||||
.name = "next-layout",
|
.name = "next-layout",
|
||||||
|
.description = "Select the next layout.",
|
||||||
.alias = "nextl",
|
.alias = "nextl",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
@@ -57,6 +59,7 @@ const struct cmd_entry cmd_next_layout_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_previous_layout_entry = {
|
const struct cmd_entry cmd_previous_layout_entry = {
|
||||||
.name = "previous-layout",
|
.name = "previous-layout",
|
||||||
|
.description = "Select the previous layout.",
|
||||||
.alias = "prevl",
|
.alias = "prevl",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-pane.c,v 1.78 2026/08/20 09:19:24 nicm Exp $ */
|
/* $OpenBSD: cmd-select-pane.c,v 1.79 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_select_pane_entry = {
|
const struct cmd_entry cmd_select_pane_entry = {
|
||||||
.name = "select-pane",
|
.name = "select-pane",
|
||||||
|
.description = "Select or mark a pane.",
|
||||||
.alias = "selectp",
|
.alias = "selectp",
|
||||||
|
|
||||||
.args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */
|
.args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */
|
||||||
@@ -44,6 +45,7 @@ const struct cmd_entry cmd_select_pane_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_last_pane_entry = {
|
const struct cmd_entry cmd_last_pane_entry = {
|
||||||
.name = "last-pane",
|
.name = "last-pane",
|
||||||
|
.description = "Select the previously active pane.",
|
||||||
.alias = "lastp",
|
.alias = "lastp",
|
||||||
|
|
||||||
.args = { "det:Z", 0, 0, NULL },
|
.args = { "det:Z", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-select-window.c,v 1.30 2021/08/21 10:22:39 nicm Exp $ */
|
/* $OpenBSD: cmd-select-window.c,v 1.31 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_select_window_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_select_window_entry = {
|
const struct cmd_entry cmd_select_window_entry = {
|
||||||
.name = "select-window",
|
.name = "select-window",
|
||||||
|
.description = "Select a window.",
|
||||||
.alias = "selectw",
|
.alias = "selectw",
|
||||||
|
|
||||||
.args = { "lnpTt:", 0, 0, NULL },
|
.args = { "lnpTt:", 0, 0, NULL },
|
||||||
@@ -44,6 +45,7 @@ const struct cmd_entry cmd_select_window_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_next_window_entry = {
|
const struct cmd_entry cmd_next_window_entry = {
|
||||||
.name = "next-window",
|
.name = "next-window",
|
||||||
|
.description = "Select the next window.",
|
||||||
.alias = "next",
|
.alias = "next",
|
||||||
|
|
||||||
.args = { "at:", 0, 0, NULL },
|
.args = { "at:", 0, 0, NULL },
|
||||||
@@ -57,6 +59,7 @@ const struct cmd_entry cmd_next_window_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_previous_window_entry = {
|
const struct cmd_entry cmd_previous_window_entry = {
|
||||||
.name = "previous-window",
|
.name = "previous-window",
|
||||||
|
.description = "Select the previous window.",
|
||||||
.alias = "prev",
|
.alias = "prev",
|
||||||
|
|
||||||
.args = { "at:", 0, 0, NULL },
|
.args = { "at:", 0, 0, NULL },
|
||||||
@@ -70,6 +73,7 @@ const struct cmd_entry cmd_previous_window_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_last_window_entry = {
|
const struct cmd_entry cmd_last_window_entry = {
|
||||||
.name = "last-window",
|
.name = "last-window",
|
||||||
|
.description = "Select the previously active window.",
|
||||||
.alias = "last",
|
.alias = "last",
|
||||||
|
|
||||||
.args = { "t:", 0, 0, NULL },
|
.args = { "t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-send-keys.c,v 1.81 2026/06/11 19:13:34 nicm Exp $ */
|
/* $OpenBSD: cmd-send-keys.c,v 1.82 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_send_keys_entry = {
|
const struct cmd_entry cmd_send_keys_entry = {
|
||||||
.name = "send-keys",
|
.name = "send-keys",
|
||||||
|
.description = "Send keys to a pane or client.",
|
||||||
.alias = "send",
|
.alias = "send",
|
||||||
|
|
||||||
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
|
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
|
||||||
@@ -46,6 +47,7 @@ const struct cmd_entry cmd_send_keys_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_send_prefix_entry = {
|
const struct cmd_entry cmd_send_prefix_entry = {
|
||||||
.name = "send-prefix",
|
.name = "send-prefix",
|
||||||
|
.description = "Send the prefix key to a pane.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "2t:", 0, 0, NULL },
|
.args = { "2t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-server-access.c,v 1.6 2026/06/09 12:58:40 nicm Exp $ */
|
/* $OpenBSD: cmd-server-access.c,v 1.7 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>
|
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static enum cmd_retval cmd_server_access_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_server_access_entry = {
|
const struct cmd_entry cmd_server_access_entry = {
|
||||||
.name = "server-access",
|
.name = "server-access",
|
||||||
|
.description = "Manage access to the tmux server.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "adglrw", 0, 1, NULL },
|
.args = { "adglrw", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-set-buffer.c,v 1.38 2026/08/24 07:05:23 nicm Exp $ */
|
/* $OpenBSD: cmd-set-buffer.c,v 1.39 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -31,6 +31,7 @@ static enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_set_buffer_entry = {
|
const struct cmd_entry cmd_set_buffer_entry = {
|
||||||
.name = "set-buffer",
|
.name = "set-buffer",
|
||||||
|
.description = "Create or modify a paste buffer.",
|
||||||
.alias = "setb",
|
.alias = "setb",
|
||||||
|
|
||||||
.args = { "ab:t:n:w", 0, 1, NULL },
|
.args = { "ab:t:n:w", 0, 1, NULL },
|
||||||
@@ -43,6 +44,7 @@ const struct cmd_entry cmd_set_buffer_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_delete_buffer_entry = {
|
const struct cmd_entry cmd_delete_buffer_entry = {
|
||||||
.name = "delete-buffer",
|
.name = "delete-buffer",
|
||||||
|
.description = "Delete a paste buffer.",
|
||||||
.alias = "deleteb",
|
.alias = "deleteb",
|
||||||
|
|
||||||
.args = { "b:", 0, 0, NULL },
|
.args = { "b:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-set-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
|
/* $OpenBSD: cmd-set-environment.c,v 1.30 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_set_environment_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_set_environment_entry = {
|
const struct cmd_entry cmd_set_environment_entry = {
|
||||||
.name = "set-environment",
|
.name = "set-environment",
|
||||||
|
.description = "Set or remove an environment variable.",
|
||||||
.alias = "setenv",
|
.alias = "setenv",
|
||||||
|
|
||||||
.args = { "Fhgrt:u", 1, 2, NULL },
|
.args = { "Fhgrt:u", 1, 2, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-set-option.c,v 1.146 2026/07/10 15:20:06 nicm Exp $ */
|
/* $OpenBSD: cmd-set-option.c,v 1.147 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -38,6 +38,7 @@ static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_set_option_entry = {
|
const struct cmd_entry cmd_set_option_entry = {
|
||||||
.name = "set-option",
|
.name = "set-option",
|
||||||
|
.description = "Set a server, session, or pane option.",
|
||||||
.alias = "set",
|
.alias = "set",
|
||||||
|
|
||||||
.args = { "aFgopqst:uUw", 1, 2, cmd_set_option_args_parse },
|
.args = { "aFgopqst:uUw", 1, 2, cmd_set_option_args_parse },
|
||||||
@@ -51,6 +52,7 @@ const struct cmd_entry cmd_set_option_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_set_window_option_entry = {
|
const struct cmd_entry cmd_set_window_option_entry = {
|
||||||
.name = "set-window-option",
|
.name = "set-window-option",
|
||||||
|
.description = "Set a window option.",
|
||||||
.alias = "setw",
|
.alias = "setw",
|
||||||
|
|
||||||
.args = { "aFgoqt:u", 1, 2, cmd_set_option_args_parse },
|
.args = { "aFgoqt:u", 1, 2, cmd_set_option_args_parse },
|
||||||
@@ -64,6 +66,7 @@ const struct cmd_entry cmd_set_window_option_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_set_hook_entry = {
|
const struct cmd_entry cmd_set_hook_entry = {
|
||||||
.name = "set-hook",
|
.name = "set-hook",
|
||||||
|
.description = "Set or remove a hook.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse },
|
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-show-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
|
/* $OpenBSD: cmd-show-environment.c,v 1.30 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static void cmd_show_environment_print(struct cmd *, struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_environment_entry = {
|
const struct cmd_entry cmd_show_environment_entry = {
|
||||||
.name = "show-environment",
|
.name = "show-environment",
|
||||||
|
.description = "Show environment variables.",
|
||||||
.alias = "showenv",
|
.alias = "showenv",
|
||||||
|
|
||||||
.args = { "hgst:", 0, 1, NULL },
|
.args = { "hgst:", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-show-messages.c,v 1.37 2025/11/18 08:37:54 nicm Exp $ */
|
/* $OpenBSD: cmd-show-messages.c,v 1.38 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -36,6 +36,7 @@ static enum cmd_retval cmd_show_messages_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_messages_entry = {
|
const struct cmd_entry cmd_show_messages_entry = {
|
||||||
.name = "show-messages",
|
.name = "show-messages",
|
||||||
|
.description = "Show server and client messages.",
|
||||||
.alias = "showmsgs",
|
.alias = "showmsgs",
|
||||||
|
|
||||||
.args = { "JTt:", 0, 0, NULL },
|
.args = { "JTt:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-show-options.c,v 1.76 2026/07/27 19:15:58 nicm Exp $ */
|
/* $OpenBSD: cmd-show-options.c,v 1.77 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -49,6 +49,7 @@ static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_options_entry = {
|
const struct cmd_entry cmd_show_options_entry = {
|
||||||
.name = "show-options",
|
.name = "show-options",
|
||||||
|
.description = "Show server, session, or pane options.",
|
||||||
.alias = "show",
|
.alias = "show",
|
||||||
|
|
||||||
.args = { "AgF:Hpqst:vw", 0, 1, NULL },
|
.args = { "AgF:Hpqst:vw", 0, 1, NULL },
|
||||||
@@ -62,6 +63,7 @@ const struct cmd_entry cmd_show_options_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_window_options_entry = {
|
const struct cmd_entry cmd_show_window_options_entry = {
|
||||||
.name = "show-window-options",
|
.name = "show-window-options",
|
||||||
|
.description = "Show window options.",
|
||||||
.alias = "showw",
|
.alias = "showw",
|
||||||
|
|
||||||
.args = { "F:gvt:", 0, 1, NULL },
|
.args = { "F:gvt:", 0, 1, NULL },
|
||||||
@@ -75,6 +77,7 @@ const struct cmd_entry cmd_show_window_options_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_hooks_entry = {
|
const struct cmd_entry cmd_show_hooks_entry = {
|
||||||
.name = "show-hooks",
|
.name = "show-hooks",
|
||||||
|
.description = "Show hooks.",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "BF:gpt:w", 0, 1, NULL },
|
.args = { "BF:gpt:w", 0, 1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-show-prompt-history.c,v 1.5 2026/06/25 11:39:11 nicm Exp $ */
|
/* $OpenBSD: cmd-show-prompt-history.c,v 1.6 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>
|
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>
|
||||||
@@ -29,6 +29,7 @@ static enum cmd_retval cmd_show_prompt_history_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_show_prompt_history_entry = {
|
const struct cmd_entry cmd_show_prompt_history_entry = {
|
||||||
.name = "show-prompt-history",
|
.name = "show-prompt-history",
|
||||||
|
.description = "Show interactive prompt history.",
|
||||||
.alias = "showphist",
|
.alias = "showphist",
|
||||||
|
|
||||||
.args = { "T:", 0, 0, NULL },
|
.args = { "T:", 0, 0, NULL },
|
||||||
@@ -40,6 +41,7 @@ const struct cmd_entry cmd_show_prompt_history_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_clear_prompt_history_entry = {
|
const struct cmd_entry cmd_clear_prompt_history_entry = {
|
||||||
.name = "clear-prompt-history",
|
.name = "clear-prompt-history",
|
||||||
|
.description = "Clear interactive prompt history.",
|
||||||
.alias = "clearphist",
|
.alias = "clearphist",
|
||||||
|
|
||||||
.args = { "T:", 0, 0, NULL },
|
.args = { "T:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-source-file.c,v 1.63 2026/08/18 08:05:05 nicm Exp $ */
|
/* $OpenBSD: cmd-source-file.c,v 1.64 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
|
||||||
@@ -37,6 +37,7 @@ static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_source_file_entry = {
|
const struct cmd_entry cmd_source_file_entry = {
|
||||||
.name = "source-file",
|
.name = "source-file",
|
||||||
|
.description = "Load commands from a configuration file.",
|
||||||
.alias = "source",
|
.alias = "source",
|
||||||
|
|
||||||
.args = { "t:Fnqv", 1, -1, NULL },
|
.args = { "t:Fnqv", 1, -1, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-split-window.c,v 1.151 2026/09/10 11:02:18 nicm Exp $ */
|
/* $OpenBSD: cmd-split-window.c,v 1.153 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -38,6 +38,7 @@ static void cmd_split_window_mouse_resize(struct client *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_new_pane_entry = {
|
const struct cmd_entry cmd_new_pane_entry = {
|
||||||
.name = "new-pane",
|
.name = "new-pane",
|
||||||
|
.description = "Create a floating pane.",
|
||||||
.alias = "newp",
|
.alias = "newp",
|
||||||
|
|
||||||
.args = { "AbB:Cc:Dde:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
.args = { "AbB:Cc:Dde:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||||
@@ -57,6 +58,7 @@ const struct cmd_entry cmd_new_pane_entry = {
|
|||||||
|
|
||||||
const struct cmd_entry cmd_split_window_entry = {
|
const struct cmd_entry cmd_split_window_entry = {
|
||||||
.name = "split-window",
|
.name = "split-window",
|
||||||
|
.description = "Create a new pane by splitting a window.",
|
||||||
.alias = "splitw",
|
.alias = "splitw",
|
||||||
|
|
||||||
.args = { "bB:c:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL },
|
.args = { "bB:c:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL },
|
||||||
@@ -362,6 +364,7 @@ cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
|
|||||||
enum pane_lines lines;
|
enum pane_lines lines;
|
||||||
u_int sx, sy;
|
u_int sx, sy;
|
||||||
int x, y, xoff, yoff, border;
|
int x, y, xoff, yoff, border;
|
||||||
|
int oxoff, oyoff, osx, osy;
|
||||||
|
|
||||||
if (c->tty.mouse_last_pane == -1)
|
if (c->tty.mouse_last_pane == -1)
|
||||||
return;
|
return;
|
||||||
@@ -417,8 +420,14 @@ cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
|
|||||||
if (sy < PANE_MINIMUM)
|
if (sy < PANE_MINIMUM)
|
||||||
sy = PANE_MINIMUM;
|
sy = PANE_MINIMUM;
|
||||||
|
|
||||||
|
oxoff = wp->xoff;
|
||||||
|
oyoff = wp->yoff;
|
||||||
|
osx = wp->sx;
|
||||||
|
osy = wp->sy;
|
||||||
|
|
||||||
layout_set_size(lc, sx, sy, xoff, yoff);
|
layout_set_size(lc, sx, sy, xoff, yoff);
|
||||||
layout_fix_panes(w, NULL);
|
layout_fix_panes(w, NULL);
|
||||||
server_redraw_window(w);
|
|
||||||
|
window_redraw_floating_pane(wp, oxoff, oyoff, osx, osy);
|
||||||
server_redraw_window_borders(w);
|
server_redraw_window_borders(w);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-swap-pane.c,v 1.55 2026/07/15 13:02:33 nicm Exp $ */
|
/* $OpenBSD: cmd-swap-pane.c,v 1.57 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -30,6 +30,7 @@ static enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_swap_pane_entry = {
|
const struct cmd_entry cmd_swap_pane_entry = {
|
||||||
.name = "swap-pane",
|
.name = "swap-pane",
|
||||||
|
.description = "Swap two panes.",
|
||||||
.alias = "swapp",
|
.alias = "swapp",
|
||||||
|
|
||||||
.args = { "dDs:t:UZ", 0, 0, NULL },
|
.args = { "dDs:t:UZ", 0, 0, NULL },
|
||||||
@@ -88,7 +89,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
if (args_has(args, 'D')) {
|
if (args_has(args, 'D')) {
|
||||||
if (window_pane_is_floating(dst_wp)) {
|
if (window_pane_is_floating(dst_wp)) {
|
||||||
cmdq_error(item, "cannot swap down on floating pane");
|
cmdq_error(item, "cannot swap down on floating pane");
|
||||||
return (CMD_RETURN_ERROR);
|
goto error;
|
||||||
}
|
}
|
||||||
src_w = dst_w;
|
src_w = dst_w;
|
||||||
src_wp = TAILQ_NEXT(dst_wp, entry);
|
src_wp = TAILQ_NEXT(dst_wp, entry);
|
||||||
@@ -100,7 +101,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
} else if (args_has(args, 'U')) {
|
} else if (args_has(args, 'U')) {
|
||||||
if (window_pane_is_floating(dst_wp)) {
|
if (window_pane_is_floating(dst_wp)) {
|
||||||
cmdq_error(item, "cannot swap up on floating pane");
|
cmdq_error(item, "cannot swap up on floating pane");
|
||||||
return (CMD_RETURN_ERROR);
|
goto error;
|
||||||
}
|
}
|
||||||
src_w = dst_w;
|
src_w = dst_w;
|
||||||
src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
|
src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
|
||||||
@@ -202,4 +203,9 @@ out:
|
|||||||
if (src_w != dst_w && window_pop_zoom(dst_w))
|
if (src_w != dst_w && window_pop_zoom(dst_w))
|
||||||
server_redraw_window(dst_w);
|
server_redraw_window(dst_w);
|
||||||
return (CMD_RETURN_NORMAL);
|
return (CMD_RETURN_NORMAL);
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (window_pop_zoom(dst_w))
|
||||||
|
server_redraw_window(dst_w);
|
||||||
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-swap-window.c,v 1.30 2026/07/27 08:03:01 nicm Exp $ */
|
/* $OpenBSD: cmd-swap-window.c,v 1.31 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -30,6 +30,7 @@ static enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_swap_window_entry = {
|
const struct cmd_entry cmd_swap_window_entry = {
|
||||||
.name = "swap-window",
|
.name = "swap-window",
|
||||||
|
.description = "Swap two windows.",
|
||||||
.alias = "swapw",
|
.alias = "swapw",
|
||||||
|
|
||||||
.args = { "ds:t:", 0, 0, NULL },
|
.args = { "ds:t:", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-switch-client.c,v 1.75 2026/08/20 09:19:24 nicm Exp $ */
|
/* $OpenBSD: cmd-switch-client.c,v 1.76 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -33,6 +33,7 @@ static enum cmd_retval cmd_switch_client_exec(struct cmd *,
|
|||||||
|
|
||||||
const struct cmd_entry cmd_switch_client_entry = {
|
const struct cmd_entry cmd_switch_client_entry = {
|
||||||
.name = "switch-client",
|
.name = "switch-client",
|
||||||
|
.description = "Switch a client to another session.",
|
||||||
.alias = "switchc",
|
.alias = "switchc",
|
||||||
|
|
||||||
.args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
|
.args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: cmd-unbind-key.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
|
/* $OpenBSD: cmd-unbind-key.c,v 1.35 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -30,6 +30,7 @@ static enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_unbind_key_entry = {
|
const struct cmd_entry cmd_unbind_key_entry = {
|
||||||
.name = "unbind-key",
|
.name = "unbind-key",
|
||||||
|
.description = "Remove key bindings.",
|
||||||
.alias = "unbind",
|
.alias = "unbind",
|
||||||
|
|
||||||
.args = { "anqT:", 0, 1, NULL },
|
.args = { "anqT:", 0, 1, NULL },
|
||||||
|
|||||||
@@ -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.25 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -32,6 +32,7 @@ static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmdq_item *);
|
|||||||
|
|
||||||
const struct cmd_entry cmd_wait_for_entry = {
|
const struct cmd_entry cmd_wait_for_entry = {
|
||||||
.name = "wait-for",
|
.name = "wait-for",
|
||||||
|
.description = "Wait for or signal named channels and events.",
|
||||||
.alias = "wait",
|
.alias = "wait",
|
||||||
|
|
||||||
.args = { "EF:LSUlvw:", 1, 1, NULL },
|
.args = { "EF:LSUlvw:", 1, 1, NULL },
|
||||||
|
|||||||
28
format.c
28
format.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: format.c,v 1.421 2026/09/28 16:52:55 nicm Exp $ */
|
/* $OpenBSD: format.c,v 1.425 2026/10/05 10:59:37 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -88,9 +88,12 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
|||||||
/* Maximum pad and trim width. */
|
/* Maximum pad and trim width. */
|
||||||
#define FORMAT_MAX_WIDTH 10000
|
#define FORMAT_MAX_WIDTH 10000
|
||||||
|
|
||||||
/* Maximum repeat size. */
|
/* Maximum repeat count. */
|
||||||
#define FORMAT_MAX_REPEAT 10000
|
#define FORMAT_MAX_REPEAT 10000
|
||||||
|
|
||||||
|
/* Maximum repeat result size in bytes. */
|
||||||
|
#define FORMAT_MAX_REPEAT_SIZE 65536
|
||||||
|
|
||||||
/* Maximum precision. */
|
/* Maximum precision. */
|
||||||
#define FORMAT_MAX_PRECISION 100
|
#define FORMAT_MAX_PRECISION 100
|
||||||
|
|
||||||
@@ -1676,7 +1679,7 @@ format_cb_client_last_session(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_client_name(struct format_tree *ft)
|
format_cb_client_name(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->c != NULL)
|
if (ft->c != NULL && ft->c->name != NULL)
|
||||||
return (xstrdup(ft->c->name));
|
return (xstrdup(ft->c->name));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -1739,7 +1742,7 @@ format_cb_client_termfeatures(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_client_termname(struct format_tree *ft)
|
format_cb_client_termname(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->c != NULL)
|
if (ft->c != NULL && ft->c->term_name != NULL)
|
||||||
return (xstrdup(ft->c->term_name));
|
return (xstrdup(ft->c->term_name));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -1760,7 +1763,7 @@ format_cb_client_termtype(struct format_tree *ft)
|
|||||||
static void *
|
static void *
|
||||||
format_cb_client_tty(struct format_tree *ft)
|
format_cb_client_tty(struct format_tree *ft)
|
||||||
{
|
{
|
||||||
if (ft->c != NULL)
|
if (ft->c != NULL && ft->c->ttyname != NULL)
|
||||||
return (xstrdup(ft->c->ttyname));
|
return (xstrdup(ft->c->ttyname));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@@ -4565,8 +4568,8 @@ format_pretty_time(time_t t, int seconds)
|
|||||||
now = t;
|
now = t;
|
||||||
age = now - t;
|
age = now - t;
|
||||||
|
|
||||||
localtime_r(&now, &now_tm);
|
if (localtime_r(&now, &now_tm) == NULL || localtime_r(&t, &tm) == NULL)
|
||||||
localtime_r(&t, &tm);
|
return (xstrdup(""));
|
||||||
|
|
||||||
/* Last 24 hours. */
|
/* Last 24 hours. */
|
||||||
if (age < 24 * 3600) {
|
if (age < 24 * 3600) {
|
||||||
@@ -4737,10 +4740,12 @@ found:
|
|||||||
found = format_pretty_time(t, 0);
|
found = format_pretty_time(t, 0);
|
||||||
else {
|
else {
|
||||||
if (time_format != NULL) {
|
if (time_format != NULL) {
|
||||||
localtime_r(&t, &tm);
|
if (localtime_r(&t, &tm) == NULL)
|
||||||
|
return (NULL);
|
||||||
format_strftime(s, sizeof s, time_format, &tm);
|
format_strftime(s, sizeof s, time_format, &tm);
|
||||||
} else {
|
} else {
|
||||||
ctime_r(&t, s);
|
if (ctime_r(&t, s) == NULL)
|
||||||
|
return (NULL);
|
||||||
s[strcspn(s, "\n")] = '\0';
|
s[strcspn(s, "\n")] = '\0';
|
||||||
}
|
}
|
||||||
found = xstrdup(s);
|
found = xstrdup(s);
|
||||||
@@ -4871,6 +4876,9 @@ format_skip1(struct format_expand_state *es, const char *s, const char *end)
|
|||||||
int brackets = 0;
|
int brackets = 0;
|
||||||
u_int check = 0;
|
u_int check = 0;
|
||||||
|
|
||||||
|
if (es != NULL && !format_check_time(es, NULL))
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
for (; *s != '\0'; s++) {
|
for (; *s != '\0'; s++) {
|
||||||
if (es != NULL && !format_check_time(es, &check))
|
if (es != NULL && !format_check_time(es, &check))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@@ -6472,7 +6480,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
value = xstrdup("");
|
value = xstrdup("");
|
||||||
else {
|
else {
|
||||||
n = strlen(left);
|
n = strlen(left);
|
||||||
if (n != 0 && nrep > (SIZE_MAX - 1) / n) {
|
if (n != 0 && nrep > FORMAT_MAX_REPEAT_SIZE / n) {
|
||||||
format_log(es, "repeat is too long: %s", copy);
|
format_log(es, "repeat is too long: %s", copy);
|
||||||
value = xstrdup("");
|
value = xstrdup("");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
207
grid-reader.c
207
grid-reader.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid-reader.c,v 1.11 2026/07/29 17:42:56 nicm Exp $ */
|
/* $OpenBSD: grid-reader.c,v 1.12 2026/10/08 07:50:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
||||||
@@ -436,3 +436,208 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
|
|||||||
gr->cx = oldx;
|
gr->cx = oldx;
|
||||||
gr->cy = oldy;
|
gr->cy = oldy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the end of the last used line. */
|
||||||
|
static void
|
||||||
|
grid_reader_output_end(struct grid *gd, u_int *x, u_int *y)
|
||||||
|
{
|
||||||
|
u_int last = gd->hsize + gd->sy - 1;
|
||||||
|
|
||||||
|
while (last > 0 && grid_get_line(gd, last)->cellused == 0)
|
||||||
|
last--;
|
||||||
|
*x = grid_get_line(gd, last)->cellused;
|
||||||
|
*y = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the most recent complete output at or before the cursor. */
|
||||||
|
static int
|
||||||
|
grid_reader_previous_output_range(struct grid_reader *gr, u_int *sx,
|
||||||
|
u_int *sy, u_int *ex, u_int *ey)
|
||||||
|
{
|
||||||
|
struct grid *gd = gr->gd;
|
||||||
|
struct grid_line *gl;
|
||||||
|
struct osc133_data *od;
|
||||||
|
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||||
|
u_int start_x, start_y, y, total;
|
||||||
|
int found = 0, have_prompt = 0;
|
||||||
|
int pending = 0;
|
||||||
|
int has_start, has_end, end_first;
|
||||||
|
int cleared;
|
||||||
|
|
||||||
|
total = gd->hsize + gd->sy;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan from the top because markers arrive in their natural order, C
|
||||||
|
* then D. A backward scan would meet D first and need extra state to
|
||||||
|
* find its C. The last complete output found is the one wanted.
|
||||||
|
*/
|
||||||
|
for (y = 0; y < total && y <= cursor_y; y++) {
|
||||||
|
gl = grid_get_line(gd, y);
|
||||||
|
od = &gl->osc133_data;
|
||||||
|
|
||||||
|
/* On the cursor's line, ignore markers after the cursor. */
|
||||||
|
has_start = has_end = end_first = 0;
|
||||||
|
if (gl->flags & GRID_LINE_START_OUTPUT) {
|
||||||
|
if (y != cursor_y || od->out_start_col <= cursor_x)
|
||||||
|
has_start = 1;
|
||||||
|
}
|
||||||
|
if (gl->flags & GRID_LINE_END_OUTPUT) {
|
||||||
|
if (y != cursor_y || od->out_end_col <= cursor_x)
|
||||||
|
has_end = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An end before the start ends the previous output. */
|
||||||
|
if (has_start && has_end && od->out_end_col < od->out_start_col)
|
||||||
|
end_first = 1;
|
||||||
|
|
||||||
|
/* A C marker starts an output that is pending until its D. */
|
||||||
|
if (has_start && !end_first) {
|
||||||
|
start_x = od->out_start_col;
|
||||||
|
start_y = y;
|
||||||
|
pending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The output may have cleared its C marker from the screen. */
|
||||||
|
if (!pending && !have_prompt &&
|
||||||
|
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||||
|
cleared = 1;
|
||||||
|
if ((gl->flags & GRID_LINE_START_PROMPT) &&
|
||||||
|
od->out_end_col > od->prompt_col)
|
||||||
|
cleared = 0;
|
||||||
|
if (cleared) {
|
||||||
|
start_x = start_y = 0;
|
||||||
|
pending = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A D marker completes the pending output. */
|
||||||
|
if (pending && has_end) {
|
||||||
|
*sx = start_x;
|
||||||
|
*sy = start_y;
|
||||||
|
*ex = od->out_end_col;
|
||||||
|
*ey = y;
|
||||||
|
found = 1;
|
||||||
|
pending = 0;
|
||||||
|
}
|
||||||
|
if (has_start && end_first) {
|
||||||
|
start_x = od->out_start_col;
|
||||||
|
start_y = y;
|
||||||
|
pending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A prompt drops an unfinished output. */
|
||||||
|
if (gl->flags & GRID_LINE_START_PROMPT) {
|
||||||
|
if (!has_start || od->out_start_col < od->prompt_col)
|
||||||
|
pending = 0;
|
||||||
|
have_prompt = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (found);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the output range for the command at the cursor, from its C marker to
|
||||||
|
* its D marker. If the prompt has no output yet, use the previous output.
|
||||||
|
* Returns 0 if there is no usable range.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
grid_reader_output_range(struct grid_reader *gr, u_int *sx, u_int *sy,
|
||||||
|
u_int *ex, u_int *ey)
|
||||||
|
{
|
||||||
|
struct grid *gd = gr->gd;
|
||||||
|
struct grid_line *gl;
|
||||||
|
struct osc133_data *od;
|
||||||
|
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||||
|
u_int prompt_x = 0, prompt_y = UINT_MAX;
|
||||||
|
u_int y, total;
|
||||||
|
int found_start = 0, found_end = 0;
|
||||||
|
int next_prompt, in_range, end_ok;
|
||||||
|
int found;
|
||||||
|
|
||||||
|
/* Find the last prompt at or before the cursor. */
|
||||||
|
total = gd->hsize + gd->sy;
|
||||||
|
for (y = 0; y < total; y++) {
|
||||||
|
gl = grid_get_line(gd, y);
|
||||||
|
od = &gl->osc133_data;
|
||||||
|
if (~gl->flags & GRID_LINE_START_PROMPT)
|
||||||
|
continue;
|
||||||
|
if (y > cursor_y)
|
||||||
|
break;
|
||||||
|
if (y == cursor_y && od->prompt_col > cursor_x)
|
||||||
|
break;
|
||||||
|
prompt_y = y;
|
||||||
|
prompt_x = od->prompt_col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With no prompt, its A marker may have left history, so start at 0. */
|
||||||
|
if (prompt_y == UINT_MAX)
|
||||||
|
y = 0;
|
||||||
|
else
|
||||||
|
y = prompt_y;
|
||||||
|
|
||||||
|
/* Walk down from the prompt to its C and D, up to the next prompt. */
|
||||||
|
for (; y < total; y++) {
|
||||||
|
gl = grid_get_line(gd, y);
|
||||||
|
od = &gl->osc133_data;
|
||||||
|
next_prompt = 0;
|
||||||
|
if (y != prompt_y && (gl->flags & GRID_LINE_START_PROMPT))
|
||||||
|
next_prompt = 1;
|
||||||
|
|
||||||
|
/* Output before the next prompt on its line is ours. */
|
||||||
|
if (y == prompt_y)
|
||||||
|
in_range = (od->out_start_col >= prompt_x);
|
||||||
|
else if (next_prompt)
|
||||||
|
in_range = (od->out_start_col < od->prompt_col);
|
||||||
|
else
|
||||||
|
in_range = 1;
|
||||||
|
if (gl->flags & GRID_LINE_START_OUTPUT && in_range) {
|
||||||
|
*sx = od->out_start_col;
|
||||||
|
*sy = y;
|
||||||
|
found_start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both A and C may have left history while D remains. */
|
||||||
|
if (!found_start && prompt_y == UINT_MAX &&
|
||||||
|
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||||
|
if (!next_prompt || od->out_end_col <= od->prompt_col) {
|
||||||
|
*sx = *sy = 0;
|
||||||
|
found_start = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An output may end on the same line or the next prompt's. */
|
||||||
|
if (found_start && (gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||||
|
end_ok = 1;
|
||||||
|
if (y == prompt_y && od->out_end_col < prompt_x)
|
||||||
|
end_ok = 0;
|
||||||
|
if (y == *sy && od->out_end_col < *sx)
|
||||||
|
end_ok = 0;
|
||||||
|
if (end_ok) {
|
||||||
|
*ex = od->out_end_col;
|
||||||
|
*ey = y;
|
||||||
|
found_end = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (next_prompt)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The cursor is on a prompt with no output yet. */
|
||||||
|
if (!found_start) {
|
||||||
|
found = grid_reader_previous_output_range(gr, sx, sy, ex, ey);
|
||||||
|
return (found);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Without a D marker the command is still running, so its output runs
|
||||||
|
* to the last used line. If the next prompt came first, it has no
|
||||||
|
* usable end.
|
||||||
|
*/
|
||||||
|
if (!found_end) {
|
||||||
|
if (y != total)
|
||||||
|
return (0);
|
||||||
|
grid_reader_output_end(gd, ex, ey);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|||||||
81
grid.c
81
grid.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */
|
/* $OpenBSD: grid.c,v 1.159 2026/10/08 07:50:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -1347,12 +1347,60 @@ grid_reflow_move(struct grid *gd, struct grid_line *from)
|
|||||||
return (to);
|
return (to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Move the OSC 133 markers from one line to another. */
|
||||||
|
static void
|
||||||
|
grid_reflow_move_osc133(struct grid_line *to, struct grid_line *from,
|
||||||
|
u_int to_col, u_int count, int last)
|
||||||
|
{
|
||||||
|
struct osc133_data *src = &from->osc133_data;
|
||||||
|
struct osc133_data *dst = &to->osc133_data;
|
||||||
|
u_short *src_col[] = {
|
||||||
|
&src->prompt_col,
|
||||||
|
&src->cmd_col,
|
||||||
|
&src->out_start_col,
|
||||||
|
&src->out_end_col
|
||||||
|
};
|
||||||
|
u_short *dst_col[] = {
|
||||||
|
&dst->prompt_col,
|
||||||
|
&dst->cmd_col,
|
||||||
|
&dst->out_start_col,
|
||||||
|
&dst->out_end_col
|
||||||
|
};
|
||||||
|
u_int flags[] = {
|
||||||
|
GRID_LINE_START_PROMPT|GRID_LINE_SECOND_PROMPT,
|
||||||
|
GRID_LINE_START_COMMAND,
|
||||||
|
GRID_LINE_START_OUTPUT,
|
||||||
|
GRID_LINE_END_OUTPUT
|
||||||
|
};
|
||||||
|
u_int i, flag;
|
||||||
|
|
||||||
|
if ((from->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||||
|
return;
|
||||||
|
for (i = 0; i < nitems(flags); i++) {
|
||||||
|
flag = from->flags & flags[i];
|
||||||
|
if (flag == 0)
|
||||||
|
continue;
|
||||||
|
if (!last && *src_col[i] >= count) {
|
||||||
|
*src_col[i] -= count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*dst_col[i] = to_col + *src_col[i];
|
||||||
|
to->flags |= flag;
|
||||||
|
from->flags &= ~flag;
|
||||||
|
*src_col[i] = 0;
|
||||||
|
if (flag == GRID_LINE_END_OUTPUT) {
|
||||||
|
dst->exit_status = src->exit_status;
|
||||||
|
src->exit_status = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Join line below onto this one. */
|
/* Join line below onto this one. */
|
||||||
static void
|
static void
|
||||||
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||||
u_int width, int already)
|
u_int width, int already)
|
||||||
{
|
{
|
||||||
struct grid_line *gl, *from = NULL;
|
struct grid_line *gl, *from = NULL, *next;
|
||||||
struct grid_cell gc;
|
struct grid_cell gc;
|
||||||
u_int lines, left, i, to, line, want = 0;
|
u_int lines, left, i, to, line, want = 0;
|
||||||
u_int at;
|
u_int at;
|
||||||
@@ -1383,13 +1431,20 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
break;
|
break;
|
||||||
line = yy + 1 + lines;
|
line = yy + 1 + lines;
|
||||||
|
|
||||||
/* If the next line is empty, skip it. */
|
/* Consume empty lines, including any markers. */
|
||||||
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
||||||
wrapped = 0;
|
wrapped = 0;
|
||||||
if (gd->linedata[line].cellused == 0) {
|
if (gd->linedata[line].cellused == 0) {
|
||||||
|
next = &gd->linedata[line];
|
||||||
|
if (!wrapped &&
|
||||||
|
(next->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||||
|
break;
|
||||||
|
from = next;
|
||||||
|
want = 0;
|
||||||
|
grid_reflow_move_osc133(gl, from, at, 0, 1);
|
||||||
|
lines++;
|
||||||
if (!wrapped)
|
if (!wrapped)
|
||||||
break;
|
break;
|
||||||
lines++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1416,6 +1471,8 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
grid_set_cell(target, at, to, &gc);
|
grid_set_cell(target, at, to, &gc);
|
||||||
at++;
|
at++;
|
||||||
}
|
}
|
||||||
|
grid_reflow_move_osc133(gl, from, at - want, want,
|
||||||
|
want == from->cellused);
|
||||||
lines++;
|
lines++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1486,6 +1543,14 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
line = target->sy + 1;
|
line = target->sy + 1;
|
||||||
first = grid_reflow_add(target, lines);
|
first = grid_reflow_add(target, lines);
|
||||||
|
|
||||||
|
/* The first line keeps only the markers within its first at cells. */
|
||||||
|
memcpy(first, gl, sizeof *first);
|
||||||
|
first->cellsize = first->cellused = at;
|
||||||
|
first->flags &= ~GRID_LINE_OSC133_FLAGS;
|
||||||
|
first->flags |= GRID_LINE_WRAPPED;
|
||||||
|
memset(&first->osc133_data, 0, sizeof first->osc133_data);
|
||||||
|
grid_reflow_move_osc133(first, gl, 0, at, 0);
|
||||||
|
|
||||||
/* Copy sections from the original line. */
|
/* Copy sections from the original line. */
|
||||||
width = 0;
|
width = 0;
|
||||||
xx = 0;
|
xx = 0;
|
||||||
@@ -1493,6 +1558,8 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
grid_get_cell1(gl, i, &gc);
|
grid_get_cell1(gl, i, &gc);
|
||||||
if (width + gc.data.width > sx) {
|
if (width + gc.data.width > sx) {
|
||||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||||
|
grid_reflow_move_osc133(&target->linedata[line], gl, 0,
|
||||||
|
xx, 0);
|
||||||
|
|
||||||
line++;
|
line++;
|
||||||
width = 0;
|
width = 0;
|
||||||
@@ -1504,11 +1571,7 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
|||||||
}
|
}
|
||||||
if (flags & GRID_LINE_WRAPPED)
|
if (flags & GRID_LINE_WRAPPED)
|
||||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||||
|
grid_reflow_move_osc133(&target->linedata[line], gl, 0, xx, 1);
|
||||||
/* Move the remainder of the original line. */
|
|
||||||
gl->cellsize = gl->cellused = at;
|
|
||||||
gl->flags |= GRID_LINE_WRAPPED;
|
|
||||||
memcpy(first, gl, sizeof *first);
|
|
||||||
grid_reflow_dead(gl);
|
grid_reflow_dead(gl);
|
||||||
|
|
||||||
/* Adjust the scroll position. */
|
/* Adjust the scroll position. */
|
||||||
|
|||||||
3
input.c
3
input.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: input.c,v 1.272 2026/09/21 10:22:31 nicm Exp $ */
|
/* $OpenBSD: input.c,v 1.273 2026/10/08 07:50:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -3313,7 +3313,6 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
|||||||
case 'A':
|
case 'A':
|
||||||
case 'N':
|
case 'N':
|
||||||
if (gl != NULL) {
|
if (gl != NULL) {
|
||||||
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
|
|
||||||
gl->osc133_data.prompt_col = s->cx;
|
gl->osc133_data.prompt_col = s->cx;
|
||||||
gl->flags |= GRID_LINE_START_PROMPT;
|
gl->flags |= GRID_LINE_START_PROMPT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.194 2026/10/08 07:50:05 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
|
" '#{?#{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,Hide Line Numbers,Show Line Numbers},}' 'L' {send -X line-numbers-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}" \
|
" '#{?#{==:#{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}" \
|
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
|
||||||
@@ -575,6 +576,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
||||||
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
||||||
"bind -Tcopy-mode C-n { send -X cursor-down }",
|
"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-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-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 -- '%%' } }",
|
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
|
||||||
@@ -592,6 +594,7 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode R { send -X rectangle-toggle }",
|
"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 T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||||
"bind -Tcopy-mode X { send -X set-mark }",
|
"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 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 g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||||
"bind -Tcopy-mode n { send -X search-again }",
|
"bind -Tcopy-mode n { send -X search-again }",
|
||||||
@@ -687,6 +690,8 @@ key_bindings_init(void)
|
|||||||
"bind -Tcopy-mode-vi K { send -X scroll-up }",
|
"bind -Tcopy-mode-vi K { send -X scroll-up }",
|
||||||
"bind -Tcopy-mode-vi L { send -X bottom-line }",
|
"bind -Tcopy-mode-vi L { send -X bottom-line }",
|
||||||
"bind -Tcopy-mode-vi M { send -X middle-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 N { send -X search-reverse }",
|
||||||
"bind -Tcopy-mode-vi P { send -X toggle-position }",
|
"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 T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||||
|
|||||||
8
layout.c
8
layout.c
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: layout.c,v 1.101 2026/09/20 08:42:46 nicm Exp $ */
|
/* $OpenBSD: layout.c,v 1.102 2026/10/02 12:48:52 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -469,7 +469,6 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
|||||||
sx = PANE_MINIMUM;
|
sx = PANE_MINIMUM;
|
||||||
else
|
else
|
||||||
sx = sx - sb_w - sb_pad;
|
sx = sx - sb_w - sb_pad;
|
||||||
wp->flags |= PANE_REDRAWSCROLLBAR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window_pane_resize(wp, sx, sy);
|
window_pane_resize(wp, sx, sy);
|
||||||
@@ -477,8 +476,11 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
|||||||
if (wp->xoff != old_xoff ||
|
if (wp->xoff != old_xoff ||
|
||||||
wp->yoff != old_yoff ||
|
wp->yoff != old_yoff ||
|
||||||
wp->sx != old_sx ||
|
wp->sx != old_sx ||
|
||||||
wp->sy != old_sy)
|
wp->sy != old_sy) {
|
||||||
|
if (window_pane_scrollbar_reserve(wp))
|
||||||
|
wp->flags |= PANE_REDRAWSCROLLBAR;
|
||||||
changed = 1;
|
changed = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
redraw_invalidate_scene(w);
|
redraw_invalidate_scene(w);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: options-table.c,v 1.247 2026/09/21 10:22:31 nicm Exp $ */
|
/* $OpenBSD: options-table.c,v 1.252 2026/10/09 13:12:14 nicm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
@@ -183,7 +183,8 @@ static const char *options_table_copy_mode_line_numbers_list[] = {
|
|||||||
"#[pop-default]" \
|
"#[pop-default]" \
|
||||||
"#[norange default]"
|
"#[norange default]"
|
||||||
#define OPTIONS_TABLE_STATUS_FORMAT2 \
|
#define OPTIONS_TABLE_STATUS_FORMAT2 \
|
||||||
"#[align=left]#{R: ,#{n:#{session_name}}}P: " \
|
"#[align=left]" \
|
||||||
|
"#{R: ,#{e|-:#{w;T;=/#{status-left-length}:status-left},3}}P: " \
|
||||||
"#[norange default]" \
|
"#[norange default]" \
|
||||||
"#[list=on align=#{status-justify}]" \
|
"#[list=on align=#{status-justify}]" \
|
||||||
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
|
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
|
||||||
@@ -194,7 +195,8 @@ static const char *options_table_copy_mode_line_numbers_list[] = {
|
|||||||
"#[push-default]" \
|
"#[push-default]" \
|
||||||
"#{T:window-pane-status-format}" \
|
"#{T:window-pane-status-format}" \
|
||||||
"#[pop-default]" \
|
"#[pop-default]" \
|
||||||
"#[norange list=on default] " \
|
"#[norange list=on default]" \
|
||||||
|
"#{?loop_last_flag,,#{E:window-status-separator}}" \
|
||||||
"," \
|
"," \
|
||||||
"#[range=pane|#{pane_id} list=focus " \
|
"#[range=pane|#{pane_id} list=focus " \
|
||||||
"#{?#{!=:#{E:pane-status-current-style},default}," \
|
"#{?#{!=:#{E:pane-status-current-style},default}," \
|
||||||
@@ -205,10 +207,12 @@ static const char *options_table_copy_mode_line_numbers_list[] = {
|
|||||||
"#[push-default]" \
|
"#[push-default]" \
|
||||||
"#{T:window-pane-current-status-format}" \
|
"#{T:window-pane-current-status-format}" \
|
||||||
"#[pop-default]" \
|
"#[pop-default]" \
|
||||||
"#[norange list=on default] " \
|
"#[norange list=on default]" \
|
||||||
|
"#{?loop_last_flag,,#{E:window-status-separator}}" \
|
||||||
"}"
|
"}"
|
||||||
#define OPTIONS_TABLE_STATUS_FORMAT3 \
|
#define OPTIONS_TABLE_STATUS_FORMAT3 \
|
||||||
"#[align=left]#{R: ,#{n:#{session_name}}}S: " \
|
"#[align=left]" \
|
||||||
|
"#{R: ,#{e|-:#{w;T;=/#{status-left-length}:status-left},3}}S: " \
|
||||||
"#[norange default]" \
|
"#[norange default]" \
|
||||||
"#[list=on align=#{status-justify}]" \
|
"#[list=on align=#{status-justify}]" \
|
||||||
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
|
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
|
||||||
@@ -217,7 +221,7 @@ static const char *options_table_copy_mode_line_numbers_list[] = {
|
|||||||
"#{E:session-status-style}" \
|
"#{E:session-status-style}" \
|
||||||
"]" \
|
"]" \
|
||||||
"#[push-default]" \
|
"#[push-default]" \
|
||||||
"#S#{session_alert}" \
|
"#S#{q/h:session_alert}" \
|
||||||
"#[pop-default]" \
|
"#[pop-default]" \
|
||||||
"#[norange list=on default] " \
|
"#[norange list=on default] " \
|
||||||
"," \
|
"," \
|
||||||
@@ -228,7 +232,7 @@ static const char *options_table_copy_mode_line_numbers_list[] = {
|
|||||||
"}" \
|
"}" \
|
||||||
"]" \
|
"]" \
|
||||||
"#[push-default]" \
|
"#[push-default]" \
|
||||||
"#S*#{session_alert}" \
|
"#S*#{q/h:session_alert}" \
|
||||||
"#[pop-default]" \
|
"#[pop-default]" \
|
||||||
"#[norange list=on default] " \
|
"#[norange list=on default] " \
|
||||||
"}"
|
"}"
|
||||||
@@ -325,7 +329,8 @@ const struct options_table_entry options_table[] = {
|
|||||||
"server-info=show-messages -JT,"
|
"server-info=show-messages -JT,"
|
||||||
"info=show-messages -JT,"
|
"info=show-messages -JT,"
|
||||||
"choose-window=choose-tree -w,"
|
"choose-window=choose-tree -w,"
|
||||||
"choose-session=choose-tree -s",
|
"choose-session=choose-tree -s,"
|
||||||
|
"help=list-commands -h",
|
||||||
.separator = ",",
|
.separator = ",",
|
||||||
.text = "Array of command aliases. "
|
.text = "Array of command aliases. "
|
||||||
"Each entry is an alias and a command separated by '='."
|
"Each entry is an alias and a command separated by '='."
|
||||||
@@ -739,6 +744,15 @@ const struct options_table_entry options_table[] = {
|
|||||||
"'User0', 'User1' and so on."
|
"'User0', 'User1' and so on."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#ifdef HAVE_UTEMPTER
|
||||||
|
{ .name = "utmp",
|
||||||
|
.type = OPTIONS_TABLE_FLAG,
|
||||||
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
|
.default_num = 1,
|
||||||
|
.text = "Whether to maintain utmp records."
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
|
||||||
{ .name = "variation-selector-always-wide",
|
{ .name = "variation-selector-always-wide",
|
||||||
.type = OPTIONS_TABLE_FLAG,
|
.type = OPTIONS_TABLE_FLAG,
|
||||||
.scope = OPTIONS_TABLE_SERVER,
|
.scope = OPTIONS_TABLE_SERVER,
|
||||||
@@ -1410,8 +1424,12 @@ const struct options_table_entry options_table[] = {
|
|||||||
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
|
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
|
||||||
"[#{copy_position}/#{copy_position_limit}]"
|
"[#{copy_position}/#{copy_position_limit}]"
|
||||||
"#{?search_timed_out, (timed out),"
|
"#{?search_timed_out, (timed out),"
|
||||||
"#{?search_count, (#{search_count}"
|
"#{?search_count_present, ("
|
||||||
"#{?search_count_partial,+,} results),}}",
|
"#{?#{==:#{search_count},0},0 matches,"
|
||||||
|
"#{?search_count_partial,#{search_count}+ matches,"
|
||||||
|
"#{?#{==:#{search_count},1},1 of 1 match,"
|
||||||
|
"#{?search_count_current,#{search_count_current} of "
|
||||||
|
"#{search_count} matches,#{search_count} matches}}}}),}}",
|
||||||
.text = "Format of the position indicator in copy mode."
|
.text = "Format of the position indicator in copy mode."
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1773,6 +1791,14 @@ const struct options_table_entry options_table[] = {
|
|||||||
.text = "Default style of the active pane."
|
.text = "Default style of the active pane."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ .name = "window-default-command",
|
||||||
|
.type = OPTIONS_TABLE_STRING,
|
||||||
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
.default_str = "",
|
||||||
|
.text = "Default command to run in new panes. If set, "
|
||||||
|
"overrides 'default-command'."
|
||||||
|
},
|
||||||
|
|
||||||
{ .name = "window-pane-current-status-format",
|
{ .name = "window-pane-current-status-format",
|
||||||
.type = OPTIONS_TABLE_STRING,
|
.type = OPTIONS_TABLE_STRING,
|
||||||
.scope = OPTIONS_TABLE_WINDOW,
|
.scope = OPTIONS_TABLE_WINDOW,
|
||||||
|
|||||||
@@ -1410,6 +1410,14 @@ options_push_changes(const char *name)
|
|||||||
tty_keys_build(&loop->tty);
|
tty_keys_build(&loop->tty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (strcmp(name, "utmp") == 0) {
|
||||||
|
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||||
|
if (options_get_number(global_options, name))
|
||||||
|
window_pane_utmp_add(wp);
|
||||||
|
else
|
||||||
|
window_pane_utmp_remove(wp);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (strcmp(name, "status") == 0 ||
|
if (strcmp(name, "status") == 0 ||
|
||||||
strcmp(name, "status-interval") == 0)
|
strcmp(name, "status-interval") == 0)
|
||||||
status_timer_start_all();
|
status_timer_start_all();
|
||||||
|
|||||||
1
proc.c
1
proc.c
@@ -32,6 +32,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_JEMALLOC
|
#ifdef HAVE_JEMALLOC
|
||||||
|
#define JEMALLOC_MANGLE
|
||||||
#include <jemalloc/jemalloc.h>
|
#include <jemalloc/jemalloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
34
regress/alternate-screen-resize.sh
Normal file
34
regress/alternate-screen-resize.sh
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A pending pane resize must still reach the pty when the application enters
|
||||||
|
# the alternate screen before the resize timer fires.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
TMP2=$(mktemp)
|
||||||
|
trap 'rm -f "$TMP" "$TMP2"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||||
|
|
||||||
|
$TMUX new -d -x 80 -y 24 \
|
||||||
|
"read x; printf '\033[?1049h'; read x; stty size >$TMP; sleep 100" \
|
||||||
|
</dev/null || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# The first split resizes and starts the resize timer, the second queues
|
||||||
|
# another resize while the timer is pending.
|
||||||
|
$TMUX splitw -h -d 'sleep 100' || exit 1
|
||||||
|
$TMUX splitw -v -d -t:0.0 'sleep 100' || exit 1
|
||||||
|
$TMUX send -t:0.0 Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
$TMUX send -t:0.0 Enter || exit 1
|
||||||
|
sleep 1
|
||||||
|
$TMUX display -p -t:0.0 '#{pane_height} #{pane_width}' >$TMP2
|
||||||
|
cmp -s "$TMP" "$TMP2" || exit 1
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
exit 0
|
||||||
79
regress/attach-session-redraw.sh
Executable file
79
regress/attach-session-redraw.sh
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Selecting a pane with attach-session from an already attached client must
|
||||||
|
# redraw pane contents when window-style and window-active-style differ.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lattach-redraw-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lattach-redraw-outer-$$ -f/dev/null"
|
||||||
|
BEFORE=$DIR/before
|
||||||
|
AFTER=$DIR/after
|
||||||
|
|
||||||
|
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_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
[ -n "$CLIENT" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
LEFT=$($INNER new-session -dPF '#{pane_id}' -s inner -x 40 -y 8 \
|
||||||
|
"printf 'LEFT'; exec sleep 100") || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g default-terminal screen || exit 1
|
||||||
|
$INNER split-window -h -t "$LEFT" "printf 'RIGHT'; exec sleep 100" || exit 1
|
||||||
|
$INNER set-option -w -t "$LEFT" window-style bg=red || exit 1
|
||||||
|
$INNER set-option -w -t "$LEFT" window-active-style bg=blue || exit 1
|
||||||
|
$INNER bind-key -n x attach-session -t "$LEFT" || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 8 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lattach-redraw-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
wait_for_client
|
||||||
|
sleep 1
|
||||||
|
$OUTER send-keys -t outer:0.0 x || exit 1
|
||||||
|
sleep 1
|
||||||
|
[ "$($INNER display-message -p -t inner '#{pane_id}')" = "$LEFT" ] ||
|
||||||
|
fail "attach-session did not select the target pane"
|
||||||
|
$OUTER capture-pane -pe -t outer:0.0 >"$BEFORE" || exit 1
|
||||||
|
|
||||||
|
# A forced redraw produces the correct scene. It must be identical to the
|
||||||
|
# scene drawn immediately by attach-session.
|
||||||
|
$INNER refresh-client -t "$CLIENT" || exit 1
|
||||||
|
sleep 1
|
||||||
|
$OUTER capture-pane -pe -t outer:0.0 >"$AFTER" || exit 1
|
||||||
|
cmp -s "$BEFORE" "$AFTER" ||
|
||||||
|
fail "attach-session left stale active/inactive pane styles"
|
||||||
|
|
||||||
|
exit 0
|
||||||
51
regress/cfg-client-free.sh
Normal file
51
regress/cfg-client-free.sh
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# a client lost while the configuration is loading is freed
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX="$TEST_TMUX -S$TMPDIR/tmux.sock"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
touch $TMPDIR/go
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
# Poll until a command succeeds.
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while ! "$@"; do
|
||||||
|
[ $i -eq 100 ] && exit 1
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# The run-shell holds the configuration until the go file exists.
|
||||||
|
cat <<EOF >$TMPDIR/conf
|
||||||
|
new-session -d -s keep
|
||||||
|
run-shell 'touch $TMPDIR/started; i=0; while [ ! -f $TMPDIR/go ] && [ \$i -lt 100 ]; do sleep 0.1; i=\$((i + 1)); done'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cd $TMPDIR || exit 1
|
||||||
|
$TMUX -v -f$TMPDIR/conf new-session -d -s first </dev/null &
|
||||||
|
pid=$!
|
||||||
|
wait_for test -f $TMPDIR/started
|
||||||
|
kill $pid
|
||||||
|
wait $pid
|
||||||
|
log=$(echo $TMPDIR/tmux-server-*.log)
|
||||||
|
wait_for grep -q 'lost client' $log
|
||||||
|
c=$(sed -n 's/.* lost client //p' $log)
|
||||||
|
|
||||||
|
# The client is freed straight away, well before the configuration ends.
|
||||||
|
wait_for grep -q "free client $c " $log
|
||||||
|
|
||||||
|
exit 0
|
||||||
90
regress/cfg-client-lost-before-wait.sh
Normal file
90
regress/cfg-client-lost-before-wait.sh
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A client lost before its configuration callback fires must not start waiting.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
python3 - "$TEST_TMUX" "$(dirname "$0")/../tmux-protocol.h" <<'PY'
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
tmux = sys.argv[1]
|
||||||
|
header = Path(sys.argv[2]).read_text()
|
||||||
|
version = int(re.search(r"#define PROTOCOL_VERSION\s+(\d+)", header)[1])
|
||||||
|
enum = re.search(r"enum msgtype\s*\{(.*?)\};", header, re.S)[1]
|
||||||
|
enum = re.sub(r"/\*.*?\*/", "", enum, flags=re.S)
|
||||||
|
types = {}
|
||||||
|
value = 0
|
||||||
|
for entry in enum.split(","):
|
||||||
|
entry = entry.strip()
|
||||||
|
if not entry:
|
||||||
|
continue
|
||||||
|
if "=" in entry:
|
||||||
|
entry, number = entry.split("=")
|
||||||
|
value = int(number.strip())
|
||||||
|
types[entry.strip()] = value
|
||||||
|
value += 1
|
||||||
|
|
||||||
|
def message(name, body=b""):
|
||||||
|
return struct.pack("=IIII", types[name], 16 + len(body), version,
|
||||||
|
os.getpid()) + body
|
||||||
|
|
||||||
|
def wait_for(predicate):
|
||||||
|
end = time.monotonic() + 5
|
||||||
|
while time.monotonic() < end:
|
||||||
|
result = predicate()
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
time.sleep(0.05)
|
||||||
|
raise AssertionError("timed out waiting for client cleanup")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory(prefix="tmux-cfg-early-") as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
sockpath = root / "socket"
|
||||||
|
go = root / "go"
|
||||||
|
conf = root / "conf"
|
||||||
|
conf.write_text("run-shell 'i=0; while [ ! -f %s ] && [ $i -lt 100 ]; "
|
||||||
|
"do sleep 0.1; i=$((i + 1)); done'\n" % go)
|
||||||
|
server = subprocess.Popen([tmux, "-D", "-v", "-S", str(sockpath),
|
||||||
|
"-f", str(conf)], cwd=tmp, stdout=subprocess.DEVNULL)
|
||||||
|
logpath = root / ("tmux-server-%d.log" % server.pid)
|
||||||
|
|
||||||
|
def log():
|
||||||
|
return logpath.read_text() if logpath.exists() else ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
wait_for(sockpath.exists)
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
|
||||||
|
client.connect(str(sockpath))
|
||||||
|
# Both messages are read together. The short MSG_COMMAND makes
|
||||||
|
# proc_event_cb lose the client before server_loop runs its queue.
|
||||||
|
client.sendall(message("MSG_IDENTIFY_CWD", tmp.encode() + b"\0") +
|
||||||
|
message("MSG_IDENTIFY_CLIENTPID", struct.pack("=i", os.getpid())) +
|
||||||
|
message("MSG_IDENTIFY_DONE") + message("MSG_COMMAND"))
|
||||||
|
lost = wait_for(lambda: re.search(r"lost client (\S+)", log()))[1]
|
||||||
|
wait_for(lambda: "free client %s (0 references)" % lost in log())
|
||||||
|
contents = log()
|
||||||
|
callback = "cmdq_next <client-%d>: [cfg_client_done/" % os.getpid()
|
||||||
|
assert contents.index("lost client " + lost) < \
|
||||||
|
contents.index(callback), contents
|
||||||
|
# Cleanup must happen while configuration is still blocked.
|
||||||
|
assert "cmdq_next <global>: [cfg_done/" not in contents, contents
|
||||||
|
finally:
|
||||||
|
go.touch()
|
||||||
|
server.terminate()
|
||||||
|
try:
|
||||||
|
server.wait(timeout=3)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
server.kill()
|
||||||
|
server.wait()
|
||||||
|
PY
|
||||||
60
regress/cfg-load-once.sh
Normal file
60
regress/cfg-load-once.sh
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# the configuration is loaded once if the client that started it is lost, and
|
||||||
|
# the next client still waits for it to finish
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d) || exit 1
|
||||||
|
TMUX="$TEST_TMUX -S$TMPDIR/tmux.sock"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
touch $TMPDIR/go
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
# Poll until a command succeeds.
|
||||||
|
wait_for()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while ! "$@"; do
|
||||||
|
[ $i -eq 100 ] && exit 1
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# The run-shell holds the configuration until the go file exists.
|
||||||
|
cat <<EOF >$TMPDIR/conf
|
||||||
|
set -ga @runs x
|
||||||
|
new-session -d -s keep
|
||||||
|
run-shell 'touch $TMPDIR/started; i=0; while [ ! -f $TMPDIR/go ] && [ \$i -lt 100 ]; do sleep 0.1; i=\$((i + 1)); done'
|
||||||
|
set -g @late 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cd $TMPDIR || exit 1
|
||||||
|
$TMUX -v -f$TMPDIR/conf new-session -d -s first </dev/null &
|
||||||
|
pid=$!
|
||||||
|
wait_for test -f $TMPDIR/started
|
||||||
|
kill $pid
|
||||||
|
wait $pid
|
||||||
|
log=$(echo $TMPDIR/tmux-server-*.log)
|
||||||
|
wait_for grep -q 'lost client' $log
|
||||||
|
|
||||||
|
# The second client must identify before the configuration is released.
|
||||||
|
$TMUX new-session -d -s second \; display -p '#{@runs} #{@late}' \
|
||||||
|
</dev/null >$TMPDIR/out &
|
||||||
|
pid=$!
|
||||||
|
wait_for grep -q "name is client-$pid\$" $log
|
||||||
|
touch $TMPDIR/go
|
||||||
|
wait $pid || exit 1
|
||||||
|
|
||||||
|
[ "$(cat $TMPDIR/out)" = "x 1" ] || exit 1
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -5,6 +5,15 @@
|
|||||||
PATH=/bin:/usr/bin
|
PATH=/bin:/usr/bin
|
||||||
TERM=screen
|
TERM=screen
|
||||||
|
|
||||||
|
# The pane's shell must not be the user's own interactive shell: a custom
|
||||||
|
# PS1/PROMPT_COMMAND that sets the terminal title (as many do, for tmux/xterm
|
||||||
|
# TERM types) would redraw over the titles this test sets and checks on
|
||||||
|
# every prompt, regardless of how long it waits first.
|
||||||
|
shell=
|
||||||
|
if command -v bash >/dev/null 2>&1; then
|
||||||
|
shell='bash --noprofile --norc +o history'
|
||||||
|
fi
|
||||||
|
|
||||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
@@ -30,7 +39,7 @@ must_equal()
|
|||||||
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
|
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
|
||||||
}
|
}
|
||||||
|
|
||||||
$TMUX new-session -d -x 80 -y 24 || exit 1
|
$TMUX new-session -d -x 80 -y 24 -- $shell || exit 1
|
||||||
$TMUX set-option -qg allow-set-title on || exit 1
|
$TMUX set-option -qg allow-set-title on || exit 1
|
||||||
$TMUX set-option -qg allow-rename on || exit 1
|
$TMUX set-option -qg allow-rename on || exit 1
|
||||||
$TMUX set-option -qg automatic-rename off || exit 1
|
$TMUX set-option -qg automatic-rename off || exit 1
|
||||||
|
|||||||
69
regress/cmd-parse-assignment.sh
Normal file
69
regress/cmd-parse-assignment.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A bare assignment in a command sequence must not discard earlier commands.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
CONF=$(mktemp)
|
||||||
|
trap '$TMUX kill-server 2>/dev/null; rm -f "$CONF"' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
$TMUX new-session -d || exit 1
|
||||||
|
|
||||||
|
check()
|
||||||
|
{
|
||||||
|
name=$1
|
||||||
|
expected=$2
|
||||||
|
$TMUX set -g @trace '' || exit 1
|
||||||
|
$TMUX set-environment -gu PARSE_ASSIGN || exit 1
|
||||||
|
printf '%s\n' "$3" >"$CONF"
|
||||||
|
$TMUX source-file "$CONF" || exit 1
|
||||||
|
trace=$($TMUX show-options -gqv @trace) || exit 1
|
||||||
|
assignment=$($TMUX show-environment -g PARSE_ASSIGN 2>/dev/null)
|
||||||
|
out="$trace:$assignment"
|
||||||
|
if [ "$out" != "$expected" ]; then
|
||||||
|
echo "$name: expected '$expected', got '$out'"
|
||||||
|
cat "$CONF"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check trailing 'ab:PARSE_ASSIGN=1' \
|
||||||
|
'set -ag @trace a; set -ag @trace b; PARSE_ASSIGN=1'
|
||||||
|
check middle 'ab:PARSE_ASSIGN=1' \
|
||||||
|
'set -ag @trace a; PARSE_ASSIGN=1; set -ag @trace b'
|
||||||
|
check leading 'ab:PARSE_ASSIGN=1' \
|
||||||
|
'PARSE_ASSIGN=1; set -ag @trace a; set -ag @trace b'
|
||||||
|
check repeated 'ab:PARSE_ASSIGN=2' \
|
||||||
|
'set -ag @trace a; PARSE_ASSIGN=1; set -ag @trace b; PARSE_ASSIGN=2'
|
||||||
|
check semicolon 'a:PARSE_ASSIGN=1' \
|
||||||
|
'set -ag @trace a; PARSE_ASSIGN=1;'
|
||||||
|
check standalone ':PARSE_ASSIGN=1' 'PARSE_ASSIGN=1'
|
||||||
|
check braces 'ab:PARSE_ASSIGN=1' \
|
||||||
|
'if-shell -F 1 { set -ag @trace a; set -ag @trace b; PARSE_ASSIGN=1 }'
|
||||||
|
check active 'ab:PARSE_ASSIGN=1' \
|
||||||
|
'%if 1 set -ag @trace a; set -ag @trace b; PARSE_ASSIGN=1 %endif'
|
||||||
|
check inactive ':' \
|
||||||
|
'%if 0 set -ag @trace a; set -ag @trace b; PARSE_ASSIGN=1 %endif'
|
||||||
|
|
||||||
|
# An assignment must not hide an invalid command earlier in the sequence.
|
||||||
|
$TMUX set -g @trace '' || exit 1
|
||||||
|
printf '%s\n' \
|
||||||
|
'set -ag @trace a; nonexistent-command; PARSE_ASSIGN=1' >"$CONF"
|
||||||
|
if out=$($TMUX source-file "$CONF" 2>&1); then
|
||||||
|
echo 'Invalid command before assignment was ignored'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$out" != "$CONF:1: unknown command: nonexistent-command" ]; then
|
||||||
|
echo "Unexpected parse error: '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
trace=$($TMUX show-options -gqv @trace) || exit 1
|
||||||
|
if [ -n "$trace" ]; then
|
||||||
|
echo 'Commands executed despite a parse error'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
75
regress/cmd-parse-condition.sh
Normal file
75
regress/cmd-parse-condition.sh
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Assignments and commands must use the first matching conditional branch.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
CONF=$(mktemp)
|
||||||
|
trap '$TMUX kill-server 2>/dev/null; rm -f "$CONF"' 0
|
||||||
|
trap 'exit 1' 1 2 15
|
||||||
|
$TMUX new-session -d || exit 1
|
||||||
|
|
||||||
|
branch()
|
||||||
|
{
|
||||||
|
printf '%%%s %s%s' "$1" "$2" "$sep"
|
||||||
|
printf '%sPARSE_BRANCH=%s%sset -g @branch %s%s' \
|
||||||
|
"$prefix" "$3" "$sep" "$3" "$sep"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cover no, one and two elif branches, with and without else, all condition
|
||||||
|
# values, and nesting inside both an active and an inactive outer branch.
|
||||||
|
for mode in normal hidden inline; do
|
||||||
|
prefix=
|
||||||
|
sep='
|
||||||
|
'
|
||||||
|
[ "$mode" = hidden ] && prefix='%hidden '
|
||||||
|
[ "$mode" = inline ] && sep=' '
|
||||||
|
for elifs in 0 1 2; do
|
||||||
|
bits=0
|
||||||
|
while [ "$bits" -lt "$((1 << (elifs + 1)))" ]; do
|
||||||
|
for otherwise in 0 1; do
|
||||||
|
for outer in 0 1; do
|
||||||
|
expected=unchanged
|
||||||
|
{
|
||||||
|
printf '%s\n' 'PARSE_BRANCH=unchanged' \
|
||||||
|
'set -g @branch unchanged'
|
||||||
|
printf '%%if %s\n' "$outer"
|
||||||
|
n=0
|
||||||
|
while [ "$n" -le "$elifs" ]; do
|
||||||
|
condition=$(((bits >> n) & 1))
|
||||||
|
directive=elif
|
||||||
|
[ "$n" -eq 0 ] && directive=if
|
||||||
|
branch "$directive" "$condition" "$n"
|
||||||
|
if [ "$condition" -eq 1 ] &&
|
||||||
|
[ "$expected" = unchanged ]; then
|
||||||
|
expected=$n
|
||||||
|
fi
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
if [ "$otherwise" -eq 1 ]; then
|
||||||
|
branch else '' else
|
||||||
|
[ "$expected" = unchanged ] && expected=else
|
||||||
|
fi
|
||||||
|
printf '%s\n' '%endif' '%endif' \
|
||||||
|
'set -g @value "$PARSE_BRANCH"'
|
||||||
|
} >"$CONF"
|
||||||
|
[ "$outer" -eq 0 ] && expected=unchanged
|
||||||
|
$TMUX source-file "$CONF" || exit 1
|
||||||
|
out=$($TMUX display-message -p '#{@value}:#{@branch}')
|
||||||
|
if [ "$out" != "$expected:$expected" ]; then
|
||||||
|
echo "$mode, $elifs elifs, bits=$bits, else=$otherwise, outer=$outer"
|
||||||
|
echo "Expected $expected:$expected, got $out"
|
||||||
|
cat "$CONF"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
bits=$((bits + 1))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
48
regress/copy-mode-no-client-scroll.sh
Normal file
48
regress/copy-mode-no-client-scroll.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# copy-mode -S can be called from a hook without an attached client.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
export PATH TERM
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
OUT=$(mktemp -d) || exit 1
|
||||||
|
TMUX_TMPDIR=$OUT
|
||||||
|
export TMUX_TMPDIR
|
||||||
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null || true
|
||||||
|
rm -rf "$OUT"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
trap 'exit 1' 1 2 3 15
|
||||||
|
|
||||||
|
$TMUX new-session -d -s test 'sleep 300' || fail "new-session failed"
|
||||||
|
$TMUX set -g remain-on-exit on || fail "setting remain-on-exit failed"
|
||||||
|
$TMUX set-hook -g pane-died 'copy-mode -S' ||
|
||||||
|
fail "setting pane-died hook failed"
|
||||||
|
$TMUX respawn-pane -k -t test:0 'exit 0' || fail "respawn-pane failed"
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$TMUX has-session -t test 2>/dev/null || \
|
||||||
|
fail "server died in copy-mode -S"
|
||||||
|
[ "$($TMUX display-message -p -t test:0 '#{pane_dead}' 2>/dev/null)" = 1 ] && \
|
||||||
|
break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "pane-died hook was not triggered"
|
||||||
|
sleep 0.5
|
||||||
|
$TMUX has-session -t test || fail "server died after copy-mode -S"
|
||||||
|
|
||||||
|
exit 0
|
||||||
161
regress/copy-mode-search-count.sh
Executable file
161
regress/copy-mode-search-count.sh
Executable file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup 0
|
||||||
|
trap 'exit 1' 1 2 3 15
|
||||||
|
|
||||||
|
$TMUX new -d -x40 -y10 \
|
||||||
|
"printf 'zero needle\none needle\ntwo needle\n'; cat" || exit 1
|
||||||
|
$TMUX set -g window-size manual || exit 1
|
||||||
|
|
||||||
|
check_count()
|
||||||
|
{
|
||||||
|
[ "$($TMUX display-message -p \
|
||||||
|
'#{search_count_current}/#{search_count}')" = "$1" ] || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_unsearched()
|
||||||
|
{
|
||||||
|
check_count /
|
||||||
|
[ -z "$($TMUX display-message -p '#{search_count_present}')" ] ||
|
||||||
|
exit 1
|
||||||
|
case "$($TMUX display-message -p \
|
||||||
|
'#{E:copy-mode-position-format}')" in
|
||||||
|
*match*|*result*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
for keys in vi emacs; do
|
||||||
|
$TMUX set -g mode-keys "$keys" || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X scroll-up || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX resize-window -x40 -y10 || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X start-of-line || exit 1
|
||||||
|
|
||||||
|
$TMUX send-keys -X search-forward-text needle || exit 1
|
||||||
|
check_count 1/3
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 2/3
|
||||||
|
$TMUX resize-window -x30 -y8 || exit 1
|
||||||
|
check_count 2/3
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 3/3
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 1/3
|
||||||
|
$TMUX send-keys -X search-reverse || exit 1
|
||||||
|
if [ "$keys" = vi ]; then
|
||||||
|
reverse_index=3
|
||||||
|
else
|
||||||
|
reverse_index=1
|
||||||
|
fi
|
||||||
|
check_count "$reverse_index/3"
|
||||||
|
case "$($TMUX display-message -p \
|
||||||
|
'#{E:copy-mode-position-format}')" in
|
||||||
|
*"($reverse_index of 3 matches)") ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X history-bottom || exit 1
|
||||||
|
$TMUX send-keys -X search-backward-text needle || exit 1
|
||||||
|
check_count 3/3
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
# Moving onto the second match before searching again skips to the third.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X start-of-line || exit 1
|
||||||
|
$TMUX send-keys -X search-forward-text needle || exit 1
|
||||||
|
$TMUX send-keys -X cursor-down || exit 1
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 3/3
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X search-forward-text absent || exit 1
|
||||||
|
check_count /0
|
||||||
|
case "$($TMUX display-message -p '#{E:copy-mode-position-format}')" in
|
||||||
|
*'(0 matches)') ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -X search-forward-text one || exit 1
|
||||||
|
check_count 1/1
|
||||||
|
case "$($TMUX display-message -p '#{E:copy-mode-position-format}')" in
|
||||||
|
*'(1 of 1 match)') ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
# Backward searches can find an overlapping start inside a marked match.
|
||||||
|
# The index must refer to the match after the cursor moves to its beginning.
|
||||||
|
$TMUX new-window -n adjacent "printf 'aaaa'; cat" || exit 1
|
||||||
|
for keys in vi emacs; do
|
||||||
|
$TMUX set -g mode-keys "$keys" || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X search-backward-incremental -- '=aa' || exit 1
|
||||||
|
check_count 2/2
|
||||||
|
$TMUX send-keys -X search-backward-incremental -- '-aa' || exit 1
|
||||||
|
check_count 1/2
|
||||||
|
case "$($TMUX display-message -p \
|
||||||
|
'#{E:copy-mode-position-format}')" in
|
||||||
|
*'(1 of 2 matches)') ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -X search-backward-incremental -- '-aa' || exit 1
|
||||||
|
check_count 2/2
|
||||||
|
$TMUX send-keys -X search-backward-incremental -- '-aa' || exit 1
|
||||||
|
check_count 1/2
|
||||||
|
$TMUX send-keys -X search-backward-incremental -- '=' || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X search-backward-text aa || exit 1
|
||||||
|
check_count 2/2
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 1/2
|
||||||
|
$TMUX send-keys -X search-again || exit 1
|
||||||
|
check_count 2/2
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Scrolling into real history must not create a search count.
|
||||||
|
$TMUX new-window -n scroll \
|
||||||
|
'i=0; while [ $i -lt 80 ]; do echo "line $i"; i=$((i + 1)); done; cat' ||
|
||||||
|
exit 1
|
||||||
|
$TMUX copy-mode -u || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{scroll_position}')" -gt 0 ] || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX resize-window -x40 -y10 || exit 1
|
||||||
|
check_unsearched
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
|
||||||
|
format=$($TMUX show -gv copy-mode-position-format) || exit 1
|
||||||
|
case "$format" in
|
||||||
|
*'#{search_count_current} of '*'#{search_count} matches'*) ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -78,4 +78,59 @@ expected=$(printf 'alpha\nbeta\ngamma')
|
|||||||
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
||||||
fail "reversed line-mode selection did not keep complete lines"
|
fail "reversed line-mode selection did not keep complete lines"
|
||||||
|
|
||||||
|
# Word mode must initialize its reset boundaries from the existing selection,
|
||||||
|
# rather than extending it back to the origin on the next cursor movement.
|
||||||
|
for mode in emacs vi; do
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
$TMUX set-window-option -g mode-keys "$mode" || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X cursor-down || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X begin-selection || exit 1
|
||||||
|
$TMUX send-keys -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X selection-mode word || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection-no-clear -C || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = beta ] ||
|
||||||
|
fail "$mode: word-mode selection did not expand to the current word"
|
||||||
|
$TMUX send-keys -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection-no-clear -C || exit 1
|
||||||
|
expected=$(printf 'beta\ngamma')
|
||||||
|
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
||||||
|
fail "$mode: word-mode movement reset the selection to the origin"
|
||||||
|
|
||||||
|
# Reversing the character selection before changing mode must keep the
|
||||||
|
# fixed end and its containing word when the cursor moves further back.
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-down || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X begin-selection || exit 1
|
||||||
|
$TMUX send-keys -X cursor-up || exit 1
|
||||||
|
$TMUX send-keys -X selection-mode word || exit 1
|
||||||
|
$TMUX send-keys -X cursor-left || exit 1
|
||||||
|
$TMUX send-keys -X copy-selection-no-clear -C || exit 1
|
||||||
|
expected=$(printf 'alpha\nbeta\ngamma')
|
||||||
|
[ "$($TMUX show-buffer)" = "$expected" ] ||
|
||||||
|
fail "$mode: reversed word-mode selection lost its fixed end"
|
||||||
|
|
||||||
|
# A stopped selection should expand to words without moving its cursor.
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X cursor-down || exit 1
|
||||||
|
$TMUX send-keys -N2 -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X begin-selection || exit 1
|
||||||
|
$TMUX send-keys -X cursor-right || exit 1
|
||||||
|
$TMUX send-keys -X stop-selection || exit 1
|
||||||
|
before=$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')
|
||||||
|
$TMUX send-keys -X selection-mode word || exit 1
|
||||||
|
[ "$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')" = \
|
||||||
|
"$before" ] || fail "$mode: word-mode switch moved a stopped cursor"
|
||||||
|
$TMUX send-keys -X copy-selection-no-clear -C || exit 1
|
||||||
|
[ "$($TMUX show-buffer)" = beta ] ||
|
||||||
|
fail "$mode: stopped word-mode selection did not contain the whole word"
|
||||||
|
done
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -110,4 +110,92 @@ wheel 64 5 5
|
|||||||
$TMUX send-keys -X copy-selection-no-clear || exit 1
|
$TMUX send-keys -X copy-selection-no-clear || exit 1
|
||||||
expect_buffer "$extended_start"
|
expect_buffer "$extended_start"
|
||||||
|
|
||||||
|
# Perform a mouse drag across a sequence of col,row coordinates (1-based).
|
||||||
|
# SGR mouse format: \033[<btn;col;rowM (btn 0=press, 32=drag) and
|
||||||
|
# \033[<0;col;rowm (release).
|
||||||
|
drag()
|
||||||
|
{
|
||||||
|
# Mouse button 1 down (press) at the starting coordinate.
|
||||||
|
col=${1%,*}
|
||||||
|
row=${1#*,}
|
||||||
|
seq=$(printf '\033[<0;%s;%sM' "$col" "$row")
|
||||||
|
shift
|
||||||
|
# Mouse drag (button 1 held) across each subsequent coordinate.
|
||||||
|
for pos; do
|
||||||
|
col=${pos%,*}
|
||||||
|
row=${pos#*,}
|
||||||
|
seq="$seq$(printf '\033[<32;%s;%sM' "$col" "$row")"
|
||||||
|
done
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
# Keep the button held briefly so auto-scrolling can run, and
|
||||||
|
# capture copy-mode state before mouse release exits copy mode.
|
||||||
|
sleep 0.5
|
||||||
|
scroll_pos=$($TMUX display -p '#{scroll_position}')
|
||||||
|
cursor_y=$($TMUX display -p '#{copy_cursor_y}')
|
||||||
|
# Mouse button 1 up (release) at the final coordinate.
|
||||||
|
seq=$(printf '\033[<0;%s;%sm' "$col" "$row")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Turn off status line so all 10 pane rows are visible in the outer client,
|
||||||
|
# and write a 24-char line on the bottom row (row 10, longer than the 18-char
|
||||||
|
# line 79 above it).
|
||||||
|
$TMUX set -g status off || exit 1
|
||||||
|
$TMUX send-keys -X cancel || exit 1
|
||||||
|
$TMUX send-keys -l "line 80 xxxxxxxxxxxxxxxx" || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
|
||||||
|
for mode in emacs vi; do
|
||||||
|
$TMUX setw -g mode-keys "$mode" || exit 1
|
||||||
|
|
||||||
|
# In emacs mode the selection end column is exclusive, so selecting all
|
||||||
|
# characters of an 18- or 24-char line requires placing the cursor one
|
||||||
|
# column past the last character; in vi mode the end column is inclusive.
|
||||||
|
if [ "$mode" = emacs ]; then
|
||||||
|
ecol18=19
|
||||||
|
ecol24=25
|
||||||
|
else
|
||||||
|
ecol18=18
|
||||||
|
ecol24=24
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Drag along the bottom row (row 10) past the end of the line and back
|
||||||
|
# when already at the bottom of history; the selection must stay on
|
||||||
|
# line 80 instead of jumping up to line 79.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
drag 1,10 "$ecol24,10" "$((ecol24 + 1)),10" "$ecol24,10"
|
||||||
|
expect_buffer "line 80 xxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
|
# Scroll up 1 line so line 80 is off-screen below, then drag from row 9
|
||||||
|
# to the bottom row (row 10) to auto-scroll down and select lines 78..80.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X scroll-up || exit 1
|
||||||
|
drag 1,9 23,10 "$ecol24,10"
|
||||||
|
expect_buffer "$(printf 'line 78 xxxxxxxxxx\nline 79 xxxxxxxxxx\nline 80 xxxxxxxxxxxxxxxx')"
|
||||||
|
|
||||||
|
# Scroll 1 line below the top of history so line 00 is off-screen above,
|
||||||
|
# then drag from row 2 to the top row (row 1) to auto-scroll up and
|
||||||
|
# select lines 00..02.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
$TMUX send-keys -X scroll-down || exit 1
|
||||||
|
drag "$ecol18,2" 1,1
|
||||||
|
expect_buffer "$(printf 'line 00 xxxxxxxxxx\nline 01 xxxxxxxxxx\nline 02 xxxxxxxxxx')"
|
||||||
|
|
||||||
|
# Scroll up 5 lines, then drag onto the bottom row and keep the button
|
||||||
|
# held to auto-scroll all the way to the bottom.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -N5 -X scroll-up || exit 1
|
||||||
|
drag 1,9 5,10
|
||||||
|
[ "$scroll_pos" = 0 ] || fail "$mode: drag auto-scroll stopped at $scroll_pos"
|
||||||
|
|
||||||
|
# At the top of history, drag onto the top row and keep the button
|
||||||
|
# held: the cursor must stay on the top row.
|
||||||
|
$TMUX copy-mode || exit 1
|
||||||
|
$TMUX send-keys -X history-top || exit 1
|
||||||
|
drag 5,2 5,1
|
||||||
|
[ "$cursor_y" = 0 ] || fail "$mode: cursor left the top row (row $cursor_y)"
|
||||||
|
done
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
149
regress/copy-mode-word-redraw.sh
Normal file
149
regress/copy-mode-word-redraw.sh
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Word selection can extend beyond the cursor's row. Compare what an attached
|
||||||
|
# client drew with a full copy-mode repaint after extending and shrinking it.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
INNER="$TEST_TMUX -Lword-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lword-outer-$$ -f/dev/null"
|
||||||
|
DIR=$(mktemp -d) || exit 1
|
||||||
|
exit_status=0
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0
|
||||||
|
trap 'exit 1' 1 2 3 15
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_buffer()
|
||||||
|
{
|
||||||
|
$INNER send-keys -X copy-selection-no-clear || exit 1
|
||||||
|
$INNER save-buffer "$DIR/copied" || exit 1
|
||||||
|
cmp -s "$DIR/expected" "$DIR/copied" || fail 'unexpected copied text'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_redraw()
|
||||||
|
{
|
||||||
|
label=$1
|
||||||
|
sleep 0.2
|
||||||
|
$OUTER capture-pane -ep -t outer:0.0 >"$DIR/before" || exit 1
|
||||||
|
$INNER send-keys -X toggle-position || exit 1
|
||||||
|
$INNER send-keys -X toggle-position || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$OUTER capture-pane -ep -t outer:0.0 >"$DIR/after" || exit 1
|
||||||
|
if ! cmp -s "$DIR/before" "$DIR/after"; then
|
||||||
|
diff -u "$DIR/before" "$DIR/after" >&2
|
||||||
|
echo "$label: stale word-selection highlight" >&2
|
||||||
|
exit_status=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x20 -y8 'sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mode-keys emacs || exit 1
|
||||||
|
$INNER set-option -g copy-mode-position-format '' || exit 1
|
||||||
|
$INNER set-option -g copy-mode-selection-style 'fg=white,bg=red' || exit 1
|
||||||
|
$OUTER new-session -d -s outer -x20 -y8 "$INNER attach -t inner" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# A wrapped word ending on the penultimate row. In emacs mode end-of-line
|
||||||
|
# moves onto the exclusive end and extends the selection into the blank row
|
||||||
|
# below; start-of-line then removes that row from the selection.
|
||||||
|
awk 'BEGIN { print "PREFIX"; for (i = 0; i < 115; i++) printf "a"; print "" }' \
|
||||||
|
>"$DIR/text" || exit 1
|
||||||
|
$INNER new-window -t inner:1 "cat '$DIR/text'; exec sleep 100" || exit 1
|
||||||
|
$INNER resize-window -x20 -y8 || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$INNER copy-mode || exit 1
|
||||||
|
$INNER send-keys -X history-top || exit 1
|
||||||
|
$INNER send-keys -X cursor-down || exit 1
|
||||||
|
$INNER send-keys -X select-word || exit 1
|
||||||
|
$INNER send-keys -X end-of-line || exit 1
|
||||||
|
[ "$($INNER display-message -p '#{selection_end_y}')" = 7 ] ||
|
||||||
|
fail 'selection did not extend into the blank row'
|
||||||
|
check_redraw 'extend onto blank row'
|
||||||
|
awk 'BEGIN { for (i = 0; i < 115; i++) printf "a"; print "" }' \
|
||||||
|
>"$DIR/expected" || exit 1
|
||||||
|
check_buffer
|
||||||
|
$INNER send-keys -X start-of-line || exit 1
|
||||||
|
[ "$($INNER display-message -p '#{selection_end_y}')" = 6 ] ||
|
||||||
|
fail 'selection did not shrink away from the blank row'
|
||||||
|
check_redraw 'shrink away from blank row'
|
||||||
|
awk 'BEGIN { for (i = 0; i < 115; i++) printf "a" }' \
|
||||||
|
>"$DIR/expected" || exit 1
|
||||||
|
check_buffer
|
||||||
|
|
||||||
|
# The selected next word can itself wrap across several rows even though the
|
||||||
|
# cursor only moved within one row. All of those rows must be repainted.
|
||||||
|
awk 'BEGIN { print "PREFIX"; print "one";
|
||||||
|
for (i = 0; i < 65; i++) printf "b"; print ""; print "END" }' \
|
||||||
|
>"$DIR/text" || exit 1
|
||||||
|
$INNER new-window -t inner:2 "cat '$DIR/text'; exec sleep 100" || exit 1
|
||||||
|
$INNER resize-window -x20 -y8 || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$INNER copy-mode || exit 1
|
||||||
|
$INNER send-keys -X history-top || exit 1
|
||||||
|
$INNER send-keys -X cursor-down || exit 1
|
||||||
|
$INNER send-keys -X select-word || exit 1
|
||||||
|
$INNER send-keys -X end-of-line || exit 1
|
||||||
|
[ "$($INNER display-message -p '#{selection_end_y}')" = 5 ] ||
|
||||||
|
fail 'selection did not extend over the wrapped next word'
|
||||||
|
check_redraw 'extend over a wrapped next word'
|
||||||
|
awk 'BEGIN { print "one"; for (i = 0; i < 65; i++) printf "b" }' \
|
||||||
|
>"$DIR/expected" || exit 1
|
||||||
|
check_buffer
|
||||||
|
$INNER send-keys -X start-of-line || exit 1
|
||||||
|
check_redraw 'shrink away from a wrapped next word'
|
||||||
|
printf 'one' >"$DIR/expected" || exit 1
|
||||||
|
check_buffer
|
||||||
|
|
||||||
|
# Drag a word selection onto the bottom row and back while there is still
|
||||||
|
# history below the viewport. A redraw must not read the next backing row
|
||||||
|
# and paint it onto the last visible row.
|
||||||
|
awk 'BEGIN { for (i = 0; i < 30; i++) printf "line%02d abc def\n", i }' \
|
||||||
|
>"$DIR/text" || exit 1
|
||||||
|
$INNER new-window -t inner:3 "cat '$DIR/text'; exec sleep 100" || exit 1
|
||||||
|
$INNER resize-window -x20 -y8 || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER unbind-key -Tcopy-mode MouseDragEnd1Pane || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
$INNER copy-mode || exit 1
|
||||||
|
$INNER send-keys -X history-top || exit 1
|
||||||
|
$INNER send-keys -X cursor-down || exit 1
|
||||||
|
$INNER send-keys -X select-word || exit 1
|
||||||
|
$OUTER send-keys -l "$(printf '\033[<0;3;2M')" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
$OUTER send-keys -l "$(printf '\033[<32;8;7M')" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
$INNER send-keys -X selection-mode word || exit 1
|
||||||
|
$OUTER send-keys -l "$(printf '\033[<32;8;8M')" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
# Leave the edge to cancel the auto-scroll timer before comparing.
|
||||||
|
$OUTER send-keys -l "$(printf '\033[<32;8;7M')" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
$OUTER send-keys -l "$(printf '\033[<0;8;7m')" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
[ "$($INNER display-message -p '#{selection_mode}')" = word ] ||
|
||||||
|
fail 'mouse drag did not retain word selection mode'
|
||||||
|
[ "$($INNER display-message -p '#{scroll_position}')" -gt 0 ] ||
|
||||||
|
fail 'mouse drag reached the bottom of history'
|
||||||
|
check_redraw 'drag word selection at bottom of viewport'
|
||||||
|
|
||||||
|
exit "$exit_status"
|
||||||
66
regress/display-message-client.sh
Executable file
66
regress/display-message-client.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# display-message -c must take the client formats from the given client, even
|
||||||
|
# when it is attached to a different session from the target pane.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
LC_ALL=C.UTF-8
|
||||||
|
export PATH TERM LC_ALL
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
INNER="$TEST_TMUX -LtestI$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -LtestO$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
$INNER new-session -d -s one -x40 -y10 'exec sleep 100' || exit 1
|
||||||
|
$INNER new-session -d -s two -x40 -y10 'exec sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
|
||||||
|
# Each client has a distinct TERM so that client_termname identifies it.
|
||||||
|
attach()
|
||||||
|
{
|
||||||
|
echo "env -i PATH=/bin:/usr/bin TERM=$1 LC_ALL=C.UTF-8 $TEST_TMUX -LtestI$$ -f/dev/null attach-session -t $2"
|
||||||
|
}
|
||||||
|
$OUTER new-session -d -s outer -x40 -y10 "$(attach screen one)" || exit 1
|
||||||
|
$OUTER new-window -d "$(attach xterm two)" || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
n=$($INNER list-clients -F '#{client_name}' 2>/dev/null|wc -l)
|
||||||
|
[ "$n" -eq 2 ] && break
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
[ "$i" -lt 50 ] || fail "clients did not attach"
|
||||||
|
|
||||||
|
c1=$($INNER list-clients -F '#{client_name}' -f '#{==:#{client_termname},screen}')
|
||||||
|
c2=$($INNER list-clients -F '#{client_name}' -f '#{==:#{client_termname},xterm}')
|
||||||
|
[ -n "$c1" ] && [ -n "$c2" ] || fail "could not find both clients"
|
||||||
|
|
||||||
|
# The target is in session two, but the client is the one in session one.
|
||||||
|
term=$($INNER display-message -c "$c1" -t two: -p '#{client_termname}')
|
||||||
|
[ "$term" = screen ] || fail "client_termname was '$term', not screen"
|
||||||
|
|
||||||
|
# The session formats still come from the target.
|
||||||
|
session=$($INNER display-message -c "$c1" -t two: -p '#{session_name}')
|
||||||
|
[ "$session" = two ] || fail "session_name was '$session', not two"
|
||||||
|
|
||||||
|
term=$($INNER display-message -c "$c2" -t one: -p '#{client_termname}')
|
||||||
|
[ "$term" = xterm ] || fail "client_termname was '$term', not xterm"
|
||||||
|
|
||||||
|
exit 0
|
||||||
135
regress/floating-pane-border-clients.sh
Executable file
135
regress/floating-pane-border-clients.sh
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Damage-only redraws must evaluate both active and inactive border styles
|
||||||
|
# for each client, even though the cached border cells belong to the pane.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lborder-style-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lborder-style-outer-$$ -f/dev/null"
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
|
||||||
|
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_clients()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
count=$($INNER list-clients 2>/dev/null | wc -l)
|
||||||
|
[ "$count" -eq 2 ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "two inner clients did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_marker()
|
||||||
|
{
|
||||||
|
target=$1
|
||||||
|
marker=$2
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t "$target" >"$CAPTURE" || exit 1
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "client in $target did not receive $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$DIR/emitter.pl" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
for my $phase (1 .. 2) {
|
||||||
|
while (!-e "$ENV{TRIGGER}-$phase") {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
# Change an unused palette entry and acknowledge it in the pane body.
|
||||||
|
print "\e]4;200;rgb:11/22/0$phase\a\e[1;1HDAMAGE$phase";
|
||||||
|
}
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 60 -y 20 \
|
||||||
|
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||||
|
$INNER set -g status off || exit 1
|
||||||
|
$INNER set -g window-size manual || exit 1
|
||||||
|
$INNER set -g automatic-rename off || exit 1
|
||||||
|
$INNER set -g status-interval 0 || exit 1
|
||||||
|
$INNER set -g pane-border-lines simple || exit 1
|
||||||
|
$INNER set -g pane-border-status top || exit 1
|
||||||
|
$INNER set -g pane-border-format 'CLIENT=#{client_name}' || exit 1
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 35 -y 6 -X 5 -Y 5 \
|
||||||
|
'sleep 100') || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 121 -y 20 'sleep 100' || exit 1
|
||||||
|
$OUTER set -g status off || exit 1
|
||||||
|
$OUTER set -g window-size manual || exit 1
|
||||||
|
$OUTER set -g default-terminal screen || exit 1
|
||||||
|
LEFT=$($OUTER display-message -p -t outer:0.0 '#{pane_id}') || exit 1
|
||||||
|
RIGHT=$($OUTER split-window -h -PF '#{pane_id}' 'sleep 100') || exit 1
|
||||||
|
for target in "$LEFT" "$RIGHT"; do
|
||||||
|
$OUTER respawn-pane -k -t "$target" "$INNER attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
done
|
||||||
|
wait_for_clients
|
||||||
|
|
||||||
|
NAME1=$($OUTER display-message -p -t "$LEFT" '#{pane_tty}') || exit 1
|
||||||
|
NAME2=$($OUTER display-message -p -t "$RIGHT" '#{pane_tty}') || exit 1
|
||||||
|
STYLE="fg=#{?#{==:#{client_name},$NAME1},red,blue}"
|
||||||
|
$INNER set -g pane-border-style "$STYLE" || exit 1
|
||||||
|
$INNER set -g pane-active-border-style "$STYLE" || exit 1
|
||||||
|
|
||||||
|
# Capture just the floating pane's rows, excluding the acknowledgement in
|
||||||
|
# the tiled pane. Exercise each cache by changing the floating pane's focus.
|
||||||
|
phase=1
|
||||||
|
while [ "$phase" -le 2 ]; do
|
||||||
|
if [ "$phase" -eq 2 ]; then
|
||||||
|
$INNER select-pane -t "$FLOAT" || exit 1
|
||||||
|
fi
|
||||||
|
$INNER refresh-client -t "$NAME1" || exit 1
|
||||||
|
$INNER refresh-client -t "$NAME2" || exit 1
|
||||||
|
sleep 0.5
|
||||||
|
for target in "$LEFT" "$RIGHT"; do
|
||||||
|
$OUTER capture-pane -pe -S 5 -E 10 -t "$target" \
|
||||||
|
>"$DIR/before-$target" || exit 1
|
||||||
|
done
|
||||||
|
RED=$(printf '\033[31m')
|
||||||
|
BLUE=$(printf '\033[34m')
|
||||||
|
grep -Fq "$RED" "$DIR/before-$LEFT" || fail "missing red border"
|
||||||
|
grep -Fq "$BLUE" "$DIR/before-$RIGHT" || fail "missing blue border"
|
||||||
|
|
||||||
|
: >"$DIR/trigger-$phase"
|
||||||
|
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||||
|
wait_for_marker "$RIGHT" "DAMAGE$phase"
|
||||||
|
sleep 0.2
|
||||||
|
for target in "$LEFT" "$RIGHT"; do
|
||||||
|
$OUTER capture-pane -pe -S 5 -E 10 -t "$target" \
|
||||||
|
>"$CAPTURE" || exit 1
|
||||||
|
diff -u "$DIR/before-$target" "$CAPTURE" ||
|
||||||
|
fail "phase $phase changed $target's border style"
|
||||||
|
done
|
||||||
|
phase=$((phase + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
88
regress/floating-pane-drag-composite.sh
Executable file
88
regress/floating-pane-drag-composite.sh
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Each drag motion must draw the floating pane content once.
|
||||||
|
|
||||||
|
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
|
||||||
|
cd "$DIR" || exit 1
|
||||||
|
INNER="$TEST_TMUX -Ldoublecomp-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Ldoublecomp-outer-$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
cd /
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
mouse()
|
||||||
|
{
|
||||||
|
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||||
|
sleep 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 40 -y 15 "printf '\\033[15;1HOUTSIDE'; exec sleep 100" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER set-option -g status-interval 0 || exit 1
|
||||||
|
$INNER set-option -g automatic-rename off || exit 1
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X 5 -Y 2 \
|
||||||
|
"printf 'DRAGMARK'; exec sleep 100") || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 15 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Ldoublecomp-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
GRABCOL=$((XOFF + 3))
|
||||||
|
BORDERROW=$YOFF
|
||||||
|
|
||||||
|
# Begin capture after focus changes from the initial mouse press have settled.
|
||||||
|
mouse 0 "$GRABCOL" "$BORDERROW" M
|
||||||
|
$OUTER pipe-pane -O -t outer:0.0 "cat >'$DIR/output'" || exit 1
|
||||||
|
$INNER refresh-client || exit 1
|
||||||
|
sleep 0.5
|
||||||
|
grep -aq DRAGMARK "$DIR/output" || fail "capture missed floating pane content"
|
||||||
|
grep -aq OUTSIDE "$DIR/output" || fail "capture missed untouched row"
|
||||||
|
offset=$(wc -c <"$DIR/output")
|
||||||
|
|
||||||
|
i=0
|
||||||
|
steps=6
|
||||||
|
while [ "$i" -lt "$steps" ]; do
|
||||||
|
GRABCOL=$((GRABCOL + 1))
|
||||||
|
mouse 32 "$GRABCOL" "$BORDERROW" M
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
mouse 0 "$GRABCOL" "$BORDERROW" m
|
||||||
|
sleep 0.3
|
||||||
|
|
||||||
|
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
[ "$NEWXOFF" -eq "$((XOFF + steps))" ] || fail "floating pane did not move six columns"
|
||||||
|
tail -c +"$((offset + 1))" "$DIR/output" >"$DIR/drag-output"
|
||||||
|
n=$(perl -0777 -ne '$n = () = /DRAGMARK/g; print "$n\n"' "$DIR/drag-output")
|
||||||
|
[ "$n" -eq "$steps" ] ||
|
||||||
|
fail "$steps drag motions wrote the floating pane content $n times"
|
||||||
|
|
||||||
|
exit 0
|
||||||
108
regress/floating-pane-drag-ghost.sh
Executable file
108
regress/floating-pane-drag-ghost.sh
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Regression test for a floating-pane drag bug: cmd_resize_pane_redraw_floating()
|
||||||
|
# (cmd-resize-pane.c) reported damage for just a dragged floating pane's
|
||||||
|
# content rectangle, not the one-cell border frame drawn around it (see the
|
||||||
|
# "floating" case in screen-redraw.c, which draws that frame at
|
||||||
|
# xoff-1/yoff-1 through xoff+sx/yoff+sy - one cell outside the pane's own
|
||||||
|
# content area). Damage scoped to only the content area left the frame's
|
||||||
|
# previous position undrawn as the pane moved, so dragging it left a trail
|
||||||
|
# of un-erased border frames behind - visible as several "corners" stacked
|
||||||
|
# up rather than just the pane's current one.
|
||||||
|
#
|
||||||
|
# This bug has nothing to do with images - it reproduces with a plain
|
||||||
|
# floating pane and no image support required.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server >/dev/null 2>&1
|
||||||
|
$TMUX2 kill-server >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# drag STARTCOL STARTROW ENDCOL ENDROW
|
||||||
|
#
|
||||||
|
# Write a plain (unmodified) SGR button-1 press, drag update and release at
|
||||||
|
# 1-based positions to the outer pane holding the inner client - this
|
||||||
|
# matches the default MouseDown1Border/MouseDrag1Border bindings used to
|
||||||
|
# move or resize a floating pane by its border.
|
||||||
|
drag()
|
||||||
|
{
|
||||||
|
scol="$1"
|
||||||
|
srow="$2"
|
||||||
|
ecol="$3"
|
||||||
|
erow="$4"
|
||||||
|
|
||||||
|
seq=$(printf '\033[<0;%s;%sM' "$scol" "$srow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<32;%s;%sM' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<0;%s;%sm' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
trap "cleanup; rm -f $TMP" 0 1 15
|
||||||
|
|
||||||
|
$TMUX new-session -d -s inner -x 60 -y 20 'sh -c "sleep 100"' || exit 1
|
||||||
|
$TMUX set -g mouse on
|
||||||
|
$TMUX set -g default-command 'sh -c "sleep 100"'
|
||||||
|
|
||||||
|
FLOAT=$($TMUX new-pane -d -PF '#{pane_id}' -x 16 -y 5 -X 5 -Y 5) ||
|
||||||
|
fail "new-pane -X -Y failed"
|
||||||
|
FTOP=$($TMUX display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
FLEFT=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
FWIDTH=$($TMUX display-message -p -t "$FLOAT" '#{pane_width}')
|
||||||
|
|
||||||
|
$TMUX2 new-session -d -x 60 -y 20 "$TMUX attach -t inner" || exit 1
|
||||||
|
sleep 1
|
||||||
|
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||||
|
[ -n "$OUTER" ] || fail "No outer pane."
|
||||||
|
|
||||||
|
# Sanity check: exactly one floating pane, so exactly one top-left corner,
|
||||||
|
# before dragging anything.
|
||||||
|
$TMUX2 capturep -p -t "$OUTER" >$TMP || fail "capture failed"
|
||||||
|
n=$(grep -o '┌' $TMP | wc -l)
|
||||||
|
[ "$n" -eq 1 ] || fail "sanity: expected 1 corner before drag, found $n"
|
||||||
|
|
||||||
|
# Drag the floating pane by its top border (row FTOP-1, some column within
|
||||||
|
# its width) down several rows in a few separate steps, then release. A
|
||||||
|
# single drag() call already does press/motion/release, so call it several
|
||||||
|
# times in a row to simulate a multi-step real drag.
|
||||||
|
GRABCOL=$((FLEFT + FWIDTH / 2))
|
||||||
|
STARTROW=$FTOP
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 6 ]; do
|
||||||
|
newrow=$((STARTROW + i + 1))
|
||||||
|
drag $((GRABCOL + 1)) $((STARTROW + i)) $((GRABCOL + 1)) $newrow
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
$TMUX2 capturep -p -t "$OUTER" >$TMP || fail "capture failed"
|
||||||
|
|
||||||
|
# Exactly one top-left corner should remain - the pane's current position.
|
||||||
|
# This is expected to fail before the fix: multiple corners (a trail of
|
||||||
|
# un-erased frames) would remain from the intermediate drag positions.
|
||||||
|
n=$(grep -o '┌' $TMP | wc -l)
|
||||||
|
[ "$n" -eq 1 ] || fail "expected exactly 1 corner after drag, found $n (ghost frames left behind)"
|
||||||
|
|
||||||
|
exit 0
|
||||||
160
regress/floating-pane-drag-sb-strip.sh
Executable file
160
regress/floating-pane-drag-sb-strip.sh
Executable file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Regression test: dragging a floating pane across another pane's ordinary
|
||||||
|
# content must not redraw that other pane's scrollbar, unless the drag
|
||||||
|
# actually crosses the scrollbar's own strip.
|
||||||
|
#
|
||||||
|
# cmd_resize_pane_redraw_floating() (cmd-resize-pane.c) used to flag
|
||||||
|
# PANE_REDRAWSCROLLBAR on any pane whose whole *body* intersected the
|
||||||
|
# floating pane's old or new rectangle, rather than just its narrow
|
||||||
|
# scrollbar strip - so dragging a floating pane back and forth over an
|
||||||
|
# ordinary tiled pane's content (never touching its scrollbar) still
|
||||||
|
# needlessly redrew that pane's scrollbar on every motion step. See
|
||||||
|
# tmux-image-redraw-known-bugs.md for the full write-up.
|
||||||
|
#
|
||||||
|
# This is checked by giving the non-dragged pane a distinctive scrollbar
|
||||||
|
# colour and counting how many times its SGR code appears in the client's
|
||||||
|
# raw output while the floating pane is dragged vertically over that pane's
|
||||||
|
# body, well clear of its scrollbar column: with the fix, it should never
|
||||||
|
# reappear after the initial draw.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$TMUX kill-server >/dev/null 2>&1
|
||||||
|
$TMUX2 kill-server >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# drag STARTCOL STARTROW ENDCOL ENDROW
|
||||||
|
drag()
|
||||||
|
{
|
||||||
|
scol="$1"
|
||||||
|
srow="$2"
|
||||||
|
ecol="$3"
|
||||||
|
erow="$4"
|
||||||
|
|
||||||
|
seq=$(printf '\033[<0;%s;%sM' "$scol" "$srow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<32;%s;%sM' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.2
|
||||||
|
seq=$(printf '\033[<0;%s;%sm' "$ecol" "$erow")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
TMP=$(mktemp)
|
||||||
|
trap "cleanup; rm -f $TMP" 0 1 15
|
||||||
|
|
||||||
|
$TMUX new-session -d -s inner -x 60 -y 20 'sh -c "sleep 100"' || exit 1
|
||||||
|
$TMUX set -g status-right '' || fail "set status-right failed"
|
||||||
|
$TMUX set -g mouse on || fail "set mouse failed"
|
||||||
|
$TMUX set -g default-command 'sh -c "sleep 100"' || fail "set default-command failed"
|
||||||
|
$TMUX set -g pane-scrollbars on || fail "set pane-scrollbars failed"
|
||||||
|
|
||||||
|
$TMUX split-window -h -t inner 'sh -c "sleep 100"' || fail "split-window failed"
|
||||||
|
|
||||||
|
PANES=$($TMUX list-panes -t inner -F '#{pane_id} #{pane_left}')
|
||||||
|
LEFT=$(echo "$PANES" | sort -k2 -n | head -1 | cut -d' ' -f1)
|
||||||
|
[ -n "$LEFT" ] || fail "could not identify left pane"
|
||||||
|
|
||||||
|
# A distinctive scrollbar colour for the non-dragged (left) pane only.
|
||||||
|
$TMUX set -p -t "$LEFT" pane-scrollbars-style 'fg=colour201,bg=colour17' ||
|
||||||
|
fail "set pane-scrollbars-style failed"
|
||||||
|
|
||||||
|
ALEFT=$($TMUX display-message -p -t "$LEFT" '#{pane_left}')
|
||||||
|
ATOP=$($TMUX display-message -p -t "$LEFT" '#{pane_top}')
|
||||||
|
AWIDTH=$($TMUX display-message -p -t "$LEFT" '#{pane_width}')
|
||||||
|
AHEIGHT=$($TMUX display-message -p -t "$LEFT" '#{pane_height}')
|
||||||
|
[ "$AWIDTH" -gt 15 ] || fail "left pane too narrow for this test ($AWIDTH)"
|
||||||
|
|
||||||
|
# A small floating pane placed well inside the left pane's content area,
|
||||||
|
# clear of its (right-hand) scrollbar column by several columns.
|
||||||
|
FLOAT=$($TMUX new-pane -d -PF '#{pane_id}' -x 8 -y 5 \
|
||||||
|
-X $((ALEFT + 2)) -Y $((ATOP + 2))) || fail "new-pane -X -Y failed"
|
||||||
|
FTOP=$($TMUX display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
FLEFT=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
FWIDTH=$($TMUX display-message -p -t "$FLOAT" '#{pane_width}')
|
||||||
|
[ $((FLEFT + FWIDTH + 3)) -lt $((ALEFT + AWIDTH)) ] ||
|
||||||
|
fail "sanity: floating pane too close to the scrollbar column"
|
||||||
|
|
||||||
|
# Start the outer session with a plain shell, then start capturing before
|
||||||
|
# triggering the attach - starting the attach as the outer pane's initial
|
||||||
|
# command would mean pipe-pane only starts after the attach-driven initial
|
||||||
|
# redraw (which draws the scrollbars) has already happened, missing it.
|
||||||
|
$TMUX2 new-session -d -x 60 -y 20 || exit 1
|
||||||
|
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||||
|
[ -n "$OUTER" ] || fail "No outer pane."
|
||||||
|
$TMUX2 pipe-pane -t "$OUTER" -O "cat >$TMP" || fail "pipe-pane failed"
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$TMUX attach -t inner" || fail "send attach failed"
|
||||||
|
$TMUX2 send-keys -t "$OUTER" Enter || fail "send enter failed"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Sanity check: the distinctive scrollbar colour reaches the client at all.
|
||||||
|
grep -qa '48;5;201' $TMP || fail "sanity: scrollbar colour never reached the client"
|
||||||
|
: >$TMP
|
||||||
|
|
||||||
|
# Drag the floating pane straight up and down by its top border, staying at
|
||||||
|
# a fixed column the whole time - this never crosses the left pane's
|
||||||
|
# scrollbar strip, only its ordinary content.
|
||||||
|
GRABCOL=$((FLEFT + FWIDTH / 2))
|
||||||
|
row=$FTOP
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 6 ]; do
|
||||||
|
newrow=$((row + 1))
|
||||||
|
drag $GRABCOL $row $GRABCOL $newrow
|
||||||
|
row=$newrow
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 6 ]; do
|
||||||
|
newrow=$((row - 1))
|
||||||
|
drag $GRABCOL $row $GRABCOL $newrow
|
||||||
|
row=$newrow
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# The scrollbar colour should never reappear - its geometry never changed,
|
||||||
|
# and the drag never crossed its column. This is expected to fail before
|
||||||
|
# the fix - see the header comment.
|
||||||
|
n=$(grep -ac '48;5;201' $TMP)
|
||||||
|
[ "$n" -eq 0 ] ||
|
||||||
|
fail "left pane's scrollbar was redrawn $n times while dragging over its body only"
|
||||||
|
|
||||||
|
# Cross the scrollbar and return, comparing each damaged redraw with a full
|
||||||
|
# refresh to check that covering and uncovering its cells leaves no artifacts.
|
||||||
|
offset=$((ALEFT + AWIDTH - FLEFT - FWIDTH / 2))
|
||||||
|
for delta in "$offset" "$((-offset))"; do
|
||||||
|
oldleft=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
newcol=$((GRABCOL + delta))
|
||||||
|
drag "$GRABCOL" "$row" "$newcol" "$row"
|
||||||
|
GRABCOL=$newcol
|
||||||
|
newleft=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
[ "$newleft" -eq "$((oldleft + delta))" ] ||
|
||||||
|
fail "floating pane did not move across the scrollbar as expected"
|
||||||
|
before=$($TMUX2 capture-pane -pe -t "$OUTER" -S 0 -E "$((AHEIGHT - 1))")
|
||||||
|
$TMUX refresh-client || fail "refresh-client failed"
|
||||||
|
sleep 0.5
|
||||||
|
after=$($TMUX2 capture-pane -pe -t "$OUTER" -S 0 -E "$((AHEIGHT - 1))")
|
||||||
|
[ "$before" = "$after" ] ||
|
||||||
|
fail "scrollbar crossing differs from a full redraw"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
93
regress/floating-pane-drag-sync.sh
Executable file
93
regress/floating-pane-drag-sync.sh
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Synchronized drag output must not cause a full redraw of untouched rows.
|
||||||
|
|
||||||
|
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
|
||||||
|
cd "$DIR" || exit 1
|
||||||
|
INNER="$TEST_TMUX -Lsyncdefer-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lsyncdefer-outer-$$ -f/dev/null"
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
cd /
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
mouse()
|
||||||
|
{
|
||||||
|
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||||
|
sleep 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 40 -y 15 "printf '\\033[15;1HOUTSIDE'; exec sleep 100" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER set-option -g status-interval 0 || exit 1
|
||||||
|
$INNER set-option -g automatic-rename off || exit 1
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X 5 -Y 2 \
|
||||||
|
"printf 'DRAGMARK'; exec sleep 100") || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 15 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$INNER set-option -as terminal-features ',screen-256color:sync' || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lsyncdefer-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
GRABCOL=$((XOFF + 3))
|
||||||
|
BORDERROW=$YOFF
|
||||||
|
|
||||||
|
# Begin capture after focus changes from the initial mouse press have settled.
|
||||||
|
mouse 0 "$GRABCOL" "$BORDERROW" M
|
||||||
|
$OUTER pipe-pane -O -t outer:0.0 "cat >'$DIR/output'" || exit 1
|
||||||
|
$INNER refresh-client || exit 1
|
||||||
|
sleep 0.5
|
||||||
|
grep -aq DRAGMARK "$DIR/output" || fail "capture missed floating pane content"
|
||||||
|
grep -aq OUTSIDE "$DIR/output" || fail "capture missed untouched row"
|
||||||
|
offset=$(wc -c <"$DIR/output")
|
||||||
|
|
||||||
|
i=0
|
||||||
|
steps=6
|
||||||
|
while [ "$i" -lt "$steps" ]; do
|
||||||
|
GRABCOL=$((GRABCOL + 1))
|
||||||
|
mouse 32 "$GRABCOL" "$BORDERROW" M
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
mouse 0 "$GRABCOL" "$BORDERROW" m
|
||||||
|
sleep 0.3
|
||||||
|
|
||||||
|
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
[ "$NEWXOFF" -eq "$((XOFF + steps))" ] || fail "floating pane did not move six columns"
|
||||||
|
tail -c +"$((offset + 1))" "$DIR/output" >"$DIR/drag-output"
|
||||||
|
n=$(perl -0777 -ne '$n = () = /DRAGMARK/g; print "$n\n"' "$DIR/drag-output")
|
||||||
|
[ "$n" -ge "$steps" ] || fail "drag output did not reach the client"
|
||||||
|
perl -0777 -ne 'exit(/\e\[\?2026h/ ? 0 : 1)' "$DIR/drag-output" ||
|
||||||
|
fail "drag did not use synchronized output"
|
||||||
|
if grep -aq OUTSIDE "$DIR/drag-output"; then
|
||||||
|
fail "synchronized drag redrew an untouched row"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
200
regress/floating-pane-drag-wide-character.sh
Executable file
200
regress/floating-pane-drag-wide-character.sh
Executable file
@@ -0,0 +1,200 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Damage at a floating pane's vacated edge must always redraw a complete
|
||||||
|
# grid character. redraw_damage_grow_span_clip() (screen-redraw.c) widens a
|
||||||
|
# damage rectangle's left edge by one cell whenever it isn't already at the
|
||||||
|
# span's own edge, to pull in a wide character's base half when the edge
|
||||||
|
# lands on its padding half - but it did this unconditionally, with no
|
||||||
|
# check of which half it was actually touching. When the edge instead
|
||||||
|
# already lands cleanly on a fresh character's base cell, growing left
|
||||||
|
# walks into the *previous*, unrelated character's padding cell and blanks
|
||||||
|
# it (tty_draw_line() treats any leading padding cell as proof its own
|
||||||
|
# range starts mid-character).
|
||||||
|
#
|
||||||
|
# This is a general damage-composition bug, not specific to any one kind of
|
||||||
|
# pane, but the exact column parity needs to be deterministic to actually
|
||||||
|
# catch it (a lucky parity draws fine). This constructs it by creating the
|
||||||
|
# floating pane, checking its real position (the border-framing offset
|
||||||
|
# added to -X is not something to hand-compute), and recreating it one
|
||||||
|
# column over if necessary until the vacated rectangle's left edge lands on
|
||||||
|
# a base cell.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lwidechar-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lwidechar-outer-$$ -f/dev/null"
|
||||||
|
EMITTER=$DIR/emitter.pl
|
||||||
|
BASE=$DIR/base
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
FLOAT=
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&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_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
[ -n "$CLIENT" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_outer_has()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "outer client did not show $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
slice_columns()
|
||||||
|
{
|
||||||
|
# Extract terminal columns [COL1, COL2) from lines [ROW1, ROW2] of
|
||||||
|
# $1, decoding UTF-8 - the pane's own new position (well clear of
|
||||||
|
# this range) must not affect the comparison, so this only looks at
|
||||||
|
# the narrow strip actually vacated, not the whole line. capture-pane
|
||||||
|
# text has one decoded character per double-width cell pair (every
|
||||||
|
# character here is width 2), so terminal columns are converted to
|
||||||
|
# character indices by halving before slicing.
|
||||||
|
perl -CSD -e '
|
||||||
|
my ($row1, $row2, $col1, $col2, $file) = @ARGV;
|
||||||
|
open my $fh, "<:encoding(UTF-8)", $file or die $!;
|
||||||
|
my @lines = <$fh>;
|
||||||
|
my $c1 = int($col1 / 2);
|
||||||
|
my $c2 = int(($col2 + 1) / 2);
|
||||||
|
for my $n ($row1 .. $row2) {
|
||||||
|
my $line = $lines[$n - 1];
|
||||||
|
$line =~ s/\R\z//;
|
||||||
|
print substr($line, $c1, $c2 - $c1), "\n";
|
||||||
|
}
|
||||||
|
' "$ROW1" "$ROW2" "$COL1" "$COL2" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_old_rows_restored()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
slice_columns "$BASE" >"$DIR/want"
|
||||||
|
slice_columns "$CAPTURE" >"$DIR/got"
|
||||||
|
cmp -s "$DIR/want" "$DIR/got" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "wide characters under the floating pane's vacated edge were not restored"
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse()
|
||||||
|
{
|
||||||
|
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$EMITTER" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
binmode STDOUT, ':encoding(UTF-8)';
|
||||||
|
$| = 1;
|
||||||
|
for my $row (1 .. 10) {
|
||||||
|
print "\e[$row;1H", chr(0x754c) x 20;
|
||||||
|
}
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 40 -y 10 "perl '$EMITTER'" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER set-option -g pane-scrollbars off || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lwidechar-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
wait_for_client
|
||||||
|
wait_outer_has '界界界'
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$BASE" || exit 1
|
||||||
|
|
||||||
|
# Create the floating pane, then check its actual resulting position. Try
|
||||||
|
# adjacent starting columns until the vacated rectangle's left edge
|
||||||
|
# (xoff - 1) lands on an even (base-cell) column - the odd case is the one
|
||||||
|
# every earlier manual test happened to land on by chance.
|
||||||
|
startx=5
|
||||||
|
tries=0
|
||||||
|
while [ "$tries" -lt 2 ]; do
|
||||||
|
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 12 -y 3 -X "$startx" \
|
||||||
|
-Y 5 'sh -c "printf FLOATMARK; exec sleep 100"') || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
oldleft=$((XOFF - 1))
|
||||||
|
if [ $((oldleft % 2)) -eq 0 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
startx=$((startx + 1))
|
||||||
|
tries=$((tries + 1))
|
||||||
|
done
|
||||||
|
[ $(((XOFF - 1) % 2)) -eq 0 ] || fail "could not find bad-parity starting column"
|
||||||
|
|
||||||
|
wait_outer_has FLOATMARK
|
||||||
|
|
||||||
|
ROW1=$((YOFF + 1))
|
||||||
|
ROW2=$((YOFF + 3))
|
||||||
|
COL1=$((XOFF - 4))
|
||||||
|
COL2=$((XOFF + 4))
|
||||||
|
|
||||||
|
# Grab the pane's top border a couple of columns in (avoiding the corner
|
||||||
|
# cells) and drag it well clear of its old rectangle.
|
||||||
|
GRABCOL=$((XOFF + 3))
|
||||||
|
BORDERROW=$((YOFF))
|
||||||
|
|
||||||
|
seq=$(printf '\033[<0;%s;%sM' "$GRABCOL" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
seq=$(printf '\033[<32;%s;%sM' "$((GRABCOL + 15))" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
seq=$(printf '\033[<0;%s;%sm' "$((GRABCOL + 15))" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
|
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
[ "$NEWXOFF" != "$XOFF" ] || fail "sanity: floating pane did not move (still at $XOFF)"
|
||||||
|
|
||||||
|
wait_old_rows_restored
|
||||||
|
|
||||||
|
exit 0
|
||||||
206
regress/floating-pane-drag-wide-right.sh
Executable file
206
regress/floating-pane-drag-wide-right.sh
Executable file
@@ -0,0 +1,206 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Mirror of floating-pane-drag-wide-character.sh for the *right* edge of a
|
||||||
|
# damage rectangle. redraw_damage_grow_span_clip() (screen-redraw.c) widens
|
||||||
|
# a damage rectangle's right edge by one cell whenever it isn't already at
|
||||||
|
# the span's own edge, to pull in a wide character's base half when the
|
||||||
|
# edge lands on its padding half - but it did this unconditionally, with no
|
||||||
|
# check of which half it was actually touching. When the edge instead
|
||||||
|
# already lands cleanly on a fresh character's base cell (a character fully
|
||||||
|
# outside the range), growing right pulls in just that base cell -
|
||||||
|
# tty_draw_line() sees it can't fit that character's full width in the
|
||||||
|
# remaining range (tty_draw_line_get_empty()'s gc->data.width > nx check)
|
||||||
|
# and clears it, exactly as the left-edge bug cleared a neighbouring
|
||||||
|
# character's padding half.
|
||||||
|
#
|
||||||
|
# As with the left-edge test, the exact column parity needs to be
|
||||||
|
# deterministic to actually catch it. This constructs it by creating the
|
||||||
|
# floating pane, checking its real position, and recreating it one column
|
||||||
|
# over if necessary until the vacated rectangle's right edge lands on a
|
||||||
|
# base cell.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lwidecharr-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lwidecharr-outer-$$ -f/dev/null"
|
||||||
|
EMITTER=$DIR/emitter.pl
|
||||||
|
BASE=$DIR/base
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
FLOAT=
|
||||||
|
PANEWIDTH=12
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&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_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
[ -n "$CLIENT" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_outer_has()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "outer client did not show $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
slice_columns()
|
||||||
|
{
|
||||||
|
# Extract terminal columns [COL1, COL2) from lines [ROW1, ROW2] of
|
||||||
|
# $1, decoding UTF-8 - the pane's own new position (well clear of
|
||||||
|
# this range) must not affect the comparison, so this only looks at
|
||||||
|
# the narrow strip actually vacated, not the whole line. capture-pane
|
||||||
|
# text has one decoded character per double-width cell pair (every
|
||||||
|
# character here is width 2), so terminal columns are converted to
|
||||||
|
# character indices by halving before slicing.
|
||||||
|
perl -CSD -e '
|
||||||
|
my ($row1, $row2, $col1, $col2, $file) = @ARGV;
|
||||||
|
open my $fh, "<:encoding(UTF-8)", $file or die $!;
|
||||||
|
my @lines = <$fh>;
|
||||||
|
my $c1 = int($col1 / 2);
|
||||||
|
my $c2 = int(($col2 + 1) / 2);
|
||||||
|
for my $n ($row1 .. $row2) {
|
||||||
|
my $line = $lines[$n - 1];
|
||||||
|
$line =~ s/\R\z//;
|
||||||
|
print substr($line, $c1, $c2 - $c1), "\n";
|
||||||
|
}
|
||||||
|
' "$ROW1" "$ROW2" "$COL1" "$COL2" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_old_rows_restored()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
slice_columns "$BASE" >"$DIR/want"
|
||||||
|
slice_columns "$CAPTURE" >"$DIR/got"
|
||||||
|
cmp -s "$DIR/want" "$DIR/got" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "wide characters under the floating pane's vacated right edge were not restored"
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse()
|
||||||
|
{
|
||||||
|
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$EMITTER" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
binmode STDOUT, ':encoding(UTF-8)';
|
||||||
|
$| = 1;
|
||||||
|
for my $row (1 .. 10) {
|
||||||
|
print "\e[$row;1H", chr(0x754c) x 20;
|
||||||
|
}
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 40 -y 10 "perl '$EMITTER'" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER set-option -g pane-scrollbars off || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lwidecharr-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
wait_for_client
|
||||||
|
wait_outer_has '界界界'
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$BASE" || exit 1
|
||||||
|
|
||||||
|
# Create the floating pane, then check its actual resulting position. Try
|
||||||
|
# adjacent starting columns until the vacated rectangle's right edge
|
||||||
|
# (xoff + PANEWIDTH + 1, the border-grown exclusive end - see
|
||||||
|
# window_pane_damage_floating(), window.c) lands on an even (base-cell)
|
||||||
|
# column.
|
||||||
|
startx=5
|
||||||
|
tries=0
|
||||||
|
while [ "$tries" -lt 2 ]; do
|
||||||
|
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x "$PANEWIDTH" -y 3 \
|
||||||
|
-X "$startx" -Y 5 \
|
||||||
|
'sh -c "printf FLOATMARK; exec sleep 100"') || exit 1
|
||||||
|
sleep 0.2
|
||||||
|
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
oldright=$((XOFF + PANEWIDTH + 1))
|
||||||
|
if [ $((oldright % 2)) -eq 0 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
startx=$((startx + 1))
|
||||||
|
tries=$((tries + 1))
|
||||||
|
done
|
||||||
|
[ $(((XOFF + PANEWIDTH + 1) % 2)) -eq 0 ] ||
|
||||||
|
fail "could not find bad-parity starting column"
|
||||||
|
|
||||||
|
wait_outer_has FLOATMARK
|
||||||
|
|
||||||
|
ROW1=$((YOFF + 1))
|
||||||
|
ROW2=$((YOFF + 3))
|
||||||
|
COL1=$((XOFF + PANEWIDTH - 2))
|
||||||
|
COL2=$((XOFF + PANEWIDTH + 2))
|
||||||
|
|
||||||
|
# Grab the pane's top border a couple of columns in (avoiding the corner
|
||||||
|
# cells) and drag it well clear of its old rectangle - far enough right
|
||||||
|
# that its new position starts past the checked columns above (which sit
|
||||||
|
# just past the pane's *original* right edge).
|
||||||
|
GRABCOL=$((XOFF + 3))
|
||||||
|
BORDERROW=$((YOFF))
|
||||||
|
|
||||||
|
seq=$(printf '\033[<0;%s;%sM' "$GRABCOL" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
seq=$(printf '\033[<32;%s;%sM' "$((GRABCOL + 20))" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
seq=$(printf '\033[<0;%s;%sm' "$((GRABCOL + 20))" "$BORDERROW")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
|
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
[ "$NEWXOFF" != "$XOFF" ] || fail "sanity: floating pane did not move (still at $XOFF)"
|
||||||
|
|
||||||
|
wait_old_rows_restored
|
||||||
|
|
||||||
|
exit 0
|
||||||
102
regress/floating-pane-offscreen-alt.sh
Executable file
102
regress/floating-pane-offscreen-alt.sh
Executable file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Returning from the alternate screen must redraw the visible part of a
|
||||||
|
# floating pane clipped at the left edge, the top edge, or both.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Loffscreen-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Loffscreen-outer-$$ -f/dev/null"
|
||||||
|
EMITTER=$DIR/emitter.pl
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&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_outer_has()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "outer client did not show $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$EMITTER" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
for my $row (1 .. 5) {
|
||||||
|
print "\e[$row;1H", 'A' x 15;
|
||||||
|
}
|
||||||
|
while (!-e "$ENV{TRIGGER}-alternate") {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
print "\e[?1049h";
|
||||||
|
for my $row (1 .. 5) {
|
||||||
|
print "\e[$row;1H", 'B' x 15;
|
||||||
|
}
|
||||||
|
while (!-e "$ENV{TRIGGER}-restore") {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
print "\e[?1049l";
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 40 -y 10 'sleep 100' || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Loffscreen-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
for position in left top both; do
|
||||||
|
case "$position" in
|
||||||
|
left) x=-5; y=2 ;;
|
||||||
|
top) x=5; y=-2 ;;
|
||||||
|
both) x=-5; y=-2 ;;
|
||||||
|
esac
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X "$x" -Y "$y" \
|
||||||
|
"TRIGGER='$DIR/$position' perl '$EMITTER'") || exit 1
|
||||||
|
[ "$($INNER display-message -p -t "$FLOAT" '#{pane_left},#{pane_top}')" = "$((x + 1)),$((y + 1))" ] ||
|
||||||
|
fail "$position: floating pane has unexpected position"
|
||||||
|
wait_outer_has AAAAA
|
||||||
|
cp "$CAPTURE" "$DIR/primary"
|
||||||
|
: >"$DIR/$position-alternate"
|
||||||
|
wait_outer_has BBBBB
|
||||||
|
: >"$DIR/$position-restore"
|
||||||
|
wait_outer_has AAAAA
|
||||||
|
cmp -s "$DIR/primary" "$CAPTURE" ||
|
||||||
|
fail "$position: primary screen was not completely restored"
|
||||||
|
$INNER kill-pane -t "$FLOAT" || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
179
regress/floating-pane-prompt-wide-character.sh
Executable file
179
regress/floating-pane-prompt-wide-character.sh
Executable file
@@ -0,0 +1,179 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A damage rectangle's clip range is grown to avoid splitting a wide
|
||||||
|
# character, but redraw_damage_grow_span_clip() (screen-redraw.c) only ever
|
||||||
|
# checks the span's own pane *content* grid (wp->screen) for that. For a
|
||||||
|
# REDRAW_SPAN_PANE span, that same range is then also handed to
|
||||||
|
# redraw_damage_draw_pane_prompt() to recompose the pane's separately
|
||||||
|
# rendered prompt (wp->prompt, e.g. from "command-prompt -P") over the
|
||||||
|
# damaged sub-range - but the prompt is drawn into its own, freshly
|
||||||
|
# allocated one-line screen, unrelated to the pane's content grid, so a
|
||||||
|
# range grown (or left ungrown) against the content is not necessarily
|
||||||
|
# grown correctly for the prompt's own wide characters.
|
||||||
|
#
|
||||||
|
# This is invisible when the pane's own content is plain ASCII (as here):
|
||||||
|
# redraw_damage_grow_span_clip() never finds anything to grow against, so
|
||||||
|
# the raw, ungrown geometric range is passed straight through to the
|
||||||
|
# prompt - and if that range's edge lands mid-character in the *prompt's*
|
||||||
|
# grid, tty_draw_line() clears the character it cuts through
|
||||||
|
# (tty_draw_line_get_empty()'s gc->data.width > nx check, for a trailing
|
||||||
|
# base cell with no room left for its padding half).
|
||||||
|
#
|
||||||
|
# The trigger is a palette change (OSC 4) in a tiled pane that is one half
|
||||||
|
# of a vertical split running the full height of the window - occluded
|
||||||
|
# under the floating pane, but still geometrically triggering a redraw of
|
||||||
|
# its own rectangle. Positioned so the split boundary falls inside the
|
||||||
|
# floating pane's own CJK prompt, this reproduces exactly Codex's report:
|
||||||
|
# "a floating pane containing a CJK prompt across a tiled-pane boundary -
|
||||||
|
# a palette update in the tiled pane blanks a prompt character."
|
||||||
|
|
||||||
|
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
|
||||||
|
cd "$DIR" || exit 1
|
||||||
|
INNER="$TEST_TMUX -Lpromptwide-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lpromptwide-outer-$$ -f/dev/null"
|
||||||
|
EMITTER=$DIR/emitter.pl
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
FLOAT=
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
$OUTER kill-server 2>/dev/null
|
||||||
|
$INNER kill-server 2>/dev/null
|
||||||
|
cd /
|
||||||
|
rm -rf "$DIR"
|
||||||
|
}
|
||||||
|
trap cleanup 0 1 15
|
||||||
|
|
||||||
|
wait_for_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
[ -n "$CLIENT" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_outer_has()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "outer client did not show $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_prompt_row_intact()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
line=$(sed -n "${PROMPTROW}p" "$CAPTURE")
|
||||||
|
case $line in
|
||||||
|
*"$PROMPTTEXT"*) return 0 ;;
|
||||||
|
esac
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "the CJK prompt was not intact after the palette-triggered damage - got: $line"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$EMITTER" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
my $line = <STDIN>;
|
||||||
|
print "\e]4;1;rgb:11/22/33\e\\";
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 60 -y 12 "perl '$EMITTER'" || exit 1
|
||||||
|
$INNER set-option -g status off || exit 1
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
LEFT=$($INNER list-panes -t inner -F '#{pane_id}') || exit 1
|
||||||
|
|
||||||
|
# Split so the boundary between the two tiled panes falls at column 17 -
|
||||||
|
# used below to pick a floating-pane column that lands the boundary
|
||||||
|
# mid-character inside the prompt.
|
||||||
|
RIGHT=$($INNER split-window -t inner -h -l 43 -PF '#{pane_id}' \
|
||||||
|
'sleep 100') || exit 1
|
||||||
|
RX=$($INNER display-message -p -t "$RIGHT" '#{pane_left}') || exit 1
|
||||||
|
|
||||||
|
# Create the floating pane, then check its actual resulting position (the
|
||||||
|
# border-framing offset added to -X is not something to hand-compute). Try
|
||||||
|
# adjacent starting columns until the split boundary lands on an odd
|
||||||
|
# (padding-half) column of the prompt's own numbering, and within the
|
||||||
|
# prompt's 12-column width.
|
||||||
|
startx=10
|
||||||
|
tries=0
|
||||||
|
while [ "$tries" -lt 4 ]; do
|
||||||
|
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 24 -y 5 -X "$startx" \
|
||||||
|
-Y 2 "sh -c 'i=0; while [ \$i -lt 10 ]; do \
|
||||||
|
printf AAAAAAAAAAAAAAAAAAAAAA\\\\n; i=\$((i+1)); done; sleep 100'") ||
|
||||||
|
exit 1
|
||||||
|
sleep 0.2
|
||||||
|
X1=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||||
|
Y1=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
H1=$($INNER display-message -p -t "$FLOAT" '#{pane_height}')
|
||||||
|
local=$((RX - X1 - 1))
|
||||||
|
if [ "$local" -ge 1 ] && [ "$local" -le 11 ] &&
|
||||||
|
[ $((local % 2)) -eq 1 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
startx=$((startx + 1))
|
||||||
|
tries=$((tries + 1))
|
||||||
|
done
|
||||||
|
local=$((RX - X1 - 1))
|
||||||
|
[ "$local" -ge 1 ] && [ "$local" -le 11 ] && [ $((local % 2)) -eq 1 ] ||
|
||||||
|
fail "could not find bad-parity starting column"
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 60 -y 12 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lpromptwide-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
wait_for_client
|
||||||
|
wait_outer_has AAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}') || exit 1
|
||||||
|
|
||||||
|
$INNER select-pane -t "$FLOAT" || exit 1
|
||||||
|
PROMPTTEXT=$(printf '\344\270\255' | perl -CSD -ne 'print $_ x 6')
|
||||||
|
$INNER command-prompt -b -P -t "$CLIENT" -p "$PROMPTTEXT" \
|
||||||
|
'display-message -- %1' || exit 1
|
||||||
|
wait_outer_has "$PROMPTTEXT"
|
||||||
|
|
||||||
|
PROMPTROW=$((Y1 + H1))
|
||||||
|
|
||||||
|
# Trigger the damage: unblock the emitter so it fires the palette change in
|
||||||
|
# the left tiled pane, which is occluded under (but geometrically overlaps)
|
||||||
|
# the floating pane's prompt row.
|
||||||
|
$INNER send-keys -t "$LEFT" Enter || exit 1
|
||||||
|
|
||||||
|
wait_prompt_row_intact
|
||||||
|
|
||||||
|
exit 0
|
||||||
151
regress/floating-pane-status-cross-client.sh
Executable file
151
regress/floating-pane-status-cross-client.sh
Executable file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Damage redraws must show each client's own pane status, even though the
|
||||||
|
# cached status screen is shared between clients.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lstatuscc-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lstatuscc-outer-$$ -f/dev/null"
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&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_clients()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
count=$($INNER list-clients 2>/dev/null | wc -l)
|
||||||
|
[ "$count" -eq 2 ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "two inner clients did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_marker()
|
||||||
|
{
|
||||||
|
target=$1
|
||||||
|
marker=$2
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t "$target" >"$CAPTURE" || exit 1
|
||||||
|
grep -q "$marker" "$CAPTURE" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "client in $target did not receive $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
cat >"$DIR/emitter.pl" <<'PERL'
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
for my $phase (1 .. 4) {
|
||||||
|
while (!-e "$ENV{TRIGGER}-$phase") {
|
||||||
|
select undef, undef, undef, 0.01;
|
||||||
|
}
|
||||||
|
# Change an unused palette entry and acknowledge it in the pane body.
|
||||||
|
print "\e]4;200;rgb:11/22/0$phase\a\e[1;1HDAMAGE$phase";
|
||||||
|
}
|
||||||
|
sleep 100;
|
||||||
|
PERL
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 60 -y 20 \
|
||||||
|
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||||
|
$INNER set -g status off || exit 1
|
||||||
|
$INNER set -g window-size manual || exit 1
|
||||||
|
$INNER set -g automatic-rename off || exit 1
|
||||||
|
$INNER set -g status-interval 0 || exit 1
|
||||||
|
$INNER set -g pane-border-lines simple || exit 1
|
||||||
|
$INNER set -g pane-border-status top || exit 1
|
||||||
|
$INNER set -g pane-border-format 'CLIENT=<#{client_name}>' || exit 1
|
||||||
|
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 35 -y 6 -X 5 -Y 5 \
|
||||||
|
'sleep 100') || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 121 -y 20 'sleep 100' || exit 1
|
||||||
|
$OUTER set -g status off || exit 1
|
||||||
|
$OUTER set -g window-size manual || exit 1
|
||||||
|
$OUTER set -g default-terminal screen || exit 1
|
||||||
|
LEFT=$($OUTER display-message -p -t outer:0.0 '#{pane_id}') || exit 1
|
||||||
|
RIGHT=$($OUTER split-window -h -PF '#{pane_id}' 'sleep 100') || exit 1
|
||||||
|
for target in "$LEFT" "$RIGHT"; do
|
||||||
|
$OUTER respawn-pane -k -t "$target" "$INNER attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
done
|
||||||
|
wait_for_clients
|
||||||
|
|
||||||
|
NAME1=$($OUTER display-message -p -t "$LEFT" '#{pane_tty}') || exit 1
|
||||||
|
NAME2=$($OUTER display-message -p -t "$RIGHT" '#{pane_tty}') || exit 1
|
||||||
|
|
||||||
|
# Disable periodic status updates above and trigger damage without a command
|
||||||
|
# that also requests a status redraw. Each client must keep its own title.
|
||||||
|
$INNER refresh-client -t "$NAME1" || exit 1
|
||||||
|
$INNER refresh-client -t "$NAME2" || exit 1
|
||||||
|
sleep 0.5
|
||||||
|
for phase in 0 1 2; do
|
||||||
|
if [ "$phase" -ne 0 ]; then
|
||||||
|
: >"$DIR/trigger-$phase"
|
||||||
|
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||||
|
wait_for_marker "$RIGHT" "DAMAGE$phase"
|
||||||
|
sleep 0.2
|
||||||
|
fi
|
||||||
|
for target in "$LEFT" "$RIGHT"; do
|
||||||
|
if [ "$target" = "$LEFT" ]; then
|
||||||
|
name=$NAME1
|
||||||
|
other=$NAME2
|
||||||
|
else
|
||||||
|
name=$NAME2
|
||||||
|
other=$NAME1
|
||||||
|
fi
|
||||||
|
$OUTER capture-pane -p -S 5 -E 10 -t "$target" \
|
||||||
|
>"$CAPTURE" || exit 1
|
||||||
|
grep -Fq "CLIENT=<$name>" "$CAPTURE" ||
|
||||||
|
fail "phase $phase: missing $name's pane status"
|
||||||
|
if grep -Fq "CLIENT=<$other>" "$CAPTURE"; then
|
||||||
|
fail "phase $phase: $name received $other's pane status"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Leave just one client so a cache keyed only by client would remain stale.
|
||||||
|
$OUTER respawn-pane -k -t "$RIGHT" 'sleep 100' || exit 1
|
||||||
|
i=0
|
||||||
|
while [ "$($INNER list-clients | wc -l)" -ne 1 ]; do
|
||||||
|
[ "$i" -lt 50 ] || fail "second client did not detach"
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
$INNER set-environment -g TEST_STATUS_VALUE initial || exit 1
|
||||||
|
$INNER set -g pane-border-format 'VALUE=#{TEST_STATUS_VALUE}' || exit 1
|
||||||
|
$INNER refresh-client -t "$NAME1" || exit 1
|
||||||
|
wait_for_marker "$LEFT" VALUE=initial
|
||||||
|
for phase in 3 4; do
|
||||||
|
# Changing the environment does not itself request a status redraw.
|
||||||
|
$INNER set-environment -g TEST_STATUS_VALUE "phase$phase" || exit 1
|
||||||
|
: >"$DIR/trigger-$phase"
|
||||||
|
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||||
|
wait_for_marker "$LEFT" "VALUE=phase$phase"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
102
regress/floating-pane-status-format.sh
Executable file
102
regress/floating-pane-status-format.sh
Executable file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Resizing a floating pane must refresh session status formats which depend on
|
||||||
|
# its geometry, not only the pane scene and borders.
|
||||||
|
|
||||||
|
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
|
||||||
|
INNER="$TEST_TMUX -Lfloating-status-inner-$$ -f/dev/null"
|
||||||
|
OUTER="$TEST_TMUX -Lfloating-status-outer-$$ -f/dev/null"
|
||||||
|
CAPTURE=$DIR/capture
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$*" >&2
|
||||||
|
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&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_client()
|
||||||
|
{
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||||
|
[ -n "$CLIENT" ] && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "inner client did not attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_outer_has_status()
|
||||||
|
{
|
||||||
|
marker=$1
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 50 ]; do
|
||||||
|
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||||
|
tail -1 "$CAPTURE" | grep -q "$marker" && return 0
|
||||||
|
sleep 0.1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fail "outer client status did not show $marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse()
|
||||||
|
{
|
||||||
|
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||||
|
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||||
|
sleep 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
$INNER new-session -d -s inner -x 50 -y 12 'sleep 100' || exit 1
|
||||||
|
FLOAT=$($INNER new-pane -PF '#{pane_id}' -x 10 -y 5 -X 5 -Y 3 \
|
||||||
|
'sleep 100') || fail "could not create floating pane"
|
||||||
|
$INNER set-option -g window-size manual || exit 1
|
||||||
|
$INNER set-option -g mouse on || exit 1
|
||||||
|
$INNER set-option -g status on || exit 1
|
||||||
|
$INNER set-option -g status-position bottom || exit 1
|
||||||
|
$INNER set-option -g status-left 'WIDTH=#{pane_width}' || exit 1
|
||||||
|
$INNER set-option -g status-right '' || exit 1
|
||||||
|
$INNER set-option -g status-interval 0 || exit 1
|
||||||
|
|
||||||
|
$OUTER new-session -d -s outer -x 50 -y 12 'sleep 100' || exit 1
|
||||||
|
$OUTER set-option -g status off || exit 1
|
||||||
|
$OUTER set-option -g window-size manual || exit 1
|
||||||
|
$OUTER respawn-pane -k -t outer:0.0 \
|
||||||
|
"$TEST_TMUX -Lfloating-status-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
wait_for_client
|
||||||
|
OLD_WIDTH=$($INNER display-message -p -t "$FLOAT" '#{pane_width}')
|
||||||
|
wait_outer_has_status "WIDTH=$OLD_WIDTH"
|
||||||
|
|
||||||
|
RIGHT=$($INNER display-message -p -t "$FLOAT" '#{pane_right}')
|
||||||
|
TOP=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||||
|
X=$((RIGHT + 2))
|
||||||
|
Y=$((TOP + 2))
|
||||||
|
|
||||||
|
# Grab the right frame and enlarge the floating pane.
|
||||||
|
mouse 0 "$X" "$Y" M
|
||||||
|
mouse 32 "$((X + 1))" "$Y" M
|
||||||
|
mouse 32 "$((X + 8))" "$Y" M
|
||||||
|
mouse 0 "$((X + 8))" "$Y" m
|
||||||
|
|
||||||
|
NEW_WIDTH=$($INNER display-message -p -t "$FLOAT" '#{pane_width}')
|
||||||
|
[ "$NEW_WIDTH" -ne "$OLD_WIDTH" ] || fail "floating pane was not resized"
|
||||||
|
wait_outer_has_status "WIDTH=$NEW_WIDTH"
|
||||||
|
|
||||||
|
exit 0
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user